#python #pandas #numpy
#python #pandas #numpy
Вопрос:
У меня есть два фрейма данных. Они выглядят так:
df_a
Framecount probability
0 0.0 [0.00019486549333333332, 4.883635666666667e-06...
1 1.0 [0.00104359155, 3.9232405e-05, 0.0015722045000...
2 2.0 [0.00048501002666666667, 1.668179e-05, 0.00052...
3 3.0 [4.994969500000001e-05, 4.0931635e-07, 0.00011...
4 4.0 [0.0004808829, 5.389742e-05, 0.002522127933333...
.. ... ...
906 906.0 [1.677140566666667e-05, 1.1745095666666665e-06...
907 907.0 [1.5164155000000002e-05, 7.66629575e-07, 0.000...
908 908.0 [8.1334184e-05, 0.00012675669636333335, 0.0028...
909 909.0 [0.00014893802999999998, 1.0407592500000001e-0...
910 910.0 [4.178489e-05, 2.17477925e-06, 0.02094931, 0.0...
И:
df_b
start stop
0 12.12 12.47
1 13.44 20.82
2 20.88 29.63
3 31.61 33.33
4 33.44 42.21
.. ... ...
228 880.44 887.92
229 888.63 892.07
230 892.13 895.30
231 895.31 900.99
232 907.58 908.35
Я хочу объединить df_a.probability
df_b
, когда df_a.Framecount
находится между df_b.start и df_b.stop
. Статистика агрегации для df_a.probability
должна быть mean
, но я сталкиваюсь с ошибками, потому df_a.probability
что это dtype np array .
Я пытаюсь использовать этот код:
idx = pd.IntervalIndex.from_arrays(df_text['start'], df_text['stop'])
df_text.join(df_vid.groupby(idx.get_indexer_non_unique(df_vid['Framecount']))['probability'].apply(np.mean), how='left')
Строка 1 создает индекс, определяющий группировку. В строке 2 я пытаюсь реализовать group by и агрегировать все значения, df_a.probability
которые попадают в индекс groupby по среднему значению. Мне нужен один массив на groupby, который является средним значением всех массивов в индексе groupby. Этот код выдает мне эту ошибку:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-271-19c7d58fb664> in <module>
1 idx = pd.IntervalIndex.from_arrays(df_text['start'], df_text['stop'])
2 f = lambda x: np.mean(np.array(x.tolist()), axis=0)
----> 3 df_text.join(df_vid.groupby(idx.get_indexer_non_unique(df_vid['Framecount']))['probability'].apply(np.mean), how='left')
~/anaconda3/lib/python3.7/site-packages/pandas/core/frame.py in groupby(self, by, axis, level, as_index, sort, group_keys, squeeze, observed)
5808 group_keys=group_keys,
5809 squeeze=squeeze,
-> 5810 observed=observed,
5811 )
5812
~/anaconda3/lib/python3.7/site-packages/pandas/core/groupby/groupby.py in __init__(self, obj, keys, axis, level, grouper, exclusions, selection, as_index, sort, group_keys, squeeze, observed, mutated)
407 sort=sort,
408 observed=observed,
--> 409 mutated=self.mutated,
410 )
411
~/anaconda3/lib/python3.7/site-packages/pandas/core/groupby/grouper.py in get_grouper(obj, key, axis, level, sort, observed, mutated, validate)
588
589 elif is_in_axis(gpr): # df.groupby('name')
--> 590 if gpr in obj:
591 if validate:
592 obj._check_label_or_level_ambiguity(gpr, axis=axis)
~/anaconda3/lib/python3.7/site-packages/pandas/core/generic.py in __contains__(self, key)
1848 def __contains__(self, key) -> bool_t:
1849 """True if the key is in the info axis"""
-> 1850 return key in self._info_axis
1851
1852 @property
~/anaconda3/lib/python3.7/site-packages/pandas/core/indexes/base.py in __contains__(self, key)
3898 @Appender(_index_shared_docs["contains"] % _index_doc_kwargs)
3899 def __contains__(self, key) -> bool:
-> 3900 hash(key)
3901 try:
3902 return key in self._engine
TypeError: unhashable type: 'numpy.ndarray'
Я пробовал несколько спецификаций агрегации, в том числе:
df_text.join(df_vid.groupby(idx.get_indexer_non_unique(df_vid['Framecount']))['probability'].apply(lambda x: np.mean(np.array(x.tolist()), axis=0)), how='left')
или
df_text.join(df_vid.groupby(idx.get_indexer_non_unique(df_vid['Framecount']))['probability'].apply((np.mean), how='left')
или
df_text.join(df_vid.groupby(idx.get_indexer_non_unique(df_vid['Framecount']))['probability'].mean()), how='left')
и я получаю ту же ошибку.
Как мне это сделать?
Ответ №1:
- Ошибка возникает из
idx.get_indexer_non_unique(df_vid['Framecount'])
tuple
-за того, что таким образом создается, и вы не можетеgroupby
создать кортеж.df_vid.groupby(idx.get_indexer_non_unique(df_vid['Framecount'])[0])
выбор первого массива вtuple
будет работать.
idx.get_indexer(df_a.fc)
в результате будет получен массив с индексом интервала, которомуfc
принадлежит. Если соответствующий интервал отсутствует, индекс будет отображаться как-1
.df_a.groupby(idx.get_indexer(df_a.fc))
группы по массиву индексов..agg({'prob': list})
объединяет все списки для каждогоfc
в список.- Результатом для каждой группы является список списков
.prob.map(np.mean)
возвращает общее среднее значение для всех списков в группе.prob.apply(lambda x: [np.mean(v) for v in x])
возвращает список средних значений для каждого списка.- Ни
'fc'
одно значение не попадает в корзину для12.12 - 12.47
.
import pandas as pd
import numpy as np
# setup df with start and stop ranges
data = {'start': [12.12, 13.44, 20.88, 31.61, 33.44, 880.44, 888.63, 892.13, 895.31, 907.58], 'stop': [12.47, 20.82, 29.63, 33.33, 42.21, 887.92, 892.07, 895.3, 900.99, 908.35]}
df = pd.DataFrame(data)
# setup sample df_a with Framecount as fc, and probability as prob
np.random.seed(365)
df_a = pd.DataFrame({'fc': range(911), 'prob': np.random.randint(1, 100, (911, 14)).tolist()})
# this will convert the column to np.arrays instead of lists; the remainder of the code works regardless
# df_a.prob = df_a.prob.map(np.array)
# create an IntervalIndex from df start and stop
idx = pd.IntervalIndex.from_arrays(df.start, df.stop, closed='both')
Это создаст список средних значений по оси = 0
dfg = df_a.groupby(idx.get_indexer(df_a.fc)).agg({'prob': list}).prob.apply(lambda x: np.mean(x, axis=0))
# join df with dfg
dfj = df.join(dfg)
# display(dfj) for list of means
start stop prob
0 12.12 12.47 NaN
1 13.44 20.82 [49.3, 57.1, 51.4, 45.9, 47.1, 45.9, 45.9, 55.3, 32.6, 48.0, 42.0, 45.0, 50.4, 54.4]
2 20.88 29.63 [42.7, 42.6, 46.0, 45.9, 54.1, 55.9, 50.1, 55.2, 51.7, 54.0, 37.6, 60.9, 49.2, 45.6]
3 31.61 33.33 [87.5, 49.0, 46.5, 54.5, 75.0, 47.0, 24.0, 40.5, 52.5, 21.0, 51.0, 72.5, 34.5, 50.5]
4 33.44 42.21 [48.6, 66.2, 45.8, 64.7, 43.1, 69.0, 54.4, 52.1, 52.6, 59.6, 51.1, 42.1, 43.3, 38.0]
5 880.44 887.92 [51.9, 50.6, 63.7, 47.7, 51.3, 34.9, 51.3, 53.0, 53.4, 65.1, 38.6, 49.4, 48.1, 44.1]
6 888.63 892.07 [45.2, 23.5, 67.2, 68.0, 38.2, 47.2, 50.2, 75.8, 35.2, 46.8, 55.0, 57.5, 44.2, 78.0]
7 892.13 895.30 [61.3, 44.0, 43.3, 36.3, 63.7, 89.7, 51.7, 57.0, 50.0, 68.7, 80.7, 46.3, 66.7, 11.3]
8 895.31 900.99 [68.2, 44.6, 50.8, 35.2, 53.2, 40.4, 34.8, 77.4, 61.0, 35.2, 26.0, 47.8, 30.4, 55.4]
9 907.58 908.35 [17.0, 78.0, 24.0, 33.0, 88.0, 3.0, 43.0, 2.0, 36.0, 48.0, 8.0, 87.0, 36.0, 34.0]
Это создаст одно среднее значение для каждой группы
dfg = df_a.groupby(idx.get_indexer(df_a.fc)).agg({'prob': list}).prob.map(np.mean)
# join df with dfg
dfj = df.join(dfg)
# display(dfj) for overall mean
start stop prob
0 12.12 12.47 NaN
1 13.44 20.82 47.877551
2 20.88 29.63 49.380952
3 31.61 33.33 50.428571
4 33.44 42.21 52.182540
5 880.44 887.92 50.224490
6 888.63 892.07 52.303571
7 892.13 895.30 55.047619
8 895.31 900.99 47.171429
9 907.58 908.35 38.357143
Ответ №2:
Я бы хотел, чтобы кто-нибудь предложил решение, которое не включает циклы, но поскольку все отсортировано, я думаю, что производительность на самом деле будет не такой плохой (линейной по длине двух фреймов данных и без затрат памяти).). Я не знаю точной спецификации ваших фреймов данных, поэтому сначала я создам несколько примеров.
n_a = 11
df_a = pd.DataFrame(
{"Framecount": list(range(n_a)), "probability": np.random.rand(n_a)}
)
n_b = 6
start = np.linspace(0, n_a, n_b)
end = start n_a / (n_b - 1) - 1e-5
df_b = pd.DataFrame({"start": start, "end": end, "mean": [np.nan] * n_b})
print(df_a)
Framecount probability
0 0 0.099412
1 1 0.492661
2 2 0.043000
3 3 0.382923
4 4 0.208177
5 5 0.110007
6 6 0.369756
7 7 0.324723
8 8 0.702838
9 9 0.182167
10 10 0.578837
print(df_b)
start end mean
0 0.0 2.19999 NaN
1 2.2 4.39999 NaN
2 4.4 6.59999 NaN
3 6.6 8.79999 NaN
4 8.8 10.99999 NaN
5 11.0 13.19999 NaN
Теперь я буду перебирать фреймы данных, объединяя все значения между текущим start
и end
и присваивать в соответствующей строке в df_b
:
i = j = 0
while i < n_a and j < n_b:
# seek to next row of df_b where start <= df_a[i]
while i < n_a and df_a.loc[i, "Framecount"] < df_b.loc[j, "start"]:
i = 1
accum = 0
count = 0
while i < n_a and df_a.loc[i, "Framecount"] < df_b.loc[j, "end"]:
accum = df_a.loc[i, "probability"]
count = 1
i = 1
df_b.loc[j, "mean"] = accum / count
j = 1
print(df_b)
start end mean
0 0.0 2.19999 0.211691
1 2.2 4.39999 0.29555
2 4.4 6.59999 0.239882
3 6.6 8.79999 0.513781
4 8.8 10.99999 0.380502
5 11.0 13.19999 NaN
Комментарии:
1. Спасибо за ваш ответ. Я в замешательстве. Что такое n_a и n_b?
2. @connor449 просто длины составленных фреймов данных. Для вас они оба будут 906.
3. Я получаю эту ошибку:` ————————————————————————— ZeroDivisionError Трассировка (последний последний вызов) <ipython-input-282-d02c0d8644d3> в <модуле> 17 i = 1 18 — > 19 df_b.loc[j,] = накопление / подсчет 20 j = 1 21 Ошибка нулевого разделения: деление на ноль `