Adicionar coluna com o número de dias entre as datas no DataFrame pandas

95

Quero subtrair as datas em 'A' das datas em 'B' e adicionar uma nova coluna com a diferença.

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

Tentei o seguinte, mas recebo um erro quando tento incluir isso em um loop 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

O que devo fazer?

Jase Villam
fonte

Respostas:

96

Supondo que essas colunas sejam datetime (se não forem aplicáveis to_datetime), você pode simplesmente subtraí-las:

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

Nota: certifique-se de que está usando um novo dos pandas (por exemplo, 0.13.1), isso pode não funcionar em versões anteriores.

Andy Hayden
fonte
21
Podemos nos livrar da parte "dias" no caso do resultado, apenas precisamos ver o valor numérico, ou seja. -58, -26 neste caso.
0nir
6
para expandir o comentário @AndyHayden, isso funciona, mas deveria pd.offsets.Day(1)(com um 's'). Eu também costumo negar, então você começa(df['A'] - df['B']) / pd.offsets.Day(-1)
dirkjot
11
No entanto, se você quiser fazer isso em uma série inteira, precisará (df['A'] - df['B']) / np.timedelta64(-1, 'D')de motivos que não entendo totalmente.
dirkjot
@dirkjot Obrigado por detectar o erro de digitação! IIRC isso foi corrigido em pandas recentes, você está usando 0.16.2 / 0.17?
Andy Hayden,
Descobri que isso era um pouco problemático quando faltavam dados. Os problemas são que 1) os dados ausentes não têm um .isnull()atributo e 2) eles têm um .dayatributo, mas os dados não perdidos têm um .daysatributo. Então, depois de criar a nova variável, executei um loop em cada observação que verifica: if hasattr(obs,'days')então atribua obs.dayse então atribua np.nan.
webelo
101

Para remover o elemento de texto 'dias', você também pode usar o acessador dt () para a série: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.dt.html

Assim,

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

que retorna:

             A          B   C
one 2014-01-01 2014-02-28  58
two 2014-02-03 2014-03-01  26
Ricky McMaster
fonte
2
Ótima resposta. No meu caso, df['C'] = (df['B'] - df['A']).dt.daysnão funcionou e tive que usar df['C'] = (df['B'] - df['A']).days. Alguma ideia de por que o meu não deu o número de dias conforme o esperado?
Samuel Nde
Nde - como exatamente não funcionou? Erro ou valores errados? Você converteu as colunas A e B em data e hora com êxito?
Ricky McMaster
1
Ambas as minhas colunas são datetime (ou datetime64[ns]para ser mais preciso). Quando o fiz df['C'] = (df['B'] - df['A']).dt.days, recebi um erro de atributo que dizia AttributeError: objeto 'Timedelta' não tem atributo 'dt' , então tentei df ['C'] = (df ['B'] - df ['A']). dias que me deram a resposta desejada. (É claro que estou usando meu próprio dataframe, não o do exemplo acima. Ou poderia ser porque também tenho tempo na minha data e não como em 2018-09-24 10:17:18.800277)
Samuel Nde
1
resposta perfeita.
user3065757
1
Ótima solução. Obrigado!
Rodrigo Hjort
10

A compreensão de uma lista é sua melhor aposta para a maneira mais pitônica (e mais rápida) de fazer isso:

[int(i.days) for i in (df.B - df.A)]
  1. Vou devolver o timedelta (por exemplo, '-58 dias')
  2. i.days retornará este valor como um valor inteiro longo (por exemplo, -58L)
  3. int (i.days) lhe dará o -58 que você procura.

Se suas colunas não estiverem no formato data e hora. A sintaxe mais curta seria:df.A = pd.to_datetime(df.A)

A.Kot
fonte
1

Que tal agora:

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