2017-06-20 6 views
2

Ich muss die Werte der Zeilen basierend auf den Werten einer anderen Spalte subtrahieren. Meine Datenrahmen sieht wie folgt aus:Optimale Möglichkeit, Zeilen basierend auf Spaltenwerten in Python zu subtrahieren

Id | col1 | col2 | col3 | 
1 | 2016-01-02 | 7:00:00 | Yes | 
1 | 2016-01-02 | 7:05:00 | No | 
1 | 2016-01-02 | 7:10:00 | Yes | 
1 | 2016-01-02 | 8:00:00 | No | 
2 | 2016-01-02 | 7:10:00 | Yes | 
2 | 2016-01-02 | 7:50:00 | No | 
2 | 2016-01-02 | 9:00:00 | No | 
2 | 2016-01-02 | 9:10:00 | No | 
2 | 2016-01-02 | 9:15:00 | No | 
3 | 2016-01-02 | 6:05:00 | Yes | 
3 | 2016-01-02 | 6:10:00 | Yes | 
3 | 2016-01-02 | 6:20:00 | Yes | 
3 | 2016-01-02 | 6:45:00 | No | 

Ich brauche die durchschnittliche Zeitdifferenz in der Kombination von col1 und col2 auf den Wert der col3 zu berechnen. Die Regel lautet:

Wann immer es eine Yes in col3 ist tun row-next row

Eine vereinfachte Version von dem, was ich bisher getan haben, ist eine Schleife durch alle Werte in der Datenrahmen und tun dies :

for i in range(len(df)): 
    if df['col3'][i] == 'Yes': 
     date1 = datetime.combine(df['col1'][i], df['col2'][i]) 
     date2 = datetime.combine(df['col1'][i+1], df['col2'][i+1]) 
     dict[df['Id'][i]] = date1-date2 

Die Variable dict ist nur ein Wörterbuch, das die Ergebnisse für jede unterschiedliche Id hält.

Da ich mehr als 6MM Reihen habe, benötigt die Schleife viel Zeit, so dass ich mich gefragt habe, ob jemand eine effizientere und elegantere Lösung finden könnte.

Danke!

Antwort

1

Ich glaube, Sie verwenden können:

#datetime column - add time to dates 
df['datetime'] = pd.to_datetime(df['col1']) + pd.to_timedelta(df['col2']) 
#get difference of all values, filter by multiple mask only if `Yes` 
#convert to ns for numeric for aggregate 
df['dif']=df['datetime'].diff(-1).mul(df['col3'] == 'Yes').fillna(0).values.astype(np.int64) 
print (df) 
    Id  col1  col2 col3   datetime   dif 
0 1 2016-01-02 7:00:00 Yes 2016-01-02 07:00:00 -300000000000 
1 1 2016-01-02 7:05:00 No 2016-01-02 07:05:00    0 
2 1 2016-01-02 7:10:00 Yes 2016-01-02 07:10:00 -3000000000000 
3 1 2016-01-02 8:00:00 No 2016-01-02 08:00:00    0 
4 2 2016-01-02 7:10:00 Yes 2016-01-02 07:10:00 -2400000000000 
5 2 2016-01-02 7:50:00 No 2016-01-02 07:50:00    0 
6 2 2016-01-02 9:00:00 No 2016-01-02 09:00:00    0 
7 2 2016-01-02 9:10:00 No 2016-01-02 09:10:00    0 
8 2 2016-01-02 9:15:00 No 2016-01-02 09:15:00    0 
9 3 2016-01-02 6:05:00 Yes 2016-01-02 06:05:00 -300000000000 
10 3 2016-01-02 6:10:00 Yes 2016-01-02 06:10:00 -600000000000 
11 3 2016-01-02 6:20:00 Yes 2016-01-02 06:20:00 -1500000000000 
12 3 2016-01-02 6:45:00 No 2016-01-02 06:45:00    0 

d = pd.to_timedelta(df.groupby('Id')['dif'].mean()).to_dict() 
print (d) 
{1: Timedelta('-1 days +23:46:15'), 
2: Timedelta('-1 days +23:52:00'), 
3: Timedelta('-1 days +23:50:00')} 

Was gleiche ist, wie:

datetime = pd.to_datetime(df['col1']) + pd.to_timedelta(df['col2']) 
diff = datetime.diff(-1).mul(df['col3'] == 'Yes').fillna(0).values.astype(np.int64) 
d = pd.to_timedelta(pd.Series(diff, index=df.index).groupby(df['Id']).mean()).to_dict() 
print (d) 
{1: Timedelta('-1 days +23:46:15'), 
2: Timedelta('-1 days +23:52:00'), 
3: Timedelta('-1 days +23:50:00')} 

Aber wenn absolute Werte müssen für entfernen negativen Timedelta numpy.abs hinzufügen:

df['datetime'] = pd.to_datetime(df['col1']) + pd.to_timedelta(df['col2']) 
df['dif'] = np.abs(df['datetime'].diff(-1) 
           .mul(df['col3'] == 'Yes') 
           .fillna(0) 
           .values 
           .astype(np.int64)) 
print (df) 
    Id  col1  col2 col3   datetime   dif 
0 1 2016-01-02 7:00:00 Yes 2016-01-02 07:00:00 300000000000 
1 1 2016-01-02 7:05:00 No 2016-01-02 07:05:00    0 
2 1 2016-01-02 7:10:00 Yes 2016-01-02 07:10:00 3000000000000 
3 1 2016-01-02 8:00:00 No 2016-01-02 08:00:00    0 
4 2 2016-01-02 7:10:00 Yes 2016-01-02 07:10:00 2400000000000 
5 2 2016-01-02 7:50:00 No 2016-01-02 07:50:00    0 
6 2 2016-01-02 9:00:00 No 2016-01-02 09:00:00    0 
7 2 2016-01-02 9:10:00 No 2016-01-02 09:10:00    0 
8 2 2016-01-02 9:15:00 No 2016-01-02 09:15:00    0 
9 3 2016-01-02 6:05:00 Yes 2016-01-02 06:05:00 300000000000 
10 3 2016-01-02 6:10:00 Yes 2016-01-02 06:10:00 600000000000 
11 3 2016-01-02 6:20:00 Yes 2016-01-02 06:20:00 1500000000000 
12 3 2016-01-02 6:45:00 No 2016-01-02 06:45:00    0 

d = pd.to_timedelta(df.groupby('Id')['dif'].mean()).to_dict() 
print (d) 
{1: Timedelta('0 days 00:13:45'), 
2: Timedelta('0 days 00:08:00'), 
3: Timedelta('0 days 00:10:00')} 
+0

Ja, viel mehr sauber und schnell. Kannst du bitte die mul Funktion erklären? Ich schaue mir die Dokumentation an, aber ich kann nicht verstehen, was es tut. https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.mul.html –

+0

Sie können ['Series.mul'] (http://pandas.pydata.org/pandas -docs/stable/generated/pandas.Series.mul.html), es ist einfach ein Vielfaches von 1 wenn "Ja" und von "0" wenn nicht "Ja". – jezrael

Verwandte Themen