¿Pueden los pandas reconocer fechas automáticamente?

151

Hoy me sorprendió positivamente el hecho de que, al leer datos de un archivo de datos (por ejemplo), los pandas pueden reconocer tipos de valores:

df = pandas.read_csv('test.dat', delimiter=r"\s+", names=['col1','col2','col3'])

Por ejemplo, se puede verificar de esta manera:

for i, r in df.iterrows():
    print type(r['col1']), type(r['col2']), type(r['col3'])

En particular, los números flotantes y las cadenas se reconocieron correctamente. Sin embargo, tengo una columna que tiene fechas en el siguiente formato: 2013-6-4. Estas fechas se reconocieron como cadenas (no como objetos de fecha de Python). ¿Hay alguna manera de "aprender" pandas a fechas reconocidas?

romano
fuente
Indique siempre la versión de pandas, para este tipo de pregunta dependiente de la versión. En julio de 2013, esto habría sido v0.11
smci
Y los dtypes son fijos para cada columna, no necesita iterar df.iterrows()y verlos para cada fila, solo hágalo df.info()una vez.
smci

Respuestas:

327

Debe agregar parse_dates=True, o parse_dates=['column name']al leer, eso suele ser suficiente para analizarlo mágicamente. Pero siempre hay formatos extraños que deben definirse manualmente. En tal caso, también puede agregar una función de analizador de fecha, que es la forma más flexible posible.

Supongamos que tiene una columna 'datetime' con su cadena, entonces:

dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)

De esta manera, incluso puede combinar varias columnas en una sola columna de fecha y hora, esto combina una columna de "fecha" y una de "hora" en una sola columna de "fecha y hora":

dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)

Puede encontrar directivas (es decir, las letras que se utilizarán para diferentes formatos) para strptimey strftime en esta página .

Rutger Kassies
fuente
8
No funcionó para mí, recibí el siguiente error:TypeError: strptime() argument 1 must be str, not float
Jean Paul
66
Recibí este error porque había nan en mi marco de datos.
Jean Paul
¿puede agregar un elemento que también contenga el material no analizable o NaN o / Ns? porque parece que este analizador omite totalmente la columna completa si algo así está presente
Amir
Hay una opción infer_datetime_format: "los pandas intentarán inferir el formato de las cadenas de fecha y hora en las columnas". Esto se puede usar en lugar de date_parser.
Winand
1
Tenga en cuenta que si sus fechas están en ISO 8601formato, no debe pasar infer_datetime_formatni una función de analizador: es mucho más lento que dejar que los pandas lo manejen (especialmente el último). El formato de fecha en esta respuesta también se
incluye
20

Quizás la interfaz de pandas ha cambiado desde que respondió @Rutger, pero en la versión que estoy usando (0.15.2), la date_parserfunción recibe una lista de fechas en lugar de un solo valor. En este caso, su código debe actualizarse así:

dateparse = lambda dates: [pd.datetime.strptime(d, '%Y-%m-%d %H:%M:%S') for d in dates]

df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)
Sean
fuente
11

El método pandas read_csv es ideal para analizar fechas. Documentación completa en http://pandas.pydata.org/pandas-docs/stable/generated/pandas.io.parsers.read_csv.html

Incluso puede tener las diferentes partes de fecha en diferentes columnas y pasar el parámetro:

parse_dates : boolean, list of ints or names, list of lists, or dict
If True -> try parsing the index. If [1, 2, 3] -> try parsing columns 1, 2, 3 each as a
separate date column. If [[1, 3]] -> combine columns 1 and 3 and parse as a single date
column. {‘foo : [1, 3]} -> parse columns 1, 3 as date and call result foo

La detección predeterminada de fechas funciona muy bien, pero parece estar sesgada hacia los formatos de fecha norteamericanos. Si vives en otro lugar, los resultados podrían atraparte ocasionalmente. Hasta donde puedo recordar 1/6/2000 significa 6 de enero en los Estados Unidos en lugar de 1 de junio donde vivo. Es lo suficientemente inteligente como para cambiarlos si se usan fechas como 23/6/2000. Sin embargo, probablemente sea más seguro permanecer con variaciones de fecha AAAAMMDD. Disculpas a los desarrolladores de pandas, aquí, pero no lo he probado con fechas locales recientemente.

puede usar el parámetro date_parser para pasar una función para convertir su formato.

date_parser : function
Function to use for converting a sequence of string columns to an array of datetime
instances. The default uses dateutil.parser.parser to do the conversion.
Joop
fuente
2
Puede especificar dayfirstcomo Verdadero para fechas europeas / internacionales. pandas.pydata.org/pandas-docs/stable/generated/…
Will Gordon el
10

Puede usar pandas.to_datetime()como se recomienda en la documentación para pandas.read_csv():

Si una columna o índice contiene una fecha no analizable, la columna o índice completo se devolverá sin modificaciones como un tipo de datos de objeto. Para el análisis de fecha y hora no estándar, use pd.to_datetimeafter pd.read_csv.

Manifestación:

>>> D = {'date': '2013-6-4'}
>>> df = pd.DataFrame(D, index=[0])
>>> df
       date
0  2013-6-4
>>> df.dtypes
date    object
dtype: object
>>> df['date'] = pd.to_datetime(df.date, format='%Y-%m-%d')
>>> df
        date
0 2013-06-04
>>> df.dtypes
date    datetime64[ns]
dtype: object
Eugene Yarmash
fuente
también está convirtiendo otras columnas hasta la fecha, que son del tipo de objeto
ratnesh
10

Al fusionar dos columnas en una sola columna de fecha y hora, la respuesta aceptada genera un error (pandas versión 0.20.3), ya que las columnas se envían a la función date_parser por separado.

Los siguientes trabajos:

def dateparse(d,t):
    dt = d + " " + t
    return pd.datetime.strptime(dt, '%d/%m/%Y %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)
Yo soy la morsa
fuente
1
Estoy usando pandas 0.22 y estoy de acuerdo en que la respuesta aceptada ya no funciona.
Dai
Esto crea un "TypeError: solo puede concatenar str (no" float ") a str" para mí. La columna de fecha es d / m / a y la columna de hora es H: M: 00
IceQueeny el
8

Sí, de acuerdo con la pandas.read_csv documentación :

Nota: Existe una ruta rápida para fechas con formato iso8601 .

Entonces, si su csv tiene una columna llamada datetimey las fechas se ven como, 2013-01-01T01:01por ejemplo, ejecutar esto hará que los pandas (estoy en v0.19.2) recojan la fecha y la hora automáticamente:

df = pd.read_csv('test.csv', parse_dates=['datetime'])

Tenga en cuenta que necesita pasar explícitamente parse_dates, no funciona sin él.

Verificar con:

df.dtypes

Debería ver que el tipo de datos de la columna es datetime64[ns]

Gaurav
fuente
Creo que malinterpretas la pregunta. El usuario tiene curiosidad por saber si la opción podría habilitarse para su formato de cadena.
Arya McCarthy
@AryaMcCarthy umm, básicamente quiere que la fecha se reconozca correctamente, por lo que menciono cómo puede transformar los datos de origen para que los pandas los reconozcan de forma natural. En ninguna parte menciona que no puede cambiar el formato de los datos de origen.
Gaurav
1

Si el rendimiento es importante para usted, asegúrese de tener tiempo:

import sys
import timeit
import pandas as pd

print('Python %s on %s' % (sys.version, sys.platform))
print('Pandas version %s' % pd.__version__)

repeat = 3
numbers = 100

def time(statement, _setup=None):
    print (min(
        timeit.Timer(statement, setup=_setup or setup).repeat(
            repeat, numbers)))

print("Format %m/%d/%y")
setup = """import pandas as pd
import io

data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,07/29/15
x2,07/29/15
x3,07/29/15
x4,07/30/15
x5,07/29/15
x6,07/29/15
x7,07/29/15
y7,08/05/15
x8,08/05/15
z3,08/05/15
''' * 100)"""

time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'date_parser=lambda x: pd.datetime.strptime(x, "%m/%d/%y")); data.seek(0)')

print("Format %Y-%m-%d %H:%M:%S")
setup = """import pandas as pd
import io

data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,2016-10-15 00:00:43
x2,2016-10-15 00:00:56
x3,2016-10-15 00:00:56
x4,2016-10-15 00:00:12
x5,2016-10-15 00:00:34
x6,2016-10-15 00:00:55
x7,2016-10-15 00:00:06
y7,2016-10-15 00:00:01
x8,2016-10-15 00:00:00
z3,2016-10-15 00:00:02
''' * 1000)"""

time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'date_parser=lambda x: pd.datetime.strptime(x, "%Y-%m-%d %H:%M:%S")); data.seek(0)')

huellas dactilares:

Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
[Clang 6.0 (clang-600.0.57)] on darwin
Pandas version 0.23.4
Format %m/%d/%y
0.19123052499999993
8.20691274
8.143124389
1.2384357139999977
Format %Y-%m-%d %H:%M:%S
0.5238807110000039
0.9202787830000005
0.9832778819999959
12.002349824999996

Así que con ISO8601 con formato de fecha ( %Y-%m-%d %H:%M:%Sque aparentemente es una fecha ISO8601-formateado, supongo que la T se puede quitar y sustituir por un espacio) se debe no especificar infer_datetime_format(que no hace una diferencia con los más comunes, ya sea en apariencia) y pasando su propio analizador en solo paraliza el rendimiento. Por otro lado, date_parserhace una diferencia con formatos de día no tan estándar. Asegúrese de tomar el tiempo antes de optimizar, como de costumbre.

Mr_and_Mrs_D
fuente
1

Al cargar el archivo csv contiene la columna de fecha. Tenemos dos enfoques para hacer pandas para reconocer la columna de fecha, es decir

  1. Los pandas reconocen explícitamente el formato por arg date_parser=mydateparser

  2. Pandas implícitamente reconoce el formato por agr infer_datetime_format=True

Algunos de los datos de la columna de fecha

01/01/18

01/02/18

Aquí no sabemos las dos primeras cosas Puede ser mes o día. Entonces, en este caso tenemos que usar el Método 1: - Pase explícitamente el formato

    mydateparser = lambda x: pd.datetime.strptime(x, "%m/%d/%y")
    df = pd.read_csv(file_name, parse_dates=['date_col_name'],
date_parser=mydateparser)

Método 2: - implícito o reconoce automáticamente el formato

df = pd.read_csv(file_name, parse_dates=[date_col_name],infer_datetime_format=True)
kamran kausar
fuente