2016-05-11 13 views
3

Ich habe eine DataFrame von einigen Transaktionen. Ich möchte diese Transaktionen in Bezug auf ihre Spaltenwerte item und time gruppieren: Ziel ist es, Elemente zu gruppieren, die innerhalb von 1 Stunde voneinander entfernt sind. Wir beginnen also eine neue Gruppe zum Zeitpunkt der nächsten Beobachtung, die nicht innerhalb einer Stunde nach der Beobachtung vorlag (siehe Spalte start time in DataFrameB).So gruppieren Sie Zeilen innerhalb eines Zeitraums mit Python

Hier sind die Daten: Ich möchte A zu B konvertieren.

A= 
item time    result 
A 2016-04-18 13:08:25 Y 
A 2016-04-18 13:57:05 N 
A 2016-04-18 14:00:12 N 
A 2016-04-18 23:45:50 Y 
A 2016-04-20 16:53:48 Y 
A 2016-04-20 17:11:47 N 
B 2016-04-18 15:24:48 N 
C 2016-04-23 13:20:44 N 
C 2016-04-23 14:02:23 Y 


B= 
item start time   end time  Ys Ns total count 
A 2016-04-18 13:08:25 2016-04-18 14:08:25 1 2 3 
A 2016-04-18 23:45:50 2016-04-18 00:45:50 1 0 1 
A 2016-04-20 16:53:48 2016-04-20 17:53:48 1 1 2 
B 2016-04-18 15:24:48 2016-04-18 16:24:48 0 1 1 
C 2016-04-23 13:20:44 2016-04-23 14:20:44 1 1 2 

Hier ist, was ich getan habe:

grouped = A.groupby('item') 
A['end'] = (grouped['time'].transform(lambda grp: grp.min()+pd.Timedelta(hours=1))) 
A2 = A.loc[(A['time'] <= A['end'])] 

Das ist mir pro Tag eine Gruppe gibt: die Transaktion innerhalb 1 Stunde nach der ersten Transaktion. Also, ich vermisse andere Transaktionen am selben Tag, aber mehr als 1 Stunde von der ersten Stunde. Mein Kampf ist, wie man diese Gruppen bekommt. Ich kann dann pd.crosstab verwenden, um die gewünschten Details aus der result Spalte zu erhalten.

Eine weitere Idee, die ich habe, ist A von item und time zu sortieren, und dann Zeile für Zeile gehen. Wenn die Zeit innerhalb von 1 Stunde nach der vorherigen Zeile liegt, wird diese Gruppe hinzugefügt, andernfalls wird eine neue Gruppe erstellt.

+2

Es gibt viele Fragen offen. Wie, gruppiert innerhalb einer Stunde nach wann? Eine Stunde erster Beobachtung? Was ist mit der nächsten Stunde? Fängt es an, als die letzte Stunde aufgehört hat? Oder beginnen wir eine neue Stunde zur Zeit der nächsten Beobachtung, die nicht innerhalb einer Stunde nach der Beobachtung vorher war? – piRSquared

+0

Was ist in Ihrem Code "gruppiert"? Wie haben Sie es bekommen? – MaxU

+0

@piRSquared Ich habe weitere Details zur Klärung hinzugefügt. – Ana

Antwort

1

1) eine window_end Säule für die spätere Verwendung mit .groupby() einrichten und definieren .get_windows() für jede item Gruppe, um zu überprüfen, ob ein row die aktuellen Strom 1 Std Fenster passen, oder nichts tun und den initialisierten Wert zu halten.Auf alle item Gruppen:

df['window_end'] = df.time + pd.Timedelta('1H') 

def get_windows(data): 
    window_end = data.iloc[0].window_end 
    for index, row in data.iloc[1:].iterrows(): 
     if window_end > row.time: 
      df.loc[index, 'window_end'] = window_end 
     else: 
      window_end = row.window_end 

df.groupby('item').apply(lambda x: get_windows(x)) 

2) Verwenden Sie windows und item mit .groupby() und Rück .value_counts() als transposedDataFrame, aufzuräumen index, und fügen Sie total:

df = df.groupby(['window_end', 'item']).result.apply(lambda x: x.value_counts().to_frame().T) 
df = df.fillna(0).astype(int).reset_index(level=2, drop=True) 
df['total'] = df.sum(axis=1) 

zu erhalten:

      N Y total 
window_end   item    
2016-04-18 14:08:25 A A 2 1  3 
2016-04-18 16:24:48 B B 1 0  1 
2016-04-19 00:45:50 A A 0 1  1 
2016-04-20 17:53:48 A A 1 1  2 
2016-04-23 14:20:44 C C 1 1  2 
+0

Danke, ja leider kann ich Hour nicht als Grouper benutzen. – Ana

+0

Siehe aktualisierte Antwort. – Stefan

+0

Danke, Ein paar Kommentare. Im zweiten Schritt sollte 'windows' durch' window_end' und '' ersetzt werden, oder? Vielleicht möchten Sie auch einen anderen für Ihren 'Result'Dataframe verwenden, damit er nicht mit der Spalte'Ergebnis' verwechselt wird. – Ana

1

insi rot (+1) von Stefan Lösung kam ich zu diesem:

B = (A.groupby(['item', A.groupby('item')['time'] 
         .diff().fillna(0).dt.total_seconds()//60//60 
       ], 
       as_index=False)['time'].min() 
) 


B[['N','Y']] = (A.groupby(['item', A.groupby('item')['time'] 
            .diff().fillna(0).dt.total_seconds()//60//60 
          ])['result'] 
       .apply(lambda x: x.value_counts().to_frame().T).fillna(0) 
       .reset_index()[['N','Y']] 
) 

Ausgang:

In [178]: B 
Out[178]: 
    item    time N Y 
0 A 2016-04-18 13:08:25 3.0 1.0 
1 A 2016-04-18 23:45:50 0.0 1.0 
2 A 2016-04-20 16:53:48 0.0 1.0 
3 B 2016-04-18 15:24:48 1.0 0.0 
4 C 2016-04-23 13:20:44 1.0 1.0 

PS ist die Idee A.groupby('item')['time'].diff().fillna(0).dt.total_seconds()//60//60 als Teil der Gruppierung zu verwenden:

In [179]: A.groupby('item')['time'].diff().fillna(0).dt.total_seconds()//60//60 
Out[179]: 
0  0.0 
1  0.0 
2  0.0 
3  9.0 
4 41.0 
5  0.0 
6  0.0 
7  0.0 
8  0.0 
Name: time, dtype: float64 
+0

Thatnks @ MaxU, bekomme ich 'AttributeError: 'TimeDeltaProperties' Objekt hat keine Attribut 'total_seconds''Fehler. Ich habe 'Datetime als dt importieren'. – Ana

1

Setup

import pandas as pd 
from StringIO import StringIO 

text = """item time    result 
A 2016-04-18 13:08:25 Y 
A 2016-04-18 13:57:05 N 
A 2016-04-18 14:00:12 N 
A 2016-04-18 23:45:50 Y 
A 2016-04-20 16:53:48 Y 
A 2016-04-20 17:11:47 N 
B 2016-04-18 15:24:48 N 
C 2016-04-23 13:20:44 N 
C 2016-04-23 14:02:23 Y 
""" 

df = pd.read_csv(StringIO(text), delimiter="\s{2,}", parse_dates=[1], engine='python') 

So

lution

Ich brauchte ein paar Prozessfunktionen zu erstellen:

def set_time_group(df): 
    cur_time = pd.NaT 
    for index, row in df.iterrows(): 
     if pd.isnull(cur_time): 
      cur_time = row.time 
     delta = row.time - cur_time 
     if delta.seconds/3600. < 1: 
      df.loc[index, 'time_ref'] = cur_time 
     else: 
      df.loc[index, 'time_ref'] = row.time 
      cur_time = row.time 
    return df 

def summarize_results(df): 
    df_ = df.groupby('result').count().iloc[:, 0] 
    df_.loc['total count'] = df_.sum() 
    return df_ 

dfg1 = df.groupby('item').apply(set_time_group) 
dfg2 = dfg1.groupby(['item', 'time_ref']).apply(summarize_results) 
df_f = dfg2.unstack().fillna(0) 

Demonstration

print df_f 

result      N Y total count 
item time_ref         
A 2016-04-18 13:08:25 2.0 1.0   3.0 
    2016-04-18 23:45:50 0.0 1.0   1.0 
    2016-04-20 16:53:48 1.0 1.0   2.0 
B 2016-04-18 15:24:48 1.0 0.0   1.0 
C 2016-04-23 13:20:44 1.0 1.0   2.0 
Verwandte Themen