Supposons que j'ai l'ensemble de données suivant avec la date (str) dans la colonne date_str et le nombre de secondes après minuit (int) dans la colonne seconds_past_midnight. Je veux analyser le datetime basé sur la combinaison de ces deux colonnes.

import pandas as pd
import numpy as np

n = 1000000
df = pd.DataFrame({'seconds_past_midnight': np.random.randint(34200, 57601, size=n), 'date_str': ['2015-07-14']*n})

print(df)

          date_str  seconds_past_midnight
0       2015-07-14                  48642
1       2015-07-14                  39170
2       2015-07-14                  43940
3       2015-07-14                  46927
4       2015-07-14                  55376
5       2015-07-14                  35859
6       2015-07-14                  38705
7       2015-07-14                  35932
8       2015-07-14                  36874
9       2015-07-14                  39487
...            ...                    ...
999990  2015-07-14                  54837
999991  2015-07-14                  47146
999992  2015-07-14                  54188
999993  2015-07-14                  54729
999994  2015-07-14                  35574
999995  2015-07-14                  35815
999996  2015-07-14                  38727
999997  2015-07-14                  38374
999998  2015-07-14                  53055
999999  2015-07-14                  43303

[1000000 rows x 2 columns]

print(df.dtypes)

date_str                 object
seconds_past_midnight     int64
dtype: object

La façon la plus simple à laquelle je peux penser est de construire pd.Timedelta en fonction de ce nombre de secondes et de les ajouter à l'objet date, mais c'est essentiellement une boucle de ligne lors de l'utilisation de pd.Timedelta pour effectuer la conversion , ce qui est très lent.

%time df.apply(lambda row: pd.to_datetime(row.date_str) + pd.Timedelta(row.seconds_past_midnight, 's'), axis=1)


CPU times: user 2min 5s, sys: 311 ms, total: 2min 5s
Wall time: 2min 5s

Je me demande donc s'il existe un moyen d'accélérer ce processus? peut-être quelques fonctions vectorisées sur un objet datetime dont je n'étais pas au courant? Je pense qu'une façon d'améliorer légèrement la vitesse est d'utiliser le module multiprocessing, et peut-être que je pourrais m'attendre à un temps 4 à 6 plus rapide sur un PC à 8 cœurs. De plus, comme j'invoque la fonction python dans le apply, cython ou jit n'aide pas dans ce cas?

0
Jianxun Li 19 juil. 2015 à 00:45

4 réponses

Meilleure réponse

pd.to_datetime et pd.to_timedelta sont déjà vectorisés.

In [13]: np.random.seed(1234)

In [14]: df = pd.DataFrame({'seconds_past_midnight': np.random.randint(34200, 57601, size=n), 'date_str': ['2015-07-14']*n})

In [15]: df.head()
Out[15]: 
     date_str  seconds_past_midnight
0  2015-07-14                  35518
1  2015-07-14                  51248
2  2015-07-14                  56721
3  2015-07-14                  57417
4  2015-07-14                  42671

In [16]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000000 entries, 0 to 999999
Data columns (total 2 columns):
date_str                 1000000 non-null object
seconds_past_midnight    1000000 non-null int64
dtypes: int64(1), object(1)
memory usage: 22.9+ MB

In [17]: (pd.to_datetime(df['date_str']) + pd.to_timedelta(df['seconds_past_midnight'],unit='s')).head()
Out[17]: 
0   2015-07-14 09:51:58
1   2015-07-14 14:14:08
2   2015-07-14 15:45:21
3   2015-07-14 15:56:57
4   2015-07-14 11:51:11
dtype: datetime64[ns]

In [18]: %timeit pd.to_datetime(df['date_str']) + pd.to_timedelta(df['seconds_past_midnight'],unit='s')        
10 loops, best of 3: 187 ms per loop

C'est avec le master actuel qui a plusieurs améliorations de performances. En 0.16.2, c'est 2x plus lent.

2
Jeff 18 juil. 2015 à 22:28

Vous pouvez peut-être l'analyser en strptime ("% Y-% m-% d% f"),% f est techniquement micro secondes, vous ne savez pas si cela aide?

0
Jamie Dale 18 juil. 2015 à 22:03

Vous pouvez également effectuer l'ajout avec les NumPy datetime64 et timedelta64:

(np.array(df['date_str'], '<M8[D]') + 
np.array(df['seconds_past_midnight'], dtype='<m8[s]'))

Par exemple,

import pandas as pd
import numpy as np
np.random.seed(1234)

n = 1000000
df = pd.DataFrame({
    'seconds_past_midnight': np.random.randint(34200, 57601, size=n), 
    'date_str': ['2015-07-14']*n})

Y compris l'affectation à un DataFrame:

In [4]: pd.__version__
Out[6]: u'0.16.2+175.g5a9a9da'

In [7]: %timeit df['date'] = np.array(df['date_str'], '<M8[D]')+np.array(df['seconds_past_midnight'], dtype='<m8[s]')
10 loops, best of 3: 94.6 ms per loop

In [8]: %timeit df['date2'] = pd.to_datetime(df['date_str']) + pd.to_timedelta(df['seconds_past_midnight'],unit='s')  
10 loops, best of 3: 188 ms per loop

In [12]: df['date'].equals(df['date2'])
Out[12]: True

(Sans attribution à un DataFrame, la somme datetime64 / timedelta64 renvoie un tableau NumPy, tandis que la somme to_datetime/to_timedelta renvoie une série Pandas, donc la comparaison serait une comparaison de pommes à oranges.)

1
unutbu 19 juil. 2015 à 00:37
dates = pd.to_datetime(df.date_str) + pd.to_timedelta(df.seconds_past_midnight, unit='s')
2
user707650user707650 18 juil. 2015 à 22:20