在熊猫中传播条件列值

时间:2018-08-21 14:28:14

标签: python pandas

我想创建一个指标变量,该变量将传播到与该指标具有相同客户周期值对的所有行。具体来说,如果bazyes,则我希望同一位客户和期间电子邮件的所有行都显示我的指标。

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用填充和另一个用填充?

3 个答案:

答案 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)

您可以分解CustomerPeriod的元组。然后使用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的哪个元素去了元组的原始列表

  1. 元组的原始列表

    [*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)]
    
  2. 分解后,唯一元组r

    r
    
    array([('A', 1), ('B', 1), ('A', 2), ('B', 2)], dtype=object)
    
  3. 位置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))