熊猫条件检查取决于行和列

时间:2020-03-31 08:57:29

标签: python pandas dataframe

我有以下阈值:

for a_x: red=10 blue=5
for a_y: red=50 blue=15
for b_x: red=8  blue=4
for b_y: red=40 blue=10

这是我拥有的数据框:

type  x1   x2  x3  y1  y2  y3  z
a     1    3   5   11  13  9   qaz
a     2    7   9   23  67  35  qeq
a     7    9   13  36  24  8   rfc
b     10   3   5   51  19  10  qwe
b     5    4   2   21  12  11  erg
b     1    2   3   9   7   8   gbt

现在对于类型为a的行,我想对列名称中包含x的任何列和包含y的a_y阈值列使用a_x阈值。

类似地,对于type = b的行,我想对包含x的列使用b_x阈值,而对于包含y的列使用b_y阈值。

最终,我想创建两个新的数据框,红色和蓝色,其中包含违反红色阈值的所有行(值> =阈值时被突破),仅蓝色阈值被违反(因此,如果红色阈值被违反则无需检查蓝色阈值,因为它将仅是红色的一部分,而不是蓝色。

因此,最后我们将具有以下数据框:

红色:

type  x1   x2  x3  y1  y2  y3  z
a     2    7   9   23  67  35  qeq
a     7    9   13  36  24  8   rfc
b     10   3   5   51  19  10  qwe

蓝色:

type  x1   x2  x3  y1  y2  y3  z
a     1    3   5   11  13  9   qaz
b     5    4   2   21  12  11  erg

type  x1   x2  x3  y1  y2  y3  z
a     1    3   5   11  13  9   qaz  -> x3 breaches blue
a     2    7   9   23  67  35  qeq  -> y2 breaches red
a     7    9   13  36  24  8   rfc  -> x3 breaches red
b     10   3   5   51  19  10  qwe  -> x1, y1 breaches red
b     5    4   2   21  12  11  erg  -> x1, x2, y1, y2, y3 breaches blue
b     1    2   3   9   7   8   gbt  -> no breach 

现在,显然,我可以遍历所有行和列并检查阈值违规,但是必须有一种更好的方法!

3 个答案:

答案 0 :(得分:1)

首先,我为嵌套字典创建条件:

d = {'a_x': {'red':10, 'blue':5},
     'a_y': {'red':50, 'blue':15},
     'b_x': {'red':8, 'blue':4},
     'b_y': {'red':40, 'blue':10}}

更好的格式是将redblue的值分别分配给外部字典:

from collections import defaultdict


d1 = defaultdict(dict)
for k, v in d.items():
    for k1, v1 in v.items():
      d1[k1][k] = v1

print (d1)
defaultdict(<class 'dict'>, {'red': {'a_x': 10, 'a_y': 50, 'b_x': 8, 'b_y': 40}, 
                             'blue': {'a_x': 5, 'a_y': 15, 'b_x': 4, 'b_y': 10}})

然后按每个字典循环,并按boolean indexing进行过滤,并用k划分_值:

red = [df.loc[df['type'].eq(k.split('_')[0]), 
            df.columns.str.startswith(k.split('_')[1])] >= v for k, v in d1['red'].items()]

然后通过concat将掩码连接在一起,并通过DataFrame.any测试至少一行是否匹配:

mask_red = pd.concat(red).any(level=0).any(axis=1)
# print (mask_red)


blue = [df.loc[df['type'].eq(k.split('_')[0]), 
           df.columns.str.startswith(k.split('_')[1])] >= v for k, v in d1['blue'].items()]

mask_blue = pd.concat(blue).any(level=0).any(axis=1)
# print (mask_blue)

最后一个过滤器匹配红色值:

df1 = df[mask_red]
print (df1)
  type  x1  x2  x3  y1  y2  y3    z
1    a   2   7   9  23  67  35  qeq
2    a   7   9  13  36  24   8  rfc
3    b  10   3   5  51  19  10  qwe

红色DataFrame中已经使用了排除的蓝色值:

df2 = df[mask_blue & ~mask_red]
print (df2)
  type  x1  x2  x3  y1  y2  y3    z
0    a   1   3   5  11  13   9  qaz
4    b   5   4   2  21  12  11  erg

为避免可能重复代码,请对字典蒙版使用字典理解:

d = {'a_x': {'red':10, 'blue':5},
     'a_y': {'red':50, 'blue':15},
     'b_x': {'red':8, 'blue':4},
     'b_y': {'red':40, 'blue':10}}


d1 = defaultdict(dict)
for k, v in d.items():
    for k1, v1 in v.items():
      d1[k1][k] = v1

print (d1)
defaultdict(<class 'dict'>, {'red': {'a_x': 10, 'a_y': 50, 'b_x': 8, 'b_y': 40}, 
                             'blue': {'a_x': 5, 'a_y': 15, 'b_x': 4, 'b_y': 10}})

masks = {k1: pd.concat([df.loc[df['type'].eq(k.split('_')[0]), 
               df.columns.str.startswith(k.split('_')[1])] >= v 
           for k, v in v1.items()]).any(level=0).any(axis=1)
           for k1, v1 in d1.items()}

print (masks)
{'red': 0    False
1     True
2     True
3     True
4    False
5    False
dtype: bool, 'blue': 0     True
1     True
2     True
3     True
4     True
5    False
dtype: bool}

df1 = df[masks['red']]
print (df1)
  type  x1  x2  x3  y1  y2  y3    z
1    a   2   7   9  23  67  35  qeq
2    a   7   9  13  36  24   8  rfc
3    b  10   3   5  51  19  10  qwe

df2 = df[masks['blue'] & ~masks['red']]
print (df2)
  type  x1  x2  x3  y1  y2  y3    z
0    a   1   3   5  11  13   9  qaz
4    b   5   4   2  21  12  11  erg

答案 1 :(得分:0)

您可以声明一些条件过滤器,例如:

 a_red_filter = (df[type] == 'a') & (df[['x1','x2','x3']] < [10]*3) &
(df[['y1','y2','y3']]<[50]*3)

 b_red_filter = (df[type] == 'b') & (df[['x1','x2','x3']] < [8]*3) &
(df[['y1','y2','y3']] <[40]*3)

,如果您执行命令:

df[a_red_filter | b_red_filter]

因此,您将获得用于红点的数据框:

type  x1   x2  x3  y1  y2  y3  z
a     2    7   9   23  67  35  qeq
a     7    9   13  36  24  8   rfc
b     10   3   5   51  19  10  qwe

显然与蓝色相同

答案 2 :(得分:0)

希望您能比我以前发布的烂书更好地阅读

#reshape table and create column of just the alphabets
M = (df.melt(['type','z'])
     .assign(letters=lambda x: x.variable.str[0])
     )

red_a = M.query('type=="a" and ((letters=="x" and value >=10) or (letters=="y" and value >=50))')
red_b = M.query('type=="b" and ((letters=="x" and value >=8) or (letters=="y" and value >=40))')
blue_a = M.query('type=="a" and ((letters=="x" and value >=5) or (letters=="y" and value >=15))')
blue_b = M.query('type=="b" and ((letters=="x" and value >=4) or (letters=="y" and value >=10))')

red_only = (pd.concat([red_a,red_b])
            .filter(['type','z'])
            .drop_duplicates('z')
           )

red_only_z = red_only["z"].tolist()

blue_only = (pd.concat([blue_a,blue_b])
             .filter(['type','z'])
              #if row already belongs in red, filter it out
             .query('z != @red_only_z')
             .drop_duplicates('z')
             )
#extract tables from the original dataframe
cond_red = df['z'].isin(red_only_z) & (df['type'].isin(red_only['type']))
cond_blue = df['z'].isin(blue_only['z']) & (df['type'].isin(blue_only['type']))
red_table = df.loc[cond_red]
blue_table = df.loc[cond_blue]

print(blues)    
    type    x1  x2  x3  y1  y2  y3  z
0   a       1   3   5   11  13  9   qaz
4   b       5   4   2   21  12  11  erg

print(reds)
    type    x1  x2  x3  y1  y2  y3  z
1   a       2   7   9   23  67  35  qeq
2   a       7   9   13  36  24  8   rfc
3   b      10   3   5   51  19  10  qwe