python pandas 将数据帧乘以向量化方式随类别变化的权重

python pandas multiply dataframe by weights that vary with category in vectorized fashion

我的问题与outlined here

非常相似

除了我的主数据框有一个类别列,我的权重也是如此:

df
Out[3]: 
Symbol         var_1      var_2     var_3     var_4    Category  
Index                                              
1903          0.000443  0.006928  0.000000  0.012375      A
1904         -0.000690 -0.007873  0.000171  0.014824      A
1905         -0.001354  0.001545  0.000007 -0.008195      C
1906         -0.001578  0.008796 -0.000164  0.015955      D
1907         -0.001578  0.008796 -0.000164  0.015955      A
1909         -0.001354  0.001545  0.000007 -0.008195      B


wgt_df
Out[4]: 
  Category   var_1_wgt var_2_wgt var_3_wgt var_4_wgt
0    A       0.182022   0.182022  0.131243  0.182022
1    B       0.534814   0.534814  0.534814  0.534814
2    C       0.131243   0.534814  0.131243  0.182022  
3    D       0.182022   0.151921  0.151921  0.131243

我想做的是为每个类别应用正确的权重以创建一个新列 df['new_var'],这是加权和。在没有类别的情况下,我可以将权重转换为 numpy 数组并使用看起来非常快的 .dot() 方法。但是,我看不出如何解决我的问题:如果我在主数据框 df 上使用 groupby(),我肯定必须以某种方式对我的权重数据框 wgt_df 执行相同的操作。

实际上df有几百万行,我需要多次重复这个计算,所以我渴望找到一个向量化的解决方案;否则我可以做 df.groupby('Category'),创建一个数据框的字典,其键是类别,例如wgts_dict['A'] = wgts_df[wgts_df.Category == 'A'],并通过 lambda x 应用我的点逻辑,尽管我也不确定如何执行此操作,因为我需要明确引用当前正在处理的组元素以便拉出wgts_df.

的正确切片

设置

print(df)
Out[655]: 
           var_1     var_2     var_3     var_4 Category
Symbol                                                 
1903    0.000443  0.006928  0.000000  0.012375        A
1904   -0.000690 -0.007873  0.000171  0.014824        A
1905   -0.001354  0.001545  0.000007 -0.008195        C
1906   -0.001578  0.008796 -0.000164  0.015955        D
1907   -0.001578  0.008796 -0.000164  0.015955        A
1909   -0.001354  0.001545  0.000007 -0.008195        B

print(w)
Out[656]: 
  Category  var_1_wgt  var_2_wgt  var_3_wgt  var_4_wgt
0        A   0.182022   0.182022   0.131243   0.182022
1        B   0.534814   0.534814   0.534814   0.534814
2        C   0.131243   0.534814   0.131243   0.182022
3        D   0.182022   0.151921   0.151921   0.131243

解决方案

#convert Category to numerical encoding
df['C_Number'] = df.Category.apply(lambda x: ord(x.lower())-97)

#Get a dot product for each row with all category weights and the extract the weights by the category number

df['new_var'] = ((df.iloc[:,:4].values).dot(w.iloc[:,-4:].values))[np.arange(len(df)),df.C_Number]

Out[654]: 
           var_1     var_2     var_3     var_4 Category  C_Number   new_var
Symbol                                                                     
1903    0.000443  0.006928  0.000000  0.012375        A         0  0.006038
1904   -0.000690 -0.007873  0.000171  0.014824        A         0 -0.001615
1905   -0.001354  0.001545  0.000007 -0.008195        C         2 -0.000595
1906   -0.001578  0.008796 -0.000164  0.015955        D         3  0.006481
1907   -0.001578  0.008796 -0.000164  0.015955        A         0  0.007300
1909   -0.001354  0.001545  0.000007 -0.008195        B         1 -0.000661

您可以按类别进行分组(select,然后按类别进行 dot(),或者您可以按类别进行 dot(),然后 select。后者在 pandas 中更快更简单。请注意,我使用的数据与数据中的列名称和权重框架匹配。

代码 dot() 然后 select:

df['dot'] = df[df_wgt.columns].dot(df_wgt.T).lookup(df.index, df.Category)

执行的步骤...

  1. Select 与 df[df_wgt.columns]

    一起使用的列

    这使用列标签和权重数据框中的排序。这很重要,因为 dot() 需要数据的顺序正确。

  2. 使用 .dot(df_wgt.T)

    对转置权重数据帧执行点积

    调换重量使它们处于 .dot() 的正确方向。这会计算每行数据的所有权重类别。这意味着在这种情况下,我们执行的乘法是所需次数的四倍,但它仍然可能比分组更快。

  3. Select 与 .lookup(df.index, df.Category)

    所需的点积

    通过使用 lookup() 我们可以为该行的类别收集正确的结果。

代码 select (groupby) 然后 dot():

def dot(group):
    category = group['Category'].iloc[0]
    weights = df_wgt.loc[category].values
    return pd.Series(
        np.dot(group[df_wgt.columns].values, weights), index=group.index)

df['dot'] = df.groupby(['Category']).apply(dot) \
    .reset_index().set_index('Index')[0]

测试代码:

import pandas as pd
from io import StringIO

df = pd.read_fwf(StringIO(u"""
    Index          var_1      var_2     var_3     var_4    Category
    1903          0.000443  0.006928  0.000000  0.012375      A
    1904         -0.000690 -0.007873  0.000171  0.014824      A
    1905         -0.001354  0.001545  0.000007 -0.008195      C
    1906         -0.001578  0.008796 -0.000164  0.015955      D
    1907         -0.001578  0.008796 -0.000164  0.015955      A
    1909         -0.001354  0.001545  0.000007 -0.008195      B"""),
                 header=1, skiprows=0).set_index(['Index'])

df_wgt = pd.read_fwf(StringIO(u"""
     Category     var_1      var_2     var_3     var_4
        A       0.182022   0.182022  0.131243  0.182022
        B       0.534814   0.534814  0.534814  0.534814
        C       0.131243   0.534814  0.131243  0.182022
        D       0.182022   0.151921  0.151921  0.131243"""),
                 header=1, skiprows=0).set_index(['Category'])

df['dot'] = df[df_wgt.columns].dot(df_wgt.T).lookup(df.index, df.Category)
print(df)

结果:

          var_1     var_2     var_3     var_4 Category       dot
Index                                                           
1903   0.000443  0.006928  0.000000  0.012375        A  0.003594
1904  -0.000690 -0.007873  0.000171  0.014824        A  0.001162
1905  -0.001354  0.001545  0.000007 -0.008195        C -0.000842
1906  -0.001578  0.008796 -0.000164  0.015955        D  0.003118
1907  -0.001578  0.008796 -0.000164  0.015955        A  0.004196
1909  -0.001354  0.001545  0.000007 -0.008195        B -0.004277