2013-01-21 11 views
7

mam ogromne pliki, które wyglądają tak:Speed-poprawa na wielkich pand read_csv z indeksem datetime

05/31/2012,15: 30: 00.029,1306.25,1, E, 0,, ​​1306,25

05/31/2012,15: 30: 00.029,1306.25,8, E, 0,, ​​1306,25

mogę łatwo odczytać je stosując następujący:

pd.read_csv(gzip.open("myfile.gz"), header=None,names= 
    ["date","time","price","size","type","zero","empty","last"], parse_dates=[[0,1]]) 

jest jakiś sposób, aby skutecznie przetworzyć daty w ten sposób w znaczniki czasu pandy? Jeśli nie, czy jest jakiś przewodnik do napisania funkcji cython, która może przejść do date_parser =?

Próbowałem napisać własną funkcję analizatora składni i nadal trwa to zbyt długo dla projektu, nad którym pracuję.

+0

Funkcja read_csv spełnia twoje potrzeby analizy, ale działa zbyt wolno? – BKay

+0

Tak, zasadniczo. Jeśli nie ma łatwego rozwiązania, chciałem sprawdzić, czy ktoś może podać wytyczne dotyczące postępowania w cytoncie. –

+0

Dezorientuje mnie, że 'pd.Timestamp' nie działa (działa indywidualnie np. Przy użyciu' pd.Timestamp ('05 /31/2012,15:30:00.029 ') '). Fakt, że tak nie jest, jest najprawdopodobniej błędem. –

Odpowiedz

6

poprawa poprzedniego solution of Michael WS:

  • konwersja do pandas.Timestamp lepiej jest wykonać poza kodem Cython
  • atoi i przetwarzania natywnej c ciągów jest mało nieco szybciej niż funcs pytona
  • liczba datetime -lib połączeniach jest zredukowane do jednego z 2 (+1 sporadyczne DATE)
  • mikrosekund są przetwarzane

UWAGA! Kolejność dat w tym kodzie to dzień/miesiąc/rok.

W sumie kod wydaje się być około 10 razy szybszy niż oryginalny . Jednak jeśli jest to wywoływane po read_csv, to na dysku twardym SSD różnica polega na tym, że całkowity czas to tylko kilka procent z powodu narzutu odczytu. Sądzę, że na zwykłym dysku twardym różnica będzie jeszcze mniejsza.

cimport numpy as np 
import datetime 
import numpy as np 
import pandas as pd 
from libc.stdlib cimport atoi, malloc, free 
from libc.string cimport strcpy 

### Modified code from Michael WS: 
### https://stackoverflow.com/a/15812787/2447082 

def convert_date_fast(np.ndarray date_vec, np.ndarray time_vec): 
    cdef int i, d_year, d_month, d_day, t_hour, t_min, t_sec, t_ms 
    cdef int N = len(date_vec) 
    cdef np.ndarray out_ar = np.empty(N, dtype=np.object) 
    cdef bytes prev_date = <bytes> 'xx/xx/xxxx' 
    cdef char *date_str = <char *> malloc(20) 
    cdef char *time_str = <char *> malloc(20) 

    for i in range(N): 
     if date_vec[i] != prev_date: 
      prev_date = date_vec[i] 
      strcpy(date_str, prev_date) ### xx/xx/xxxx 
      date_str[2] = 0 
      date_str[5] = 0 
      d_year = atoi(date_str+6) 
      d_month = atoi(date_str+3) 
      d_day = atoi(date_str) 

     strcpy(time_str, time_vec[i]) ### xx:xx:xx:xxxxxx 
     time_str[2] = 0 
     time_str[5] = 0 
     time_str[8] = 0 
     t_hour = atoi(time_str) 
     t_min = atoi(time_str+3) 
     t_sec = atoi(time_str+6) 
     t_ms = atoi(time_str+9) 

     out_ar[i] = datetime.datetime(d_year, d_month, d_day, t_hour, t_min, t_sec, t_ms) 
    free(date_str) 
    free(time_str) 
    return pd.to_datetime(out_ar) 
6

mam niesamowite przyspieszenie (50x) z następującego kodu Cython:

połączenia z pytona: timestamps = convert_date_cython (DF [ "Data"] Wartości DF [ "czas"] wartości.).

cimport numpy as np 
import pandas as pd 
import datetime 
import numpy as np 
def convert_date_cython(np.ndarray date_vec, np.ndarray time_vec): 
    cdef int i 
    cdef int N = len(date_vec) 
    cdef out_ar = np.empty(N, dtype=np.object) 
    date = None 
    for i in range(N): 
     if date is None or date_vec[i] != date_vec[i - 1]: 
      dt_ar = map(int, date_vec[i].split("/")) 
      date = datetime.date(dt_ar[2], dt_ar[0], dt_ar[1]) 
     time_ar = map(int, time_vec[i].split(".")[0].split(":")) 
     time = datetime.time(time_ar[0], time_ar[1], time_ar[2]) 
     out_ar[i] = pd.Timestamp(datetime.datetime.combine(date, time)) 
    return out_ar 
2

Liczebność łańcuchów datetime nie jest ogromna. Na przykład liczba łańcuchów czasu w formacie %H-%M-%S to 24 * 60 * 60 = 86400. Jeśli liczba wierszy zbioru danych jest znacznie większa niż ta lub dane zawierają dużo duplikatów sygnatur czasowych, dodanie pamięci podręcznej w procesie analizy może znacznie przyspieszyć działanie.

Dla tych, którzy nie mają Cython dostępne, oto rozwiązanie alternatywne w czystym Pythonie:

import numpy as np 
import pandas as pd 
from datetime import datetime 


def parse_datetime(dt_array, cache=None): 
    if cache is None: 
     cache = {} 
    date_time = np.empty(dt_array.shape[0], dtype=object) 
    for i, (d_str, t_str) in enumerate(dt_array): 
     try: 
      year, month, day = cache[d_str] 
     except KeyError: 
      year, month, day = [int(item) for item in d_str[:10].split('-')] 
      cache[d_str] = year, month, day 
     try: 
      hour, minute, sec = cache[t_str] 
     except KeyError: 
      hour, minute, sec = [int(item) for item in t_str.split(':')] 
      cache[t_str] = hour, minute, sec 
     date_time[i] = datetime(year, month, day, hour, minute, sec) 
    return pd.to_datetime(date_time) 


def read_csv(filename, cache=None): 
    df = pd.read_csv(filename) 
    df['date_time'] = parse_datetime(df.loc[:, ['date', 'time']].values, cache=cache) 
    return df.set_index('date_time') 

z następującymi określonego zestawu danych, przyspieszenie jest 150x +:

$ ls -lh test.csv 
-rw-r--r-- 1 blurrcat blurrcat 1.2M Apr 8 12:06 test.csv 
$ head -n 4 data/test.csv 
user_id,provider,date,time,steps 
5480312b6684e015fc2b12bc,fitbit,2014-11-02 00:00:00,17:47:00,25 
5480312b6684e015fc2b12bc,fitbit,2014-11-02 00:00:00,17:09:00,4 
5480312b6684e015fc2b12bc,fitbit,2014-11-02 00:00:00,19:10:00,67 

W ipython:

In [1]: %timeit pd.read_csv('test.csv', parse_dates=[['date', 'time']]) 
1 loops, best of 3: 10.3 s per loop 
In [2]: %timeit read_csv('test.csv', cache={}) 
1 loops, best of 3: 62.6 ms per loop 

Aby ograniczyć wykorzystanie pamięci, po prostu wymień pamięć podręczną dyktowania na coś jak LRU.

+0

W moim przykładzie wybito jego milisekundę. Jest ogromny 15: 30: 00.029 –

+0

@MichaelWS, więc możesz użyć 1k dodatkowych elementów w pamięci podręcznej, dostaniesz punkt – blurrcat