2017-03-02 76 views
1

第一:也许这适合代码审查更好,但我认为这里是更多熊猫仿射用户。随意移动它,如果你认为否则根据其他列创建新列:DRY并启用功能

这是常有的情况下,一个要计算一个新列了现有的: 考虑以下大熊猫据帧

df = pd.DataFrame({'ItemNumber':np.arange(1, 10), 
    'A':[1,1,1,2,2,2,3,3,3], 'B': [1,2,3]*3}) 
print(df) 
    A B ItemNumber 
0 1 1   1 
1 1 2   2 
2 1 3   3 
3 2 1   4 
4 2 2   5 
5 2 3   6 
6 3 1   7 
7 3 2   8 
8 3 3   9 

比方说,我们计算通过

df.loc[(df["A"] == 1) & (df["B"] > 1), 'C'] = 1 
df.loc[(df["B"] == 1) & (df["A"] > 1), 'C'] = 2 
df.loc[(df["A"] > 1) & (df["B"] > 1), 'C' ] = 3 
df.loc[(df["A"] == 1) & (df["B"] == 1), 'C' ] = 4 

新列“C”这会相比,迭代的方法,在大型dataframes执行相当快也呈现here。 特别是,该方法的性能问题使我得到了上述代码。

但是,这段代码违反了DRY原则。复制粘贴感觉很尴尬。

所以,让我们多一点的功能和定义两个咖喱功能:

def col_equals_value(col, value): 
    def filter_df(df): 
     return df[col] == value 
    return filter_df 

def col_greater_value(col, value): 
    def filter_df(df): 
     return df[col] > value 
    return filter_df 

从那里,我们定义的比较:需要

a1 = col_equals_value('A', 1) 
b1 = col_equals_value('B', 1) 
agt1 = col_greater_value('A', 1) 
bgt1 = col_greater_value('B', 1) 

另一个函数的值赋给列:

def assign_value(cond_1, cond_2, value): 
    def assign_col_value(df, col): 
     df.loc[df.apply(cond_1, axis=1) & df.apply(cond_2, axis=1), col] =value 
    return assign_col_value 

最后,我们可以定义condtion-to-values mappin G作为

mapping = [(a1, b1, 4), 
      (a1, bgt1, 1), 
      (agt1, b1, 2), 
      (agt1, bgt1, 3)] 

构建assign_value_functions

m = [assign_value(x, y, z) for (x,y,z) in mapping] 

和各功能适用于数据框:

for f in m: 
    f(df, 'C') 
print(df) 

    A B ItemNumber 
0 1 1   1 
1 1 2   2 
2 1 3   3 
3 2 1   4 
4 2 2   5 
5 2 3   6 
6 3 1   7 
7 3 2   8 
8 3 3   9 

那么什么是问题? 这种方法似乎不是非常可扩展的。对于每个比较运算符,我似乎都需要定义一个全新的函数。比较运算符是一个变量吗? 目前,我只支持2个条件与&运营商连接。如何推广? 我不确定如何调用apply方法。我觉得应该有一个更简单的方法。

欢迎任何帮助

回答

1

你可以利用pandas.DataFrame.eval这里。首先,定义一个包含要应用的转换的字典trans。其次,采用辅助函数apply它接受的EVAL优势:

trans = {"C": {"A == 1 and B > 1": 1, 
       "B == 1 and A > 1": 2, 
       "A > 1 and B > 1": 3, 
       "A == 1 and B == 1": 4}} 

def apply(sub_df, trans_dict): 
    # sub_df = sub_df.copy() # in case you don't want change original df 
    for column, transforms in trans_dict.items(): 
     for transform, value in transforms.items(): 
      sub_df.loc[sub_df.eval(transform), column] = value 

    return sub_df 

apply(df, trans) 

    A B ItemNumber C 
0 1 1 1   4.0 
1 1 2 2   1.0 
2 1 3 3   1.0 
3 2 1 4   2.0 
4 2 2 5   3.0 
5 2 3 6   3.0 
6 3 1 7   2.0 
7 3 2 8   3.0 
8 3 3 9   3.0 

我觉得是有道理的使用熊猫EVAL这里获得更好的可读性。您现在可以向您的trans字典提供任何列条件值组合。

但是,我们还是有点违反DRY,因为像A == 1这样的每个原子条件都会被多次评估,而不是像您提供的示例那样只评估一次。但我想会有解决方法来有效地记住这些布尔序列。

+0

嘿,我真的很喜欢你的想法,它很简单,映射的定义比较精确。但是,我还不确定我是否在寻找更多功能的解决方案。我想,我的解决方案可以通过使用运算符模块和reduce函数来改进,以允许任意多个条件。但映射/转换可能不太可读。 – Quickbeam2k1

相关问题