首先:也许这在代码审查中更合适,但我认为这里有更多的熊猫仿冒用户。如果您不这么认为,请随意移动
通常情况下,人们想要从现有列中计算新列: 请考虑以下pandas Dataframe
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
假设我们通过
计算新列'C'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
与呈现here的迭代方法相比,这对大型数据帧的执行速度也非常快。 特别是,这种方法的性能问题引导我看到上面提到的代码。
但是,此代码违反了DRY原则。复制粘贴感觉很难。
所以让我们更具功能性并定义两个curried函数:
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映射定义为
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
那么问题是什么? 这种方法似乎不太可扩展。对于每个比较运算符,我似乎需要定义一个全新的函数。比较运算符可以是变量吗? 目前,我只支持与& amp;运营商。如何概括呢? 我不确定调用apply方法。我觉得应该有一个更简单的方法。
欢迎任何帮助
答案 0 :(得分: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
这样的每个原子条件都会被多次评估,而不是像你提供的例子那样只评估一次。但我想有效地记住那些布尔系列会有变通方法。