n 多个 Pandas 列的值的动态比较
Dynamic comparison of values of n multiple Pandas columns
假设用户可以输入列和值来比较 DF,因此我们可以:
column_list = ['col1', 'col2', 'col3']
value_list = [val1, val2, val3]
所以对于 select 满足 col1 >= val1 AND col2 >= val2 AND col3 >= val3 的行,我们会写:
selection = (df['col1'] >= val1) & (df['col2'] >= val2) & (df['col3'] >= val3))
或者可以是以下形式:
selection = df.loc[(df['col1'] >= val1) & (df['col2'] >= val2) & (df['col3'] >= val3)]
列数事先不知道,所以我们可以有 n 列。我们可以试试这个方法:
if n=1:
selection = (df['col1'] >= val1))
elif n=2:
selection = (df['col1'] >= val1) & (df['col2'] >= val2))
elif n=3:
selection = (df['col1'] >= val1) & (df['col2'] >= val2) & (df['col3'] >= val3))
但这既不具有可扩展性也不高效。我尝试在给定输入列表的情况下使用 for 循环生成字符串 df['col<>'] >= val<>)
,但由于 str
格式,它不适用于 Pandas。
最好的 pythonic 方法是什么?为了避免所有选项都带有 if 和 else 语句。
要对所有列使用相同的运算符进行比较,请创建一个包含值和列 ID 的系列,并使用它与数据帧进行对齐比较:
df[df.gt(pd.Series(value_list, index=column_list)).all(1)]
示例输入:
>>> value_list
[3, 7, 11]
>>> df
col1 col2 col3
0 0 1 2
1 3 4 5
2 6 7 8
3 9 10 11
4 12 13 14
输出:
col1 col2 col3
4 12 13 14
中间体:
>>> pd.Series(value_list, index=column_list)
col1 3
col2 7
col3 11
>>> df.gt(pd.Series(value_list, index=column_list))
col1 col2 col3
0 False False False
1 False False False
2 True False False
3 True True False
4 True True True
>>> df.gt(pd.Series(value_list, index=column_list)).all(1)
0 False
1 False
2 False
3 False
4 True
这是处理任意长比较列表的另一种可能性(我单独发布,因为方法完全不同):
column_list = ['col1', 'col2', 'col3']
value_list = [3, 7, 11]
operator_list = [pd.Series.gt, pd.Series.ge, pd.Series.gt]
op_dic = dict(zip(column_list, operator_list))
val_dic = dict(zip(column_list, value_list))
df[df.apply(lambda c: op_dic[c.name](c, val_dic[c.name])).all(1)]
工作原理:
使用 apply
,我们对所有列执行自定义操作,每行 return 一个布尔值,然后取对所有都为 True 的行。
输入:
col1 col2 col3
0 0 1 2
1 3 4 5
2 6 7 8
3 9 10 11
4 12 13 14
输出:
col1 col2 col3
4 12 13 14
注意。为了更简洁,也可以这样做:
from operator import gt, ge
operator_list = [gt, ge, gt]
有了这样的df
,
In [1]: df
Out[1]:
a b c
0 1 7 7
1 2 1 1
2 6 2 6
3 2 6 3
4 3 3 8
5 5 9 0
以及值、列和任意运算符,
In [2]: import operator
In [3]: values = [1, 2, 7]
In [4]: columns = ['a', 'b', 'c']
In [5]: operators = [operator.gt, operator.ge, operator.le] # >, >=, <=
制作 df
的副本并遍历压缩项目:
In [6]: selection = df.copy()
In [7]: for col, op, val in zip(columns, operators, values):
...: selection = selection[op(selection[col], val)]
...:
In [8]: selection
Out[8]:
a b c
2 6 2 6
3 2 6 3
5 5 9 0
当然,如果您事先不知道有多少列,那么您似乎也可能事先不知道 operators无论是哪种,都违背了目的。如果您只需要使用一个运算符,这将变得 多 容易,但从您的示例来看,情况似乎并非如此。
如果您的原文 post 确实有拼写错误(所有比较应该是 >
,还是全部 >=
?)并且您实际上打算执行 single比较操作,见@mozway的。
假设用户可以输入列和值来比较 DF,因此我们可以:
column_list = ['col1', 'col2', 'col3']
value_list = [val1, val2, val3]
所以对于 select 满足 col1 >= val1 AND col2 >= val2 AND col3 >= val3 的行,我们会写:
selection = (df['col1'] >= val1) & (df['col2'] >= val2) & (df['col3'] >= val3))
或者可以是以下形式:
selection = df.loc[(df['col1'] >= val1) & (df['col2'] >= val2) & (df['col3'] >= val3)]
列数事先不知道,所以我们可以有 n 列。我们可以试试这个方法:
if n=1:
selection = (df['col1'] >= val1))
elif n=2:
selection = (df['col1'] >= val1) & (df['col2'] >= val2))
elif n=3:
selection = (df['col1'] >= val1) & (df['col2'] >= val2) & (df['col3'] >= val3))
但这既不具有可扩展性也不高效。我尝试在给定输入列表的情况下使用 for 循环生成字符串 df['col<>'] >= val<>)
,但由于 str
格式,它不适用于 Pandas。
最好的 pythonic 方法是什么?为了避免所有选项都带有 if 和 else 语句。
要对所有列使用相同的运算符进行比较,请创建一个包含值和列 ID 的系列,并使用它与数据帧进行对齐比较:
df[df.gt(pd.Series(value_list, index=column_list)).all(1)]
示例输入:
>>> value_list
[3, 7, 11]
>>> df
col1 col2 col3
0 0 1 2
1 3 4 5
2 6 7 8
3 9 10 11
4 12 13 14
输出:
col1 col2 col3
4 12 13 14
中间体:
>>> pd.Series(value_list, index=column_list)
col1 3
col2 7
col3 11
>>> df.gt(pd.Series(value_list, index=column_list))
col1 col2 col3
0 False False False
1 False False False
2 True False False
3 True True False
4 True True True
>>> df.gt(pd.Series(value_list, index=column_list)).all(1)
0 False
1 False
2 False
3 False
4 True
这是处理任意长比较列表的另一种可能性(我单独发布,因为方法完全不同):
column_list = ['col1', 'col2', 'col3']
value_list = [3, 7, 11]
operator_list = [pd.Series.gt, pd.Series.ge, pd.Series.gt]
op_dic = dict(zip(column_list, operator_list))
val_dic = dict(zip(column_list, value_list))
df[df.apply(lambda c: op_dic[c.name](c, val_dic[c.name])).all(1)]
工作原理:
使用 apply
,我们对所有列执行自定义操作,每行 return 一个布尔值,然后取对所有都为 True 的行。
输入:
col1 col2 col3
0 0 1 2
1 3 4 5
2 6 7 8
3 9 10 11
4 12 13 14
输出:
col1 col2 col3
4 12 13 14
注意。为了更简洁,也可以这样做:
from operator import gt, ge
operator_list = [gt, ge, gt]
有了这样的df
,
In [1]: df
Out[1]:
a b c
0 1 7 7
1 2 1 1
2 6 2 6
3 2 6 3
4 3 3 8
5 5 9 0
以及值、列和任意运算符,
In [2]: import operator
In [3]: values = [1, 2, 7]
In [4]: columns = ['a', 'b', 'c']
In [5]: operators = [operator.gt, operator.ge, operator.le] # >, >=, <=
制作 df
的副本并遍历压缩项目:
In [6]: selection = df.copy()
In [7]: for col, op, val in zip(columns, operators, values):
...: selection = selection[op(selection[col], val)]
...:
In [8]: selection
Out[8]:
a b c
2 6 2 6
3 2 6 3
5 5 9 0
当然,如果您事先不知道有多少列,那么您似乎也可能事先不知道 operators无论是哪种,都违背了目的。如果您只需要使用一个运算符,这将变得 多 容易,但从您的示例来看,情况似乎并非如此。
如果您的原文 post 确实有拼写错误(所有比较应该是 >
,还是全部 >=
?)并且您实际上打算执行 single比较操作,见@mozway的