#python #pandas #numpy
#python #pandas #numpy
Вопрос:
Итак, у меня есть два столбца значений и два столбца веса в фрейме данных Pandas, и я хочу сгенерировать третий столбец, который является сгруппированным по взвешенному среднему значению этих двух столбцов.
Итак, для:
df = pd.DataFrame({'category':['a','a','b','b'],
'var1':np.random.randint(0,100,4),
'var2':np.random.randint(0,100,4),
'weights1':np.random.random(4),
'weights2':np.random.random(4)})
df
category var1 var2 weights1 weights2
0 a 84 45 0.955234 0.729862
1 a 49 5 0.225470 0.159662
2 b 77 95 0.957212 0.991960
3 b 27 65 0.491877 0.195680
Я бы хотел выполнить:
df
category var1 var2 weights1 weights2 average
0 a 84 45 0.955234 0.729862 67.108023
1 a 49 5 0.225470 0.159662 30.759124
2 b 77 95 0.957212 0.991960 86.160443
3 b 27 65 0.491877 0.195680 37.814851
Я уже добился этого, используя только арифметические операторы, подобные этому:
df['average'] = df.groupby('category', group_keys=False)
.apply(lambda g: (g.weights1 * g.var1 g.weights2 * g.var2) / (g.weights1 g.weights2))
Но я хочу обобщить его на использование numpy.average, чтобы я мог, например, взять средневзвешенное значение по 3 столбцам или более.
Я пытаюсь сделать что-то подобное, но, похоже, это не работает:
df['average'] = df.groupby('category', group_keys=False)
.apply(lambda g: np.average([g.var1, g.var2], axis=0, weights=[g.weights1, g.weights2]))
возврат
TypeError: incompatible index of inserted column with frame index
Кто-нибудь может мне помочь в этом?
Комментарии:
1. Насколько точно вы хотите, чтобы этот новый столбец соответствовал исходному фрейму данных? Поскольку в group by меньше значений, чем в исходном фрейме данных, pandas не знает, как выровнять новый столбец с исходным фреймом данных. Можете ли вы опубликовать образец фрейма данных и ожидаемый результат
2. Количество значений должно быть одинаковым, поскольку оно просто принимает средневзвешенное значение по столбцам, которые уже существуют в каждой группе. Я добавлю образец фрейма данных и вывода
Ответ №1:
Я даже не думаю, что вам это нужно groupby
. Обратите внимание, это соответствует результату с apply
lambda
.
Попробуйте это:
col=df.drop('category',1)
s=col.groupby(col.columns.str.findall(r'd ').str[0],axis=1).prod().sum(1)
s/df.filter(like='weight').sum(1)
Out[33]:
0 67.108014
1 30.759168
2 86.160444
3 37.814871
dtype: float64
Комментарии:
1. 1 за использование like в этом случае. Я фильтровал по невзвешенным столбцам, но я не понимал, что это вариант.
2. Если вы просто используете последний признак столбцов weights и var для определения того, что нужно умножать, то это будет работать только для 9 столбцов var и weight
Ответ №2:
Это один из подходов:
import numpy as np
import pandas as pd
df = pd.DataFrame({'category': ['a', 'a', 'b', 'b'],
'var1': np.random.randint(0, 100, 4),
'var2': np.random.randint(0, 100, 4),
'weights1': np.random.random(4),
'weights2': np.random.random(4)})
df_averages = df[df.columns.difference(['category', 'var1', 'var2'])]
Вывод:
weights1 weights2
0 0.002812 0.483088
1 0.159774 0.818346
2 0.285366 0.586706
3 0.427240 0.428667
df_averages['Average'] = df_averages.mean(axis=1)
Вывод:
weights1 weights2 Average
0 0.002812 0.483088 0.242950
1 0.159774 0.818346 0.489060
2 0.285366 0.586706 0.436036
3 0.427240 0.428667 0.427954
df['Averages'] = df_averages['Average'].astype(float)
Вывод:
category var1 var2 weights1 weights2 Averages
0 a 60 22 0.002812 0.483088 0.242950
1 a 66 63 0.159774 0.818346 0.489060
2 b 18 10 0.285366 0.586706 0.436036
3 b 68 32 0.427240 0.428667 0.427954
По сути, удалите невзвешенные столбцы из фрейма данных и переместите взвешенные столбцы в новый. Затем вы можете применить среднее значение по строкам этого фрейма данных и объединить его обратно, поскольку индекс будет таким же.
Ответ №3:
Поскольку у вас есть одно значение в среднем столбце для каждой строки в df, вам действительно не нужно группировать. Вам просто нужен динамический способ вычисления среднего значения для переменного числа 'varXXX'
столбцов.
Приведенный ниже ответ основан на одинаковом количестве столбцов ‘var’ и столбцов ‘weights’ с согласованным шаблоном именования, поскольку он создает строку имени столбца
df = pd.DataFrame({'category': ['a', 'a', 'b', 'b'],
'var1': np.random.randint(0, 100, 4),
'var2': np.random.randint(0, 100, 4),
'var3': np.random.randint(0, 100, 4),
'weights1': np.random.random(4),
'weights2': np.random.random(4),
'weights3': np.random.random(4)
})
n_cols = len([1 for i in df.columns if i[:3] == 'var'])
def weighted_av_func(x):
numerator = 0
denominator = 0
for i in range(1, n_cols 1):
numerator = x['var{}'.format(i)] * x['weights{}'.format(i)]
denominator = x['weights{}'.format(i)]
return numerator / denominator
df['average'] = df.apply(weighted_av_func, axis=1)
print(df)
category var1 var2 var3 weights1 weights2 weights3 average
0 a 53 58 2 0.101798 0.073881 0.919632 10.517238
1 a 52 0 26 0.073988 0.816425 0.888792 15.150578
2 b 30 78 46 0.641875 0.029402 0.370237 37.042735
3 b 36 72 92 0.186941 0.663270 0.774427 77.391136
Редактировать:
Если вы хотите использовать np.average и можете гарантировать упорядочение столбцов var и столбцов weights в вашем фрейме данных, то вы могли бы сделать это:
df['np_average'] = df.apply(
lambda x: np.average(a=x[1:1 n_cols],
weights=x[n_cols 1:2 * n_cols 1]),
axis=1)