Fügen Sie eine Spalte mit der Anzahl der Tage zwischen den Daten in DataFrame-Pandas hinzu

95

Ich möchte Daten in 'A' von Daten in 'B' subtrahieren und eine neue Spalte mit der Differenz hinzufügen.

df
          A        B
one 2014-01-01  2014-02-28 
two 2014-02-03  2014-03-01

Ich habe Folgendes versucht, erhalte jedoch eine Fehlermeldung, wenn ich versuche, dies in eine for-Schleife aufzunehmen ...

import datetime
date1=df['A'][0]
date2=df['B'][0]
mdate1 = datetime.datetime.strptime(date1, "%Y-%m-%d").date()
rdate1 = datetime.datetime.strptime(date2, "%Y-%m-%d").date()
delta =  (mdate1 - rdate1).days
print delta

Was soll ich machen?

Jase Villam
quelle

Antworten:

93

Angenommen, es handelt sich um Datetime-Spalten (falls diese nicht zutreffen to_datetime), können Sie sie einfach subtrahieren:

df['A'] = pd.to_datetime(df['A'])
df['B'] = pd.to_datetime(df['B'])

In [11]: df.dtypes  # if already datetime64 you don't need to use to_datetime
Out[11]:
A    datetime64[ns]
B    datetime64[ns]
dtype: object

In [12]: df['A'] - df['B']
Out[12]:
one   -58 days
two   -26 days
dtype: timedelta64[ns]

In [13]: df['C'] = df['A'] - df['B']

In [14]: df
Out[14]:
             A          B        C
one 2014-01-01 2014-02-28 -58 days
two 2014-02-03 2014-03-01 -26 days

Hinweis: Stellen Sie sicher, dass Sie ein neues Pandas verwenden (z. B. 0.13.1). Dies funktioniert möglicherweise nicht in älteren Versionen.

Andy Hayden
quelle
21
Können wir den "Tage" -Teil im Ergebnis loswerden, wenn wir nur den numerischen Wert sehen müssen, dh. -58, -26 in diesem Fall.
0nir
6
@AndyHayden Kommentar zu erweitern, das funktioniert, aber es sollte pd.offsets.Day(1)(mit einem 's'). Ich (df['A'] - df['B']) / pd.offsets.Day(-1)
negiere
11
Wenn Sie dies jedoch für eine ganze Serie tun möchten, benötigen Sie (df['A'] - df['B']) / np.timedelta64(-1, 'D')Gründe, die ich nicht vollständig verstehe.
Dirkjot
@dirkjot Danke, dass du den Tippfehler entdeckt hast! IIRC Dies wurde in den letzten Pandas behoben. Verwenden Sie 0.16.2 / 0.17?
Andy Hayden
Ich fand, dass dies ein bisschen fehlerhaft war, wenn Daten fehlten. Die Probleme sind, dass 1) die fehlenden Daten kein .isnull()Attribut haben und 2) sie ein .dayAttribut haben, aber die nicht fehlenden Daten ein .daysAttribut haben. Nachdem ich die neue Variable erstellt hatte, führte ich eine Schleife über jede obsErvation durch, die prüfte: if hasattr(obs,'days')dann zuweisen obs.daysund sonst zuweisen np.nan.
Webelo
100

Um das Textelement 'Tage' zu entfernen, können Sie auch den Accessor dt () für Serien verwenden: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.dt.html

So,

df[['A','B']] = df[['A','B']].apply(pd.to_datetime) #if conversion required
df['C'] = (df['B'] - df['A']).dt.days

was zurückgibt:

             A          B   C
one 2014-01-01 2014-02-28  58
two 2014-02-03 2014-03-01  26
Ricky McMaster
quelle
2
Gute Antwort. In meinem Fall df['C'] = (df['B'] - df['A']).dt.dayshat nicht funktioniert und ich musste verwenden df['C'] = (df['B'] - df['A']).days. Irgendeine Idee, warum meine nicht die erwartete Anzahl von Tagen angegeben hat?
Samuel Nde
Nde - wie genau hat es nicht funktioniert? Fehler oder falsche Werte? Haben Sie sowohl A- als auch B-Spalten erfolgreich in datetime konvertiert?
Ricky McMaster
1
Meine beiden Spalten sind datetime (oder datetime64[ns]genauer gesagt). Als ich das tat df['C'] = (df['B'] - df['A']).dt.days, bekam ich einen Attributfehler mit dem Attribut AttributeError: 'Timedelta'-Objekt hat kein Attribut' dt ' , also habe ich df [' C '] = (df [' B '] - df [' A ']) versucht . Tage, die mir die gewünschte Antwort gaben. (Natürlich verwende ich meinen eigenen Datenrahmen, nicht den im obigen Beispiel. Oder könnte es sein, dass ich auch Zeit in meinem Datum habe und nicht wie in 2018-09-24 10:17:18.800277)
Samuel Nde
1
perfekte Antwort.
user3065757
1
Tolle Lösung. Vielen Dank!
Rodrigo Hjort
10

Ein Listenverständnis ist die beste Wahl für den pythonischsten (und schnellsten) Weg, dies zu tun:

[int(i.days) for i in (df.B - df.A)]
  1. Ich werde das Zeitdelta zurückgeben (zB '-58 Tage')
  2. i.days gibt diesen Wert als langen ganzzahligen Wert zurück (z. B. -58L).
  3. int (i.days) gibt Ihnen die -58, die Sie suchen.

Wenn Ihre Spalten nicht im Datum / Uhrzeit-Format vorliegen. Die kürzere Syntax wäre:df.A = pd.to_datetime(df.A)

A. Kot
quelle
1

Wie wäre es damit:

times['days_since'] = max(list(df.index.values))  
times['days_since'] = times['days_since'] - times['months']  
times
Tom
quelle