web-dev-qa-db-fra.com

Pandas: conversion lente de la date

Je lis une énorme CSV avec un champ de date au format YYYYMMDD et j'utilise le lambda suivant pour le convertir en lisant:

import pandas as pd

df = pd.read_csv(filen,
                 index_col=None,
                 header=None,
                 parse_dates=[0],
                 date_parser=lambda t:pd.to_datetime(str(t),
                                            format='%Y%m%d', coerce=True))

Cette fonction est cependant très lente.

Une suggestion pour l'améliorer?

16
ppaulojr

Essayez d’utiliser cette fonction pour analyser les dates:

def lookup(s):
    """
    This is an extremely fast approach to datetime parsing.
    For large data, the same dates are often repeated. Rather than
    re-parse these, we store all unique dates, parse them, and
    use a lookup to convert all dates.
    """
    dates = {date:pd.to_datetime(date) for date in s.unique()}
    return s.map(dates)

Utilisez-le comme:

df['date-column'] = lookup(df['date-column'])

Benchmarks:

$ python date-parse.py
to_datetime: 5799 ms
dateutil:    5162 ms
strptime:    1651 ms
manual:       242 ms
lookup:        32 ms

Source: https://github.com/sanand0/benchmarks/tree/master/date-parse

50
fixxxer

Excellente suggestion @ EdChum! Comme @EdChum le suggère, utiliser infer_datetime_format=True peut être significativement plus rapide. Voici mon exemple.

J'ai un fichier de données de température d'un journal de capteur, qui ressemble à ceci:

RecNum,Date,LocationID,Unused
1,11/7/2013 20:53:01,13.60,"117","1",
2,11/7/2013 21:08:01,13.60,"117","1",
3,11/7/2013 21:23:01,13.60,"117","1",
4,11/7/2013 21:38:01,13.60,"117","1",
...

Mon code lit le csv et analyse la date (parse_dates=['Date']). Avec infer_datetime_format=False, cela prend 8min 8sec :

Tue Jan 24 12:18:27 2017 - Loading the Temperature data file.
Tue Jan 24 12:18:27 2017 - Temperature file is 88.172 MB.
Tue Jan 24 12:18:27 2017 - Loading into memory. Please be patient.
Tue Jan 24 12:26:35 2017 - Success: loaded 2,169,903 records.

Avec infer_datetime_format=True, il faut 13sec :

Tue Jan 24 13:19:58 2017 - Loading the Temperature data file.
Tue Jan 24 13:19:58 2017 - Temperature file is 88.172 MB.
Tue Jan 24 13:19:58 2017 - Loading into memory. Please be patient.
Tue Jan 24 13:20:11 2017 - Success: loaded 2,169,903 records.
6
Sam Davey

Analyse de date simplifiée avec mise en cache

La lecture de toutes les données, puis leur conversion seront toujours plus lentes que lors de la lecture du fichier CSV. Puisque vous n'aurez pas besoin de parcourir toutes les données deux fois si vous le faites tout de suite. Vous n'avez pas non plus besoin de le stocker sous forme de chaînes en mémoire.

Nous pouvons définir notre propre analyseur de date qui utilise un cache pour les dates qu'il a déjà vues.

import pandas as pd

cache = {}

def cached_date_parser(s):
    if s in cache:
        return cache[s]
    dt = pd.to_datetime(s, format='%Y%m%d', coerce=True)
    cache[s] = dt
    return dt

df = pd.read_csv(filen,
                 index_col=None,
                 header=None,
                 parse_dates=[0],
                 date_parser=cached_date_parser)

Possède les mêmes avantages que la réponse de @fixxxer en analysant une seule fois chaque chaîne, avec le bonus supplémentaire de ne pas avoir à lire toutes les données et à les analyser ensuite. Vous économise de la mémoire et du temps de traitement.

5
firelynx

Essayez la bibliothèque standard:

import datetime
parser = lambda t: datetime.datetime.strptime(str(t), "%Y%m%d")

Cependant, je ne sais pas vraiment si c'est beaucoup plus rapide que les pandas.

Puisque votre format est si simple, qu'en est-il

def parse(t):
     string_ = str(t)
     return datetime.date(int(string_[:4]), int(string[4:6]), int(string[6:]))

EDITvous dites que vous devez vous occuper des données non valides.

def parse(t):
     string_ = str(t)
     try:
         return datetime.date(int(string_[:4]), int(string[4:6]), int(string[6:]))
     except:
         return default_datetime #you should define that somewhere else

Au total, je suis un peu en conflit sur la validité de votre problème:

  • vous devez être rapide, mais vous obtenez toujours vos données d'un fichier CSV
  • vous devez être rapide, mais toujours traiter des données non valides

C'est un peu contradictoire. Mon approche personnelle ici serait de supposer que votre "énorme" CSV a simplement besoin d'être converti une fois dans un format plus performant, et soit vous ne devez pas vous soucier de la vitesse de ce processus de conversion (car il ne se produit qu'une fois), ou vous devriez probablement apportez tout ce qui produit le fichier CSV pour vous fournir de meilleures données - de nombreux formats ne reposent pas sur l'analyse de chaînes.

2
Marcus Müller

Inutile de spécifier un date_parser, les pandas peuvent l'analyser sans problème, et ce sera beaucoup plus rapide:

In [21]:

import io
import pandas as pd
t="""date,val
20120608,12321
20130608,12321
20140308,12321"""
df = pd.read_csv(io.StringIO(t), parse_dates=[0])
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3 entries, 0 to 2
Data columns (total 2 columns):
date    3 non-null datetime64[ns]
val     3 non-null int64
dtypes: datetime64[ns](1), int64(1)
memory usage: 72.0 bytes
In [22]:

df
Out[22]:
        date    val
0 2012-06-08  12321
1 2013-06-08  12321
2 2014-03-08  12321
1
EdChum

Si votre date/heure a un horodatage UTC et que vous n’avez besoin que d’une partie de celui-ci. Convertissez-le en chaîne, découpez ce dont vous avez besoin, puis appliquez ce qui suit pour un accès beaucoup plus rapide.

created_at
2018-01-31 15:15:08 UTC
2018-01-31 15:16:02 UTC
2018-01-31 15:27:10 UTC
2018-02-01 07:05:55 UTC
2018-02-01 08:50:14 UTC

df["date"]=  df["created_at"].apply(lambda x: str(x)[:10])


df["date"] = pd.to_datetime(df["date"])
1
srikar saggurthi