Ajouter une colonne avec le nombre de jours entre les dates dans les pandas DataFrame

95

Je veux soustraire les dates dans «A» des dates dans «B» et ajouter une nouvelle colonne avec la différence.

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

J'ai essayé ce qui suit, mais j'obtiens une erreur lorsque j'essaie de l'inclure dans une boucle for ...

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

Que devrais-je faire?

Jase Villam
la source

Réponses:

96

En supposant qu'il s'agissait de colonnes datetime (si elles ne s'appliquent pas to_datetime), vous pouvez simplement les soustraire:

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

Remarque: assurez-vous que vous utilisez un nouveau de pandas (par exemple 0.13.1), cela peut ne pas fonctionner dans les anciennes versions.

Andy Hayden
la source
21
Pouvons-nous nous débarrasser de la partie "jours" dans le résultat au cas où nous aurions juste besoin de voir la valeur numérique, c'est-à-dire. -58, -26 dans ce cas.
0nir
6
pour développer le commentaire @AndyHayden, ça marche mais ça devrait pd.offsets.Day(1)(avec un 's'). Je le nie aussi généralement, donc vous obtenez(df['A'] - df['B']) / pd.offsets.Day(-1)
dirkjot
11
Cependant, si vous voulez faire cela sur toute une série, vous en avez besoin (df['A'] - df['B']) / np.timedelta64(-1, 'D')pour des raisons que je ne comprends pas entièrement.
dirkjot
@dirkjot Merci d'avoir repéré la faute de frappe! IIRC a été corrigé dans les pandas récents, utilisez-vous 0.16.2 / 0.17?
Andy Hayden
J'ai trouvé que c'était un peu bogué lorsqu'il manquait des données. Le problème est que 1) les données manquantes n'ont pas d' .isnull()attribut et 2) elles ont un .dayattribut mais les données non manquantes ont un .daysattribut. Donc, après avoir créé la nouvelle variable, j'ai exécuté une boucle sur chaque obsréservation qui vérifie: if hasattr(obs,'days')puis assigner obs.dayset sinon assigner np.nan.
webelo
101

Pour supprimer l'élément de texte 'days', vous pouvez également utiliser l'accesseur dt () pour les séries: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.dt.html

Alors,

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

qui renvoie:

             A          B   C
one 2014-01-01 2014-02-28  58
two 2014-02-03 2014-03-01  26
Ricky McMaster
la source
2
Très bonne réponse. Dans mon cas, df['C'] = (df['B'] - df['A']).dt.daysn'a pas fonctionné et j'ai dû utiliser df['C'] = (df['B'] - df['A']).days. Une idée pourquoi la mienne n'a pas donné le nombre de jours comme prévu?
Samuel Nde
Nde - comment cela n'a-t-il pas fonctionné exactement? Erreur ou mauvaises valeurs? Avez-vous réussi à convertir les colonnes A et B en datetime?
Ricky McMaster
1
Mes deux colonnes sont datetime (ou datetime64[ns]pour être précis). Quand je l'ai fait df['C'] = (df['B'] - df['A']).dt.days, j'ai eu une erreur d'attribut qui disait AttributeError: l'objet 'Timedelta' n'a pas d'attribut 'dt' , j'ai donc essayé df ['C'] = (df ['B'] - df ['A']). jours qui m'ont donné la réponse souhaitée. (Bien sûr, j'utilise ma propre trame de données et non celle de l'exemple ci-dessus. Ou est-ce possible parce que j'ai aussi du temps dans ma date et non comme dans 2018-09-24 10:17:18.800277)
Samuel Nde
1
réponse parfaite.
user3065757
1
Excellente solution. Merci!
Rodrigo Hjort le
10

Une compréhension de liste est votre meilleur pari pour le moyen le plus pythonique (et le plus rapide) de le faire:

[int(i.days) for i in (df.B - df.A)]
  1. je retournerai le timedelta (par exemple '-58 jours')
  2. i.days renverra cette valeur sous forme de valeur entière longue (par exemple -58L)
  3. int (i.days) vous donnera le -58 que vous recherchez.

Si vos colonnes ne sont pas au format datetime. La syntaxe la plus courte serait:df.A = pd.to_datetime(df.A)

A.Kot
la source
1

Que dis-tu de ça:

times['days_since'] = max(list(df.index.values))  
times['days_since'] = times['days_since'] - times['months']  
times
À M
la source