Agregue una columna con el número de días entre fechas en DataFrame pandas

101

Quiero restar las fechas en 'A' de las fechas en 'B' y agregar una nueva columna con la diferencia.

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

He intentado lo siguiente, pero aparece un error cuando intento incluir esto en un bucle 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

¿Qué tengo que hacer?

Jase Villam
fuente

Respuestas:

100

Suponiendo que estas fueran columnas de fecha y hora (si no se aplican to_datetime), puede restarlas:

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: asegúrese de que está usando un nuevo pandas (por ejemplo, 0.13.1), esto puede no funcionar en versiones anteriores.

Andy Hayden
fuente
24
¿Podemos deshacernos de la porción de "días" en el resultado en caso de que solo necesitemos ver el valor numérico? -58, -26 en este caso.
0nir
6
para expandir el comentario de @AndyHayden, eso funciona pero debería pd.offsets.Day(1)(con una 's'). Normalmente también lo niego, así que obtienes(df['A'] - df['B']) / pd.offsets.Day(-1)
dirkjot
12
Sin embargo, si desea hacer esto en una serie completa, lo necesita (df['A'] - df['B']) / np.timedelta64(-1, 'D')por razones que no entiendo completamente.
dirkjot
@dirkjot ¡Gracias por detectar el error tipográfico! IIRC esto se solucionó en pandas recientes, ¿estás usando 0.16.2 / 0.17?
Andy Hayden
2
@webelo, el DatetimeIndex / Series en sí debería tener un .dt.daysatributo que debería ser muy preferido.
Andy Hayden
109

Para eliminar el elemento de texto 'días', también puede utilizar el acceso dt () para la serie: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.dt.html

Entonces,

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

que devuelve:

             A          B   C
one 2014-01-01 2014-02-28  58
two 2014-02-03 2014-03-01  26
Ricky McMaster
fuente
3
Gran respuesta. En mi caso, df['C'] = (df['B'] - df['A']).dt.daysno funcionó y tuve que usar df['C'] = (df['B'] - df['A']).days. ¿Alguna idea de por qué el mío no dio la cantidad de días que esperaba?
Samuel Nde
Nde - ¿cómo no funcionó exactamente? ¿Error o valores incorrectos? ¿Convirtió correctamente las columnas A y B a fecha y hora?
Ricky McMaster
1
Ambas columnas son de fecha y hora (o datetime64[ns]para ser precisos). Cuando lo hice df['C'] = (df['B'] - df['A']).dt.days, recibí un error de atributo que decía AttributeError: El objeto 'Timedelta' no tiene atributo 'dt' , así que probé df ['C'] = (df ['B'] - df ['A']). días que me dieron la respuesta deseada. (Por supuesto que estoy usando mi propio marco de datos, no el del ejemplo anterior. O podría ser porque también tengo tiempo en mi fecha y no como en 2018-09-24 10:17:18.800277)
Samuel Nde
1
Respuesta perfecta.
user3065757
1
Gran solucion ¡Gracias!
Rodrigo Hjort
11

Una lista de comprensión es su mejor opción para la forma más Pythonic (y más rápida) de hacer esto:

[int(i.days) for i in (df.B - df.A)]
  1. Devolveré el timedelta (por ejemplo, '-58 días')
  2. i.days devolverá este valor como un valor entero largo (por ejemplo, -58L)
  3. int (i.days) le dará el -58 que busca.

Si sus columnas no están en formato de fecha y hora. La sintaxis más corta sería:df.A = pd.to_datetime(df.A)

A.Kot
fuente
1

Qué tal esto:

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