在熊猫数据框中找到条纹

时间:2016-02-16 08:24:09

标签: python pandas dataframe

我有一个pandas数据帧如下:

time    winner  loser   stat
1       A       B       0
2       C       B       0
3       D       B       1
4       E       B       0
5       F       A       0
6       G       A       0
7       H       A       0
8       I       A       1

每一行都是匹配结果。第一列是比赛时间,第二列和第三列包含赢家/输家,第四列是比赛中的一个数据。

我想检测每个输家的统计数据为零。

预期结果应如下所示:

time    winner  loser   stat    streak
1       A       B       0       1
2       C       B       0       2
3       D       B       1       0
4       E       B       0       1
5       F       A       0       1
6       G       A       0       2
7       H       A       0       3
8       I       A       1       0

在伪代码中,算法应该像这样工作:

  • .groupby loser列。
  • 然后迭代每个loser
  • 的每一行 在每行
  • 中,查看stat列:如果它包含0,则将streak值从上一行增加0。如果不是0,则启动新的streak,即将0放入streak列。

所以.groupby很清楚。但是,我需要某种.apply,我可以看看上一行?这就是我被困住的地方。

3 个答案:

答案 0 :(得分:13)

您可以apply自定义函数f,然后cumsumcumcountastype

def f(x):
    x['streak'] = x.groupby( (x['stat'] != 0).cumsum()).cumcount() + 
                  ( (x['stat'] != 0).cumsum() == 0).astype(int) 
    return x

df = df.groupby('loser', sort=False).apply(f)
print df
   time winner loser  stat  streak
0     1      A     B     0       1
1     2      C     B     0       2
2     3      D     B     1       0
3     4      E     B     0       1
4     5      F     A     0       1
5     6      G     A     0       2
6     7      H     A     0       3
7     8      I     A     1       0

为了更好的未成年:

def f(x):
    x['c'] = (x['stat'] != 0).cumsum()
    x['a'] = (x['c'] == 0).astype(int)
    x['b'] = x.groupby( 'c' ).cumcount()

    x['streak'] = x.groupby( 'c' ).cumcount() + x['a']

    return x
df = df.groupby('loser', sort=False).apply(f)
print df
   time winner loser  stat  c  a  b  streak
0     1      A     B     0  0  1  0       1
1     2      C     B     0  0  1  1       2
2     3      D     B     1  1  0  0       0
3     4      E     B     0  1  0  1       1
4     5      F     A     0  0  1  0       1
5     6      G     A     0  0  1  1       2
6     7      H     A     0  0  1  2       3
7     8      I     A     1  1  0  0       0

答案 1 :(得分:4)

不像jezrael的answer那么优雅,但对我来说更容易理解......

首先,定义一个适用于单个输家的函数:

def f(df):
    df['streak2'] = (df['stat'] == 0).cumsum()
    df['cumsum'] = np.nan
    df.loc[df['stat'] == 1, 'cumsum'] = df['streak2']
    df['cumsum'] = df['cumsum'].fillna(method='ffill')
    df['cumsum'] = df['cumsum'].fillna(0)
    df['streak'] = df['streak2'] - df['cumsum']
    df.drop(['streak2', 'cumsum'], axis=1, inplace=True)
    return df

条纹本质上是cumsum,但我们需要在每次stat为1时重置它。因此,我们减去cumsum的值,其中stat为1 ,继续推进到下一个。

然后groupbyapply由输家:

df.groupby('loser').apply(f)

结果与预期一致。

答案 2 :(得分:3)

您可以使用iterrows访问上一行:

df['streak'] = 0

for i, row in df.iterrows():
    if i != 0:
        if row['stat'] == 0:
            if row['loser'] == df.ix[i-1, 'loser']:
                df.ix[i, 'streak'] = df.ix[i-1, 'streak'] + 1        
            else:
                df.ix[i, 'streak'] = 1
    else:
        if row['stat'] == 0:
            df.ix[i, 'streak'] = 1

给出了:

In [210]: df
Out[210]:
   time winner loser  stat  streak
0     1      A     B     0       1
1     2      C     B     0       2
2     3      D     B     1       0
3     4      E     B     0       1
4     5      F     A     0       1
5     6      G     A     0       2
6     7      H     A     0       3
7     8      I     A     1       0