我想创建一个指标变量,该变量将传播到与该指标具有相同客户周期值对的所有行。具体来说,如果baz
为yes
,则我希望同一位客户和期间电子邮件的所有行都显示我的指标。
df
Customer Period Question Score
A 1 foo 2
A 1 bar 3
A 1 baz yes
A 1 biz 1
B 1 bar 2
B 1 baz no
B 1 qux 3
A 2 foo 5
A 2 baz yes
B 2 baz yes
B 2 biz 2
我尝试过
df['Indicator'] = np.where(
(df.Question.str.contains('baz') & (df.Score == 'yes')),
1, 0)
返回
Customer Period Question Score Indicator
A 1 foo 2 0
A 1 bar 3 0
A 1 baz yes 1
A 1 biz 1 0
B 1 bar 2 0
B 1 baz no 0
B 1 qux 3 0
A 2 foo 5 0
A 2 baz yes 1
B 2 baz yes 1
B 2 biz 2 0
但这是所需的输出:
Customer Period Question Score Indicator
A 1 foo 2 1
A 1 bar 3 1
A 1 baz yes 1
A 1 biz 1 1
B 1 bar 2 0
B 1 baz no 0
B 1 qux 3 0
A 2 foo 5 1
A 2 baz yes 1
B 2 baz yes 1
B 2 biz 2 1
我不确定该如何得到我想要的东西。也许groupby用填充和另一个用填充?
答案 0 :(得分:5)
您可以使用
In [954]: df['Indicator'] = (df.assign(eq=df.Question.eq('baz') & df.Score.eq('yes'))
.groupby(['Customer', 'Period'])['eq']
.transform('any').astype(int))
In [955]: df
Out[955]:
Customer Period Question Score Indicator
0 A 1 foo 2 1
1 A 1 bar 3 1
2 A 1 baz yes 1
3 A 1 biz 1 1
4 B 1 bar 2 0
5 B 1 baz no 0
6 B 1 qux 3 0
7 A 2 foo 5 1
8 A 2 baz yes 1
9 B 2 baz yes 1
10 B 2 biz 2 1
详细信息
In [956]: df.Question.eq('baz') & df.Score.eq('yes')
Out[956]:
0 False
1 False
2 True
3 False
4 False
5 False
6 False
7 False
8 True
9 True
10 False
dtype: bool
In [957]: df.assign(eq=df.Question.eq('baz') & df.Score.eq('yes'))
Out[957]:
Customer Period Question Score Indicator eq
0 A 1 foo 2 1 False
1 A 1 bar 3 1 False
2 A 1 baz yes 1 True
3 A 1 biz 1 1 False
4 B 1 bar 2 0 False
5 B 1 baz no 0 False
6 B 1 qux 3 0 False
7 A 2 foo 5 1 False
8 A 2 baz yes 1 True
9 B 2 baz yes 1 True
10 B 2 biz 2 1 False
答案 1 :(得分:4)
这是一种方式。想法是将布尔掩码与MultiIndex
一起使用。然后使用pd.Series.isin
与过滤后的索引进行比较。
mask = (df['Question'] == 'baz') & (df['Score'] == 'yes')
idx_cols = ['Customer', 'Period']
idx = df.set_index(idx_cols).loc[mask.values].index
df['Indicator'] = pd.Series(df.set_index(idx_cols).index.values).isin(idx).astype(int)
print(df)
Customer Period Question Score Indicator
0 A 1 foo 2 1
1 A 1 bar 3 1
2 A 1 baz yes 1
3 A 1 biz 1 1
4 B 1 bar 2 0
5 B 1 baz no 0
6 B 1 qux 3 0
7 A 2 foo 5 1
8 A 2 baz yes 1
9 B 2 baz yes 1
10 B 2 biz 2 1
答案 2 :(得分:4)
您可以分解Customer
和Period
的元组。然后使用np.logical_or.at
进行逐组any
i, r = pd.factorize([*zip(df.Customer, df.Period)])
a = np.zeros(len(r), dtype=np.bool8)
np.logical_or.at(a, i, df.eval('Question == "baz" and Score == "yes"'))
df.assign(Indicator=a[i].astype(np.int64))
Customer Period Question Score Indicator
0 A 1 foo 2 1
1 A 1 bar 3 1
2 A 1 baz yes 1
3 A 1 biz 1 1
4 B 1 bar 2 0
5 B 1 baz no 0
6 B 1 qux 3 0
7 A 2 foo 5 1
8 A 2 baz yes 1
9 B 2 baz yes 1
10 B 2 biz 2 1
i, r = pd.factorize([*zip(df.Customer, df.Period)])
在(Customer, Period)
中产生唯一的r
对,其中i
是一个数组,用于跟踪r
的哪个元素去了元组的原始列表>
元组的原始列表
[*zip(df.Customer, df.Period)]
[('A', 1),
('A', 1),
('A', 1),
('A', 1),
('B', 1),
('B', 1),
('B', 1),
('A', 2),
('A', 2),
('B', 2),
('B', 2)]
分解后,唯一元组r
r
array([('A', 1), ('B', 1), ('A', 2), ('B', 2)], dtype=object)
位置i
i
array([0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3])
我现在可以使用i
作为索引,通过对any
使用Numpy的at
方法来评估Numpy中的分组ufuncs
。基本上,这使我可以预先创建一个数组,其值可能会根据我的at
操作而改变。然后指定一个索引数组(即i
)和一个匹配i
大小的数组,这是我对该索引进行操作的第二部分。
我最终将其用作匹配数组
df.eval('Question == "baz" and Score == "yes"')
0 False
1 False
2 True
3 False
4 False
5 False
6 False
7 False
8 True
9 True
10 False
dtype: bool
让我详细介绍一下
Flag GroupIndex Group State of a
0 False 0 (A, 1) [0, 0, 0, 0] # Flag is False, So do Nothing
1 False 0 (A, 1) [0, 0, 0, 0] # Flag is False, So do Nothing
2 True 0 (A, 1) [1, 0, 0, 0] # Flag is True, or_eq for Index 0
3 False 0 (A, 1) [1, 0, 0, 0] # Flag is False, So do Nothing
4 False 1 (B, 1) [1, 0, 0, 0] # Flag is False, So do Nothing
5 False 1 (B, 1) [1, 0, 0, 0] # Flag is False, So do Nothing
6 False 1 (B, 1) [1, 0, 0, 0] # Flag is False, So do Nothing
7 False 2 (A, 2) [1, 0, 0, 0] # Flag is False, So do Nothing
8 True 2 (A, 2) [1, 0, 1, 0] # Flag is True, or_eq for Index 2
9 True 3 (B, 2) [1, 0, 1, 1] # Flag is True, or_eq for Index 3
10 False 3 (B, 2) [1, 0, 1, 1] # Flag is False, So do Nothing
最后的State
是[1, 0, 1, 1]
或布尔值[True, False, True, True]
。这代表了or
a
累积
a
array([ True, False, True, True])
如果我将其与i
中的索引位置一起切片并转换为整数,则会得到
a[i].astype(np.int64)
array([1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1])
这正是我们想要的。
最后,我使用assign
生成带有新列的数据框副本。
df.assign(Indicator=a[i].astype(np.int64))
Customer Period Question Score Indicator
0 A 1 foo 2 1
1 A 1 bar 3 1
2 A 1 baz yes 1
3 A 1 biz 1 1
4 B 1 bar 2 0
5 B 1 baz no 0
6 B 1 qux 3 0
7 A 2 foo 5 1
8 A 2 baz yes 1
9 B 2 baz yes 1
10 B 2 biz 2 1
脾气暴躁通常会更快。
下面是一种稍微优化的方法。 (基本相同)
i, r = pd.factorize([*zip(df.Customer, df.Period)])
a = np.zeros(len(r), dtype=np.bool8)
q = df.Question.values == 'baz'
s = df.Score.values == 'yes'
m = q & s
np.logical_or.at(a, i, m)
df.assign(Indicator=a[i].astype(np.int64))