#python #pandas #dataframe #numpy
Вопрос:
У меня есть фрейм данных, в котором один столбец представляет собой numpy
массив чисел. Например,
import numpy as np
import pandas as pd
df = pd.DataFrame.from_dict({
'id': [1, 1, 2, 2, 3, 3, 3, 4, 4],
'data': [np.array([0.43, 0.32, 0.19]),
np.array([0.41, 0.11, 0.21]),
np.array([0.94, 0.35, 0.14]),
np.array([0.78, 0.92, 0.45]),
np.array([0.32, 0.63, 0.48]),
np.array([0.17, 0.12, 0.15]),
np.array([0.54, 0.12, 0.16]),
np.array([0.48, 0.16, 0.19]),
np.array([0.14, 0.47, 0.01])]
})
Я хочу получить groupby
столбец идентификатора и агрегировать его, взяв среднее значение по элементам массива. Сначала разбить массив на части невозможно, так как он имеет длину 300, а у меня более 200 000 строк. Когда я это делаю df.groupby('id').mean()
, я получаю сообщение об ошибке «Нет числовых типов для агрегирования». Я могу получить среднее значение по элементам списков , используя df['data'].mean()
, поэтому я думаю, что должен быть способ сделать сгруппированное среднее значение. Чтобы уточнить, я хочу, чтобы вывод был массивом для каждого значения идентификатора. Каждый элемент в результирующем массиве должен быть средним значением значений элементов в соответствующей позиции внутри каждой группы. В приведенном примере результат должен быть:
pd.DataFrame.from_dict({
'id': [1, 2,3,4],
'data': [np.array([0.42, 0.215, 0.2]),
np.array([0.86, 0.635, 0.29500000000000004]),
np.array([0.3433333333333333, 0.29, 0.26333333333333336]),
np.array([0.31, 0.315, 0.1])]
})
Может кто-нибудь подсказать, как я мог бы это сделать? Спасибо!
Комментарии:
1. Как должен выглядеть результат?
Ответ №1:
Подразумевайте это дважды, один раз на уровне массива и один раз на уровне группы:
df['data'].map(np.mean).groupby(df['id']).mean().reset_index()
id data
0 1 0.278333
1 2 0.596667
2 3 0.298889
3 4 0.241667
Основываясь на комментариях, вы можете сделать:
pd.DataFrame(df['data'].tolist(),index=df['id']).mean(level=0).agg(np.array,1)
id
1 [0.42, 0.215, 0.2]
2 [0.86, 0.635, 0.29500000000000004]
3 [0.3433333333333333, 0.29, 0.26333333333333336]
4 [0.31, 0.315, 0.1]
dtype: object
Или:
df.groupby("id")['data'].apply(np.mean)
Комментарии:
1. Я должен был быть более ясным, я хочу, чтобы вывод был массивом (в примере он был бы длиной четыре), где каждый элемент является средним значением элементов в этой позиции.
2. @AndrejKesely Да, это так, но
np.mean
в моей версии, только что протестированной и отредактированной 🙂 только значит, что дает мнеDataError: No numeric types to aggregate
3.
Using the level keyword in DataFrame and Series aggregations is deprecated
Должно бытьpd.DataFrame(df['data'].tolist(),index=df['id']).groupby(level=0).mean().agg(np.array,1)
для будущих версий.
Ответ №2:
Во-первых, разделение массива возможно, поскольку ваше текущее хранилище требует хранения сложного объекта со всеми значениями в пределах фрейма данных. Это займет намного больше места, чем просто хранение плоского 2D-массива
# Your current memory usage
df.memory_usage(deep=True).sum()
1352
# Create a new DataFrame (really just overwrite `df` but keep separate for illustration)
df1 = pd.concat([df['id'], pd.DataFrame(df['data'].tolist())], 1)
# id 0 1 2
#0 1 0.43 0.32 0.19
#1 1 0.41 0.11 0.21
#2 2 0.94 0.35 0.14
#3 2 0.78 0.92 0.45
#4 3 0.32 0.63 0.48
#5 3 0.17 0.12 0.15
#6 3 0.54 0.12 0.16
#7 4 0.48 0.16 0.19
#8 4 0.14 0.47 0.01
Да, это выглядит больше, но это не с точки зрения памяти, на самом деле это меньше. Коэффициент 3x здесь немного экстремален, для больших кадров данных с длинными массивами он, вероятно, составит 95% памяти. И все же это должно быть меньше.
df1.memory_usage(deep=True).sum()
#416
И теперь ваша агрегация является обычной groupby
mean
, столбцы указывают местоположение в массиве
df1.groupby('id').mean()
# 0 1 2
#id
#1 0.420000 0.215 0.200000
#2 0.860000 0.635 0.295000
#3 0.343333 0.290 0.263333
#4 0.310000 0.315 0.100000
Комментарии:
1. Потрясающе. Я предполагаю здесь, но я думаю, что сохранение идентификатора в качестве индекса вместо конката может сэкономить немного места. Затем мы можем обратиться к уровню при группировании
2. @anky, не уверен насчет памяти и индекса, я никогда в это не заглядывал. К сожалению
mean(level=)
, это устарело, потому что кажется, что они идут по пути войны, чтобы упростить api pandas и удалить все избыточные способы выполнения одной и той же операции (r.i.p. lookup: (), так что вам все равно понадобится.groupby('id').mean()
(хорошая мысльid
может быть, по крайней мере, в индексе)
Ответ №3:
Сгруппируйте по среднему значению для массива, где выводом является массив среднего значения
df['data'].map(np.array).groupby(df['id']).mean().reset_index()
Выход:
id data
0 1 [0.42, 0.215, 0.2]
1 2 [0.86, 0.635, 0.29500000000000004]
2 3 [0.3433333333333333, 0.29, 0.26333333333333336]
3 4 [0.31, 0.315, 0.1]
Комментарии:
1. Какова ваша версия для панд, которую я получаю
DataError: No numeric types to aggregate
, когда запускаю это в'1.1.3'
2. панды==1.3.2 numpy==1.21.2
3. Это интересно, так как похоже на мой первый ответ, но дает другой результат. Не могу сказать, что панды меняются с каждой версией :/
Ответ №4:
Ты всегда можешь .apply
иметь в виду глупость.
df.groupby('id')['data'].apply(np.mean).apply(np.mean)
# returns:
id
1 0.278333
2 0.596667
3 0.298889
4 0.241667
Name: data, dtype: float64