2015-08-16 5 views
20

Ich habe eine CSV-Datei in einem solchen Formatist pandas.to_datetime langsam für Nicht-Standard-Zeitformat wie ‚2014.12.31‘

timestmp, p 
2014/12/31 00:31:01:9200, 0.7 
2014/12/31 00:31:12:1700, 1.9 
... 

und wenn über pd.read_csv und wandeln die Zeit str zu lesen datetime mit pd.to_datetime, fällt die Leistung dramatisch. Hier ist ein minimales Beispiel.

import re 
import pandas as pd 

d = '2014-12-12 01:02:03.0030' 
c = re.sub('-', '/', d) 

%timeit pd.to_datetime(d) 
%timeit pd.to_datetime(c) 
%timeit pd.to_datetime(c, format="%Y/%m/%d %H:%M:%S.%f") 

und die Leistungen sind:

10000 loops, best of 3: 62.4 µs per loop 
10000 loops, best of 3: 181 µs per loop 
10000 loops, best of 3: 82.9 µs per loop 

so, wie konnte ich die Leistung von pd.to_datetime verbessern, wenn aus einer CSV-Datei zu lesen Datum?

Antwort

25

Dies liegt daran, dass Pandas dateutil.parser.parse zurückfällt die Saiten zum Parsen, wenn sie ein nicht-Standardformat haben oder wenn kein format String geliefert wird (dies ist viel flexibler, aber auch langsamer). Wie Sie oben gezeigt haben, können Sie die Leistung verbessern, indem Sie einen format String an to_datetime senden. Oder eine andere Option ist infer_datetime_format=True


Anscheinend zu verwenden, die infer_datetime_format kann nicht geschlossen werden, wenn es Mikrosekunden sind. Mit einem Beispiel ohne denjenigen, können Sie ein große Geschwindigkeit-up sehen:

In [28]: d = '2014-12-24 01:02:03' 

In [29]: c = re.sub('-', '/', d) 

In [30]: s_c = pd.Series([c]*10000) 

In [31]: %timeit pd.to_datetime(s_c) 
1 loops, best of 3: 1.14 s per loop 

In [32]: %timeit pd.to_datetime(s_c, infer_datetime_format=True) 
10 loops, best of 3: 105 ms per loop 

In [33]: %timeit pd.to_datetime(s_c, format="%Y/%m/%d %H:%M:%S") 
10 loops, best of 3: 99.5 ms per loop 
+0

'infer_datetime_format = True' ist langsamer, die Ergebnisse sind' 1000 loops, best of 3: 908 μs pro Schleife'. Gibt es eine schnellere Möglichkeit, nicht standardmäßige Zeitzeichenfolgen in datetime zu konvertieren? – liubenyuan

+1

Das liegt daran, dass Sie eine einzelne Zeichenfolge zeitlich festlegen. Wenn Sie es für eine größere Serie tun, werden Sie die Beschleunigung sehen. – joris

+1

Und wie gesagt, es gibt * einen schnelleren Weg, den du selbst benutzt hast in der Frage: stelle einen 'format' String zur Verfügung. – joris

3

Oft bin ich kann kein Standarddatumsformat vor der Zeit geben, weil ich einfach nicht wissen, wie jeder Kunde es zu unterbreiten wählen. Die Daten sind unvorhersehbar formatiert und fehlen oft.

In diesen Fällen anstelle pd.to_datetime verwenden, ich habe es effizienter fand meinen eigenen Wrapper dateutil.parser.parse zu schreiben:

import pandas as pd 
from dateutil.parser import parse 
import numpy as np 

def parseDateStr(s): 
    if s != '': 
     try: 
      return np.datetime64(parse(s)) 
     except ValueError: 
      return np.datetime64('NaT') 
    else: return np.datetime64('NaT')    

# Example data: 
someSeries=pd.Series( ['NotADate','','1-APR-16']*10000) 

# Compare times: 
%timeit pd.to_datetime(someSeries, errors='coerce') #1 loop, best of 3: 1.78 s per loop 
%timeit someSeries.apply(parseDateStr)    #1 loop, best of 3: 904 ms per loop 

# The approaches return identical results: 
someSeries.apply(parseDateStr).equals(pd.to_datetime(someSeries, errors='coerce')) # True 

In diesem Fall ist die Laufzeit in zwei Hälften geschnitten wird, aber YMMV.

3

Diese Frage wurde bereits ausreichend beantwortet, aber ich wollte die Ergebnisse einiger Tests hinzufügen, die ich ausgeführt habe, um meinen eigenen Code zu optimieren.

Ich bekam dieses Format von einer API: "Mi Feb 08 17:58:56 +0000 2017".

Mit dem Standard pd.to_datetime(SERIES) mit einer impliziten Konvertierung dauerte es über eine Stunde, um etwa 20 Millionen Zeilen zu verarbeiten (abhängig davon, wieviel freier Speicher ich hatte).

Das heißt, ich drei verschiedene Umwandlungen getestet:

# sample of 250k rows 
dt_series_sample = df['created_at'][:250000] 

%timeit format_datetime_1(dt_series_sample) 
%timeit format_datetime_2(dt_series_sample) 
%timeit format_datetime_3(dt_series_sample) 
%timeit format_datetime_baseline(dt_series_sample) 

1 loop, best of 3: 1.56 s per loop 
1 loop, best of 3: 2.09 s per loop 
1 loop, best of 3: 1.72 s per loop 
1 loop, best of 3: 1min 9s per loop 

Die ersten Testergebnisse in einer erstaunlichen 97,7% Laufzeitreduktion:

# explicit conversion of essential information only -- parse dt str: concat 
def format_datetime_1(dt_series): 

    def get_split_date(strdt): 
     split_date = strdt.split() 
     str_date = split_date[1] + ' ' + split_date[2] + ' ' + split_date[5] + ' ' + split_date[3] 
     return str_date 

    dt_series = pd.to_datetime(dt_series.apply(lambda x: get_split_date(x)), format = '%b %d %Y %H:%M:%S') 

    return dt_series 

# explicit conversion of what datetime considers "essential date representation" -- parse dt str: del then join 
def format_datetime_2(dt_series): 

    def get_split_date(strdt): 
     split_date = strdt.split() 
     del split_date[4] 
     str_date = ' '.join(str(s) for s in split_date) 
     return str_date 

    dt_series = pd.to_datetime(dt_series.apply(lambda x: get_split_date(x)), format = '%c') 

    return dt_series 

# explicit conversion of what datetime considers "essential date representation" -- parse dt str: concat 
def format_datetime_3(dt_series): 

    def get_split_date(strdt): 
     split_date = strdt.split() 
     str_date = split_date[0] + ' ' + split_date[1] + ' ' + split_date[2] + ' ' + split_date[3] + ' ' + split_date[5] 
     return str_date 

    dt_series = pd.to_datetime(dt_series.apply(lambda x: get_split_date(x)), format = '%c') 

    return dt_series 

# implicit conversion 
def format_datetime_baseline(dt_series): 

    return pd.to_datetime(dt_series) 

Das sind die Ergebnisse war!

Etwas überraschend sieht es sogar so aus, als würde selbst die "angemessene Darstellung" länger dauern, wahrscheinlich weil sie halbimplizit ist.

Fazit: je expliziter Sie sind, desto schneller wird es laufen.

+0

Dieser Benchmark ist ziemlich nutzlos ohne Berücksichtigung von 'pd.to_datetime (dt_series, infer_datetime_format = True)' Wenn Ihre Strings alle das gleiche Format haben, kann dies eine enorme Beschleunigung gegenüber dem Standard sein 'infer_datetime_format = False' – C8H10N4O2

+0

Nun, würde ich nicht sagen Sie, es ist nutzlos, aber Sie haben Recht, das wäre eine gute Ergänzung gewesen (ich dachte eigentlich, das war der Standard, der in der Baseline-Funktion behandelt worden wäre). Vielleicht können Sie das als Antwort hinzufügen? – Zach