Сгруппированные по взвешенным средним значениям столбцов в Pandas

#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)