2017-09-18 7 views
2

J'ai deux DataFrames df et evol comme suit (simplifié pour l'exemple):Python pandas: comment vectoriser cette fonction

In[6]: df 
Out[6]: 
    data year_final year_init 
0 12  2023  2012 
1 34  2034  2015 
2  9  2019  2013 
... 

In[7]: evol 
Out[7]: 
     evolution 
year   
2000 1.474946 
2001 1.473874 
2002 1.079157 
... 
2037 1.463840 
2038 1.980807 
2039 1.726468 

je voudrais utiliser l'opération suivante dans une manière vectorisé (courant pour la mise en œuvre boucle est trop longue quand j'ai Go de données):

for index, row in df.iterrows(): 
    for year in range(row['year_init'], row['year_final']): 
     factor = evol.at[year, 'evolution'] 
     df.at[index, 'data'] += df.at[index, 'data'] * factor 

la complexité vient du fait que la portée de l'année ne sont pas les mêmes sur chaque ligne ... Dans l'exemple ci-dessus ouput serait:

 data year_final year_init 
0  163673  2023  2012 
1 594596046  2034  2015 
2  1277  2019  2013 

(plein evol dataframe pour des fins d'essai :)

 evolution 
year   
2000 1.474946 
2001 1.473874 
2002 1.079157 
2003 1.876762 
2004 1.541348 
2005 1.581923 
2006 1.869508 
2007 1.289033 
2008 1.924791 
2009 1.527834 
2010 1.762448 
2011 1.554491 
2012 1.927348 
2013 1.058588 
2014 1.729124 
2015 1.025824 
2016 1.117728 
2017 1.261009 
2018 1.705705 
2019 1.178354 
2020 1.158688 
2021 1.904780 
2022 1.332230 
2023 1.807508 
2024 1.779713 
2025 1.558423 
2026 1.234135 
2027 1.574954 
2028 1.170016 
2029 1.767164 
2030 1.995633 
2031 1.222417 
2032 1.165851 
2033 1.136498 
2034 1.745103 
2035 1.018893 
2036 1.813705 
2037 1.463840 
2038 1.980807 
2039 1.726468 
+1

pouvez-vous ajouter la sortie de l'échantillon? – Dark

+0

Je viens d'éditer la question – Prikers

+0

ceci est vraiment compliqué à vectoriser de la communauté de pandas juste ainsi ajouté la balise numpy. numba pour la vitesse. – Dark

Répondre

2

Une approche de vectorisation en utilisant uniquement des pandas géants est de faire une jointure cartésienne entre les deux cadres et sous-ensemble . Commencerais comme:

df['dummy'] = 1 
evol['dummy'] = 1 
combined = df.merge(evol, on='dummy') 
# filter date ranges, multiply etc 

Ce sera probablement plus rapide que ce que vous faites, mais la mémoire inefficace et pourrait sauter sur vos données réelles.

Si vous pouvez accepter la dépendance numba, quelque chose comme ça devrait être très rapide - essentiellement une version compilée de ce que vous faites maintenant. Quelque chose de similaire serait également possible dans le cython. Notez que cela nécessite que la base de données evol soit triée et contiguë par année, ce qui pourrait être assoupli avec modification.

import numba 

@numba.njit 
def f(data, year_final, year_init, evol_year, evol_factor): 
    data = data.copy() 
    for i in range(len(data)): 
     year_pos = np.searchsorted(evol_year, year_init[i]) 
     n_years = year_final[i] - year_init[i] 
     for offset in range(n_years): 
      data[i] += data[i] * evol_factor[year_pos + offset]    
    return data 

f(df['data'].values, df['year_final'].values, df['year_init'].values, evol.index.values, evol['evolution'].values) 
Out[24]: array([ 163673, 594596044,  1277], dtype=int64) 

Edit: Certains horaires avec vos données de test

In [25]: %timeit f(df['data'].values, df['year_final'].values, df['year_init'].values, evol.index.values, evol['evolution'].values) 
15.6 µs ± 338 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 


In [26]: %%time 
    ...: for index, row in df.iterrows(): 
    ...:  for year in range(row['year_init'], row['year_final']): 
    ...:   factor = evol.at[year, 'evolution'] 
    ...:   df.at[index, 'data'] += df.at[index, 'data'] * factor 
Wall time: 3 ms 
+0

cela vous dérangerait d'ajouter les horaires, cela aiderait beaucoup à trouver la différence. – Dark

+0

En effet, il semble que pour ce cas, il est préférable d'utiliser numba! Merci pour cela, je n'ai pas été capable de trouver un moyen efficace de le vectoriser autrement ... – Prikers