我有一个如下数据框:
data = np.array([[5, 'red', 2,6, 8, 10],
[11, 'red', 3,9,6,15],
[8, 'blue', 0, 3, 5, 10],
[2, 'blue', 1, 2, 3, 4]])
df = pd.DataFrame(data, columns = ['A','B','red_lower', 'red_upper', 'blue_lower', 'blue_upper'])
A B red_lower red_upper blue_lower blue_upper
0 5 red 2 6 8 10
1 11 red 3 9 6 15
2 8 blue 0 3 5 10
3 2 blue 1 2 3 4
我想创建一个附加列,告诉我A列中的值是否在B列中指定的颜色范围内。 例如,在第0行中,由于5的名称为红色,因此我将检查5是否在2到6之间。是的,因此我将使新列的值为1。
所需结果:
A B red_lower red_upper blue_lower blue_upper in_range
0 5 red 2 6 8 10 1
1 11 red 3 9 6 15 0
2 8 blue 0 3 5 10 1
3 2 blue 1 2 3 4 0
我试图编写一个循环,但是我遇到了很多系列错误。我真的不想不得不拆分数据帧(按颜色),但是也许那是要走的路? (在我的实际数据框中,有六种不同的“颜色”,而不仅仅是两种)。
谢谢!
编辑:如果我们有额外的一栏,请告诉我值是否大于或小于范围!例如,第1行中的11超出范围,因此太高。表格应如下所示:
A B red_lower red_upper blue_lower blue_upper in_range
0 5 red 2 6 8 10 inside
1 11 red 3 9 6 15 above
2 8 blue 0 3 5 10 inside
3 2 blue 1 2 3 4 below
答案 0 :(得分:3)
justify
+ broadcast
+ mask
+ logical_and
您可以在此处使用一些漂亮的广播,而功能justify
则来自另一个答案。假设每种颜色都有一个有效范围。它还假定您所有的数字列实际上都是数字。
values = df.A.values
colors = df.B.values
range_frame = df.iloc[:, 2:]
ranges = range_frame.columns.str.split('_').str[0].values
m = colors != ranges[:, None]
masked = range_frame.mask(m)
jf = justify(masked.values, invalid_val=np.nan)[:, :2]
ir = np.logical_and(jf[:, 0] < values, values < jf[:, 1]).astype(int)
c1 = values <= jf[:, 0]
c2 = values >= jf[:, 1]
irl = np.select([c1, c2], ['below', 'above'], 'inside')
df.assign(in_range=ir, in_range_flag=irl)
A B red_lower red_upper blue_lower blue_upper in_range in_range_flag
0 5 red 2 6 8 10 1 inside
1 11 red 3 9 6 15 0 above
2 8 blue 0 3 5 10 1 inside
3 3 blue 1 2 3 4 0 below
stack
+ reshape
+ logical_and
再次做出与第一个答案相同的假设。
u = df.set_index(['A', 'B']).stack().rename_axis(['A', 'B', 'flag']).reset_index()
frame = u[u.flag.str.split('_').str[0] == u.B]
values = frame[::2].A.values
ranges = frame[0].values.reshape(-1, 2)
ir = np.logical_and(ranges[:, 0] < values, values < ranges[:, 1])
c1 = values <= ranges[:, 0]
c2 = values >= ranges[:, 1]
irl = np.select([c1, c2], ['below', 'above'], 'inside')
df.assign(in_range=ir, in_range_flag=irl)
这是@Divakar对justify
函数的定义:
def justify(a, invalid_val=0, axis=1, side='left'):
"""
Justifies a 2D array
Parameters
----------
A : ndarray
Input array to be justified
axis : int
Axis along which justification is to be made
side : str
Direction of justification. It could be 'left', 'right', 'up', 'down'
It should be 'left' or 'right' for axis=1 and 'up' or 'down' for axis=0.
"""
if invalid_val is np.nan:
mask = ~np.isnan(a)
else:
mask = a!=invalid_val
justified_mask = np.sort(mask,axis=axis)
if (side=='up') | (side=='left'):
justified_mask = np.flip(justified_mask,axis=axis)
out = np.full(a.shape, invalid_val)
if axis==1:
out[justified_mask] = a[mask]
else:
out.T[justified_mask.T] = a.T[mask.T]
return out
答案 1 :(得分:3)
这里使用groupby
拆分df和定义所处理的大部分步骤,这意味着您不需要每次都输入不同的颜色
l=[]
for name,x in df.groupby('B',sort=False):
s1=(x.A >= x.filter(like=name).iloc[:, 0]) & (x.A <= x.filter(like=name).iloc[:, 1])
s2=x.A<x.filter(like=name).iloc[:, 0]
l.extend(np.select([s1,s2],['inside','below'],default='above').tolist())
df['in_range']=l
df
Out[64]:
A B red_lower red_upper blue_lower blue_upper in_range
0 5 red 2 6 8 10 inside
1 11 red 3 9 6 15 above
2 8 blue 0 3 5 10 inside
3 2 blue 1 2 3 4 below