Conversión entre datetime, Timestamp y datetime64

290

¿Cómo convierto un numpy.datetime64objeto a datetime.datetime(o Timestamp)?

En el siguiente código, creo un objeto datetime, timestamp y datetime64.

import datetime
import numpy as np
import pandas as pd
dt = datetime.datetime(2012, 5, 1)
# A strange way to extract a Timestamp object, there's surely a better way?
ts = pd.DatetimeIndex([dt])[0]
dt64 = np.datetime64(dt)

In [7]: dt
Out[7]: datetime.datetime(2012, 5, 1, 0, 0)

In [8]: ts
Out[8]: <Timestamp: 2012-05-01 00:00:00>

In [9]: dt64
Out[9]: numpy.datetime64('2012-05-01T01:00:00.000000+0100')

Nota: es fácil obtener la fecha y hora de la marca de tiempo:

In [10]: ts.to_datetime()
Out[10]: datetime.datetime(2012, 5, 1, 0, 0)

Pero, ¿cómo extraemos el datetimeo Timestampde un numpy.datetime64( dt64)?

.

Actualización: un ejemplo algo desagradable en mi conjunto de datos (quizás el ejemplo motivador) parece ser:

dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100')

que debería ser datetime.datetime(2002, 6, 28, 1, 0), y no mucho (!) ( 1025222400000000000L) ...

Andy Hayden
fuente
2
probablemente debería aceptar la respuesta de @Wes McKinney que es mucho más corto y debería funcionar en los últimos numpy, pandaslas versiones.
jfs
@JFSebastian Hmmm, eso significa que la respuesta es "no te muevas de np.datetime a datetime" ... solo usa pd.Timestamp (ya que de todos modos es una subclase de datetime), o si realmente debes usarlo pd.Timestamp(dt64).to_datetime(). Todavía estoy un poco insatisfecho con esto, ¡pero ciertamente Wes 'es menos específico para mi antiguo problema (y por lo tanto mejor para el mundo)! Gracias de nuevo por tomarse el tiempo para responder. :)
Andy Hayden
Su pregunta dice "o Timestamp" y Timestampes una datetime(una subclase de) de todos modos :)
jfs
3
Para aquellos que lleguen a esta pregunta en 2017+, mire mi respuesta a continuación para obtener un tutorial detallado de datetime, datetime64 y Timestamps: stackoverflow.com/a/46921593/3707607
Ted Petrou

Respuestas:

132

Para convertir numpy.datetime64a objeto datetime que represente la hora en UTC en numpy-1.8:

>>> from datetime import datetime
>>> import numpy as np
>>> dt = datetime.utcnow()
>>> dt
datetime.datetime(2012, 12, 4, 19, 51, 25, 362455)
>>> dt64 = np.datetime64(dt)
>>> ts = (dt64 - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
>>> ts
1354650685.3624549
>>> datetime.utcfromtimestamp(ts)
datetime.datetime(2012, 12, 4, 19, 51, 25, 362455)
>>> np.__version__
'1.8.0.dev-7b75899'

El ejemplo anterior supone que un objeto datetime ingenuo se interpreta np.datetime64como hora en UTC.


Para convertir datetime a np.datetime64 y back ( numpy-1.6):

>>> np.datetime64(datetime.utcnow()).astype(datetime)
datetime.datetime(2012, 12, 4, 13, 34, 52, 827542)

Funciona tanto en un solo objeto np.datetime64 como en una matriz numpy de np.datetime64.

Piense en np.datetime64 de la misma manera que lo haría con np.int8, np.int16, etc. y aplique los mismos métodos para convertir entre objetos de Python como int, datetime y los objetos numpy correspondientes.

Su "ejemplo desagradable" funciona correctamente:

>>> from datetime import datetime
>>> import numpy 
>>> numpy.datetime64('2002-06-28T01:00:00.000000000+0100').astype(datetime)
datetime.datetime(2002, 6, 28, 0, 0)
>>> numpy.__version__
'1.6.2' # current version available via pip install numpy

Puedo reproducir el longvalor en numpy-1.8.0instalado como:

pip install git+https://github.com/numpy/numpy.git#egg=numpy-dev

El mismo ejemplo:

>>> from datetime import datetime
>>> import numpy
>>> numpy.datetime64('2002-06-28T01:00:00.000000000+0100').astype(datetime)
1025222400000000000L
>>> numpy.__version__
'1.8.0.dev-7b75899'

Devuelve longporque for numpy.datetime64type .astype(datetime)es equivalente a .astype(object)eso devuelve Python integer ( long) on numpy-1.8.

Para obtener el objeto de fecha y hora, podría:

>>> dt64.dtype
dtype('<M8[ns]')
>>> ns = 1e-9 # number of seconds in a nanosecond
>>> datetime.utcfromtimestamp(dt64.astype(int) * ns)
datetime.datetime(2002, 6, 28, 0, 0)

Para obtener datetime64 que usa segundos directamente:

>>> dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100', 's')
>>> dt64.dtype
dtype('<M8[s]')
>>> datetime.utcfromtimestamp(dt64.astype(int))
datetime.datetime(2002, 6, 28, 0, 0)

Los documentos numpy dicen que la API de fecha y hora es experimental y puede cambiar en futuras versiones numpy.

jfs
fuente
1
Me temo que esto no parece funcionar siempre: por ejemplo dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100'), lo que da un largo ( 1025222400000000000L) (!)
Andy Hayden
@hayden: inténtalo type(dt64). dt64.astype(datetime) == datetime.utcfromtimestamp(dt64.astype(int)*1e-6)
jfs
@JFSebastian type(dt64)es numpy.datetime64y dt64.astype(datetime)es el mismo int largo ...: s
Andy Hayden
@hayden: ¿Cuál es tu versión numpy? Mina: numpy.__version__->'1.6.1'
jfs
Versión 1.8.0 (en python 2.7.3), si funciona para usted, ¡sugiere que es un error en mi sistema!
Andy Hayden
212

Simplemente puede usar el constructor pd.Timestamp. El siguiente diagrama puede ser útil para esta y otras preguntas relacionadas.

Conversiones entre representaciones de tiempo

Quant
fuente
2
¡¡¡Agradable!!! (Vale la pena mencionar que la situación ha mejorado desde que escribí esta pregunta, se ha hecho mucho trabajo aquí :))
Andy Hayden
107
Solo mirar este diagrama me dice que hay algo fundamentalmente malo con todo este tiempo.
erizo demente
44
Es muy confuso que pd.to_datetime produzca un TimeStamp si se le da el número de ms o ns, pero produciría un datetime.datetime si se le da un datetime.datetime o un np.datetime64 si se le da un np.datetime64 ... ¿Por qué alguien piensa que esto es razonable?
Mr.WorshipMe
77
@ Mr.WorshipMe Este diagrama debe actualizarse. pd.to_datetimeconvierte todo a pd.Timestamp. Un pd.Timestampobjeto tiene el método to_pydatetimepara volver a un datetime.datetimeobjeto y un to_datetime64método para convertirlo np.datetime64.
Ted Petrou
2
¿Cómo puedo obtener una resolución más alta de esta foto?
user3226167
137

Bienvenido al infierno.

Puede pasar un objeto datetime64 a pandas.Timestamp:

In [16]: Timestamp(numpy.datetime64('2012-05-01T01:00:00.000000'))
Out[16]: <Timestamp: 2012-05-01 01:00:00>

Noté que esto no funciona bien en NumPy 1.6.1:

numpy.datetime64('2012-05-01T01:00:00.000000+0100')

Además, pandas.to_datetimese puede usar (esto está fuera de la versión de desarrollo, no he comprobado v0.9.1)

In [24]: pandas.to_datetime('2012-05-01T01:00:00.000000+0100')
Out[24]: datetime.datetime(2012, 5, 1, 1, 0, tzinfo=tzoffset(None, 3600))
Wes McKinney
fuente
55
Deberías mencionar que issubclass(pd.Timestamp, datetime)es True. Y la Timestampclase misma tiene to_datetime()método.
jfs
77
pd.to_datetime('2012-05-01T01:00:00.000000+0100')regresa Timestamp('2012-05-01 00:00:00')al menos en pandas 0.17.1.
Anton Protopopov
96

Creo que podría haber un esfuerzo más consolidado en una respuesta para explicar mejor la relación entre el módulo datetime de Python, el datetime64 / timedelta64 de numpy y los objetos Timestamp / Timedelta de pandas.

La biblioteca estándar de fecha y hora de Python

La biblioteca estándar de fecha y hora tiene cuatro objetos principales.

  • tiempo: solo tiempo, medido en horas, minutos, segundos y microsegundos
  • fecha: solo año, mes y día
  • datetime: todos los componentes de hora y fecha
  • timedelta: cantidad de tiempo con unidad máxima de días

Crea estos cuatro objetos

>>> import datetime
>>> datetime.time(hour=4, minute=3, second=10, microsecond=7199)
datetime.time(4, 3, 10, 7199)

>>> datetime.date(year=2017, month=10, day=24)
datetime.date(2017, 10, 24)

>>> datetime.datetime(year=2017, month=10, day=24, hour=4, minute=3, second=10, microsecond=7199)
datetime.datetime(2017, 10, 24, 4, 3, 10, 7199)

>>> datetime.timedelta(days=3, minutes = 55)
datetime.timedelta(3, 3300)

>>> # add timedelta to datetime
>>> datetime.timedelta(days=3, minutes = 55) + \
    datetime.datetime(year=2017, month=10, day=24, hour=4, minute=3, second=10, microsecond=7199)
datetime.datetime(2017, 10, 27, 4, 58, 10, 7199)

Objetos datetime64 y timedelta64 de NumPy

NumPy no tiene objetos de fecha y hora separados, solo un objeto datetime64 para representar un solo momento en el tiempo. El objeto datetime del módulo datetime tiene una precisión de microsegundos (una millonésima de segundo). El objeto datetime64 de NumPy le permite establecer su precisión desde horas hasta attosegundos (10 ^ -18). Su constructor es más flexible y puede tomar una variedad de entradas.

Construye los objetos datetime64 y timedelta64 de NumPy

Pase un número entero con una cadena para las unidades. Ver todas las unidades aquí . Se convierte a esa cantidad de unidades después de la época de UNIX: 1 de enero de 1970

>>> np.datetime64(5, 'ns') 
numpy.datetime64('1970-01-01T00:00:00.000000005')

>>> np.datetime64(1508887504, 's')
numpy.datetime64('2017-10-24T23:25:04')

También puede usar cadenas siempre que estén en formato ISO 8601.

>>> np.datetime64('2017-10-24')
numpy.datetime64('2017-10-24')

Timedeltas tienen una sola unidad

>>> np.timedelta64(5, 'D') # 5 days
>>> np.timedelta64(10, 'h') 10 hours

También puede crearlos restando dos objetos datetime64

>>> np.datetime64('2017-10-24T05:30:45.67') - np.datetime64('2017-10-22T12:35:40.123')
numpy.timedelta64(147305547,'ms')

Pandas Timestamp y Timedelta crean mucha más funcionalidad además de NumPy

Una marca de tiempo de pandas es un momento en el tiempo muy similar a una fecha y hora pero con mucha más funcionalidad. Puede construirlos con pd.Timestampo pd.to_datetime.

>>> pd.Timestamp(1239.1238934) #defautls to nanoseconds
Timestamp('1970-01-01 00:00:00.000001239')

>>> pd.Timestamp(1239.1238934, unit='D') # change units
Timestamp('1973-05-24 02:58:24.355200')

>>> pd.Timestamp('2017-10-24 05') # partial strings work
Timestamp('2017-10-24 05:00:00')

pd.to_datetime funciona de manera muy similar (con algunas opciones más) y puede convertir una lista de cadenas en marcas de tiempo.

>>> pd.to_datetime('2017-10-24 05')
Timestamp('2017-10-24 05:00:00')

>>> pd.to_datetime(['2017-1-1', '2017-1-2'])
DatetimeIndex(['2017-01-01', '2017-01-02'], dtype='datetime64[ns]', freq=None)

Convertir Python datetime a datetime64 y Timestamp

>>> dt = datetime.datetime(year=2017, month=10, day=24, hour=4, 
                   minute=3, second=10, microsecond=7199)
>>> np.datetime64(dt)
numpy.datetime64('2017-10-24T04:03:10.007199')

>>> pd.Timestamp(dt) # or pd.to_datetime(dt)
Timestamp('2017-10-24 04:03:10.007199')

Convertir numpy datetime64 a datetime y Timestamp

>>> dt64 = np.datetime64('2017-10-24 05:34:20.123456')
>>> unix_epoch = np.datetime64(0, 's')
>>> one_second = np.timedelta64(1, 's')
>>> seconds_since_epoch = (dt64 - unix_epoch) / one_second
>>> seconds_since_epoch
1508823260.123456

>>> datetime.datetime.utcfromtimestamp(seconds_since_epoch)
>>> datetime.datetime(2017, 10, 24, 5, 34, 20, 123456)

Convertir a marca de tiempo

>>> pd.Timestamp(dt64)
Timestamp('2017-10-24 05:34:20.123456')

Convertir de marca de tiempo a fecha y hora y fecha y hora64

Esto es bastante fácil ya que las marcas de tiempo de los pandas son muy poderosas

>>> ts = pd.Timestamp('2017-10-24 04:24:33.654321')

>>> ts.to_pydatetime()   # Python's datetime
datetime.datetime(2017, 10, 24, 4, 24, 33, 654321)

>>> ts.to_datetime64()
numpy.datetime64('2017-10-24T04:24:33.654321000')
Ted Petrou
fuente
3
Es una locura cuán molesto para datetime todavía es difícil / hacky ... ¿realmente no hay mejor manera? Esta es una buena respuesta, estoy pensando en aceptar moverlo al nivel superior. Tengo que leer a los demás más profundamente una vez por computadora.
Andy Hayden
¿Qué tiene de peculiar? Las marcas de tiempo de Pandas funcionan bien y son bastante simples.
Ted Petrou
2
Numpy a datetime.
Andy Hayden
1
Creo que esta es la mejor respuesta que he visto. Viniendo de Excel, VBA, SAS o SQL, Python parece extraño porque no hay "una sola forma" de trabajar con fechas / horas. Al igual que con muchas cosas en Python o R, parece que uno debe elegir un método / módulo / clase favorito y seguir con él.
Sean McCarthy
Increíble ansewer
gioxc88
29
>>> dt64.tolist()
datetime.datetime(2012, 5, 1, 0, 0)

Para DatetimeIndex, el tolistdevuelve una lista de datetimeobjetos. Para un solo datetime64objeto, devuelve un solo datetimeobjeto.

eumiro
fuente
Realmente debería haber probado todos los métodos :) (Estoy sorprendido por cuánto tiempo estuve lidiando con este) Gracias
Andy Hayden
55
@hayden si sabes que es una matriz escalar / 0-d que preferiría usar, .item()que es mucho más explícita (y nadie puede venir y comenzar a argumentar que debería devolver una lista).
seberg
1
Me temo que esto no parece siempre el trabajo: por ejemplo dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100'), lo que da un largo ( 1025222400000000000L) (!)
Andy Hayden
44
@hayden: el tipo que devuelve .item()(sugerido por @seberg) .tolist()depende de las unidades que use datetime64, por ejemplo, Dproduce datetime.date(), us(microsegundos) produce datetime.datetime(), ns(nanosegundos) produce long. Y las unidades cambian según los valores de entrada, por ejemplo, numpy.datetime64('2012-05-01')usos 'D', numpy.datetime64('2012-05-01T00:00:00.000')usos ms, numpy.datetime64('2012-05-01T00:00:00.000000000')usos ns. Podría abrir un problema si lo encuentra confuso.
jfs
@AndyHayden También puede agregar un argumento adicional, 'us' o 'ms' para garantizar que se aplique el mismo formato, lo que da como resultado que se produzca el mismo elemento de fecha y hora en tolist ()
NM
11

Si desea convertir una serie completa de fechas de pandas a fechas y horas de Python normales, también puede usarlas .to_pydatetime().

pd.date_range('20110101','20110102',freq='H').to_pydatetime()

> [datetime.datetime(2011, 1, 1, 0, 0) datetime.datetime(2011, 1, 1, 1, 0)
   datetime.datetime(2011, 1, 1, 2, 0) datetime.datetime(2011, 1, 1, 3, 0)
   ....

También es compatible con zonas horarias:

pd.date_range('20110101','20110102',freq='H').tz_localize('UTC').tz_convert('Australia/Sydney').to_pydatetime()

[ datetime.datetime(2011, 1, 1, 11, 0, tzinfo=<DstTzInfo 'Australia/Sydney' EST+11:00:00 DST>)
 datetime.datetime(2011, 1, 1, 12, 0, tzinfo=<DstTzInfo 'Australia/Sydney' EST+11:00:00 DST>)
....

NOTA : Si está operando en una Serie Pandas, no puede llamar to_pydatetime()a toda la serie. Deberá llamar .to_pydatetime()a cada fecha y hora individual64 utilizando una lista de comprensión o algo similar:

datetimes = [val.to_pydatetime() for val in df.problem_datetime_column]
fantabolous
fuente
10

Una opción es usar str, y luego to_datetime(o similar):

In [11]: str(dt64)
Out[11]: '2012-05-01T01:00:00.000000+0100'

In [12]: pd.to_datetime(str(dt64))
Out[12]: datetime.datetime(2012, 5, 1, 1, 0, tzinfo=tzoffset(None, 3600))

Nota: no es igual a dtporque se ha vuelto "consciente del desplazamiento" :

In [13]: pd.to_datetime(str(dt64)).replace(tzinfo=None)
Out[13]: datetime.datetime(2012, 5, 1, 1, 0)

Esto parece poco elegante.

.

Actualización: esto puede tratar con el "ejemplo desagradable":

In [21]: dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100')

In [22]: pd.to_datetime(str(dt64)).replace(tzinfo=None)
Out[22]: datetime.datetime(2002, 6, 28, 1, 0)
Andy Hayden
fuente
Gracias Andy por compartir este consejo. Por alguna razón no puedo hacer que funcione, como lo discuto aquí: stackoverflow.com/questions/22825349/…
Amelio Vazquez-Reina
@ user815423426 esta nunca fue una solución muy sólida, supongo que puede pasar un formato al constructor de fecha y hora para que funcione de manera más general. Aunque no muy pandastic!
Andy Hayden
8

Esta publicación ha estado vigente durante 4 años y todavía luché con este problema de conversión, por lo que el problema aún está activo en 2017 en algún sentido. Me sorprendió un poco que la documentación numpy no ofrezca fácilmente un algoritmo de conversión simple, pero esa es otra historia.

He encontrado otra forma de hacer la conversión que solo involucra módulos numpyy datetimeno requiere que se importen pandas, lo que me parece que es mucho código para importar para una conversión tan simple. Noté que datetime64.astype(datetime.datetime)devolverá un datetime.datetimeobjeto si el original datetime64está en unidades de microsegundos, mientras que otras unidades devuelven una marca de tiempo entera. Uso el módulo xarraypara E / S de datos de archivos Netcdf que usa datetime64unidades en nanosegundos, lo que hace que la conversión falle, a menos que primero se convierta a unidades de micro segundos. Aquí está el código de conversión de ejemplo,

import numpy as np
import datetime

def convert_datetime64_to_datetime( usert: np.datetime64 )->datetime.datetime:
    t = np.datetime64( usert, 'us').astype(datetime.datetime)
return t

Solo se probó en mi máquina, que es Python 3.6 con una distribución reciente de Anaconda 2017. Solo he visto la conversión escalar y no he verificado las conversiones basadas en matrices, aunque supongo que será bueno. Tampoco he mirado el código fuente de datetime64 numpy para ver si la operación tiene sentido o no.

ndl303
fuente
Esto es genial. Gracias por hacer esto.
Yu Chen
Buen material. Gracias.
misantroop
1

Volví a esta respuesta más veces de las que puedo contar, así que decidí organizar una pequeña clase rápida, que convierte un datetime64valor de Numpy en un valor de Python datetime. Espero que ayude a otros por ahí.

from datetime import datetime
import pandas as pd

class NumpyConverter(object):
    @classmethod
    def to_datetime(cls, dt64, tzinfo=None):
        """
        Converts a Numpy datetime64 to a Python datetime.
        :param dt64: A Numpy datetime64 variable
        :type dt64: numpy.datetime64
        :param tzinfo: The timezone the date / time value is in
        :type tzinfo: pytz.timezone
        :return: A Python datetime variable
        :rtype: datetime
        """
        ts = pd.to_datetime(dt64)
        if tzinfo is not None:
            return datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, tzinfo=tzinfo)
        return datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second)

Voy a guardar esto en mi bolsa de herramientas, algo me dice que lo necesitaré nuevamente.

MikeyE
fuente
2
Podrías hacerlots.to_pydatetime()
Ted Petrou
0
import numpy as np
import pandas as pd 

def np64toDate(np64):
    return pd.to_datetime(str(np64)).replace(tzinfo=None).to_datetime()

use esta función para obtener el objeto de fecha y hora nativo de pitones

Cristal
fuente
Recibí un error que decíareplace() got an unexpected keyword argument 'tzinfo'
ogogmad
¿Qué versión de pandas usas? Tengo la versión: 0.18.1 (pip show pandas)
Crystal
mismo que usted. . .
ogogmad
No lo sé entonces, pero me funciona como un encanto. pix.toile-libre.org/upload/original/1475645621.png
Crystal
0

Algunas soluciones me funcionan bien, pero numpy dejará en desuso algunos parámetros. La solución que funciona mejor para mí es leer la fecha como una fecha y hora de los pandas y extraer explícitamente el año, mes y día de un objeto de pandas. El siguiente código funciona para la situación más común.

def format_dates(dates):
    dt = pd.to_datetime(dates)
    try: return [datetime.date(x.year, x.month, x.day) for x in dt]    
    except TypeError: return datetime.date(dt.year, dt.month, dt.day)
João Gabriel John
fuente
-1

de hecho, todos estos tipos de fecha y hora pueden ser difíciles y potencialmente problemáticos (debe realizar un seguimiento cuidadoso de la información de la zona horaria). Esto es lo que he hecho, aunque admito que me preocupa que al menos parte "no sea por diseño". Además, esto se puede hacer un poco más compacto según sea necesario. comenzando con un numpy.datetime64 dt_a:

dt_a

numpy.datetime64 ('2015-04-24T23: 11: 26.270000-0700')

dt_a1 = dt_a.tolist () # produce un objeto datetime en UTC, pero sin tzinfo

dt_a1

datetime.datetime (2015, 4, 25, 6, 11, 26, 270000)

# now, make your "aware" datetime:

dt_a2 = datetime.datetime (* list (dt_a1.timetuple () [: 6]) + [dt_a1.microsecond], tzinfo = pytz.timezone ('UTC'))

... y, por supuesto, eso se puede comprimir en una línea según sea necesario.

yoder
fuente
docs.scipy.org/doc/numpy/reference/… para los cambios en el manejo de la zona horaria.
hpaulj
Por favor, editpara cumplir con el correcto: formato de código, formato de cotización y formato de texto. Además, adhiérase a las mayúsculas, la gramática y compruebe los errores tipográficos, según las pautas de SO: consulte: Cómo
publicar