2016-02-16 32 views
9

ho un dataframe panda come segue:striature trovando in panda dataframe

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 

ogni riga è un risultato della partita. la prima colonna è l'ora della partita, la seconda e la terza colonna contengono il vincitore/perdente e la quarta colonna è una statistica della partita.

Desidero rilevare strisce di zeri per questa statistica per perdente.

Il risultato atteso dovrebbe essere simile a questo:

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 

In pseudocodice l'algoritmo dovrebbe funzionare in questo modo:

  • .groupbyloser colonna.
  • poi iterare su ciascuna fila di ciascun gruppo loser
  • in ciascuna riga, la colonna stat: se contiene 0, quindi incrementare il valore streak dalla riga precedente 0. se non è 0, quindi avviare un nuovo streak, ovvero inserire 0 nella colonna streak.

Quindi il .groupby è chiaro. Ma poi avrei bisogno di una sorta di .apply dove posso guardare la riga precedente? questo è dove sono bloccato.

+0

Non è esattamente una risposta, ma pensa che la soluzione potrebbe essere trovata combinando [questo] (http://stackoverflow.com/questions/17266129/python-pandas-conditional-sums) e [questo] (http://stackoverflow.com/questions/35314936/summing-values-in-read-csv-python) –

risposta

3

Non elegante come Jezrael di answer, ma per me più facile da capire ...

In primo luogo, definire una funzione che funziona con un solo perdente:

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 

La sequenza è essenzialmente cumsum, ma è necessario ripristinarla ogni volta stat è 1. Si sottrae quindi il valore dello cumsum dove stat è 1, portato f ORIZZONTE fino al prossimo 1.

Poi groupby e apply da perdente:

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

Il risultato è come previsto.

+0

Mi piace di più questa soluzione, perché è davvero trasparente. inoltre, la risposta di jezrael funziona solo se la colonna stat ha valori 0 o 1. In effetti, i miei dati reali contengono anche altri valori (ad es. 3, 4, 5) nella colonna stat. scusa, questo è ovviamente il mio cattivo, per non averlo indicato nel mio esempio. Grazie per l'aiuto! – beta

13

Puoi apply funzione personalizzata f, poi cumsum, cumcount e astype:

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 

Per una migliore undestanding:

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 
3

Si potrebbe utilizzare iterrows per accedere riga precedente:

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 

che dà:

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