J'ai remarqué de très mauvaises performances lors de l'utilisation d'iterrows de pandas.
Est-ce quelque chose que les autres vivent? Est-ce spécifique à iterrows et cette fonction doit-elle être évitée pour les données d'une certaine taille (je travaille avec 2-3 millions de lignes)?
Cette discussion sur GitHub m'a amené à croire que cela est causé lors du mélange de dtypes dans le dataframe, mais l'exemple simple ci-dessous montre qu'il existe même lorsque vous utilisez un dtype (float64). Cela prend 36 secondes sur ma machine:
import pandas as pd
import numpy as np
import time
s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})
start = time.time()
i=0
for rowindex, row in dfa.iterrows():
i+=1
end = time.time()
print end - start
Pourquoi les opérations vectorisées comme s'appliquent-elles tellement plus rapidement? J'imagine qu'il doit y avoir une itération ligne par ligne là aussi.
Je ne peux pas comprendre comment ne pas utiliser iterrows dans mon cas (je vais le garder pour une question future). Par conséquent, j'apprécierais de savoir si vous avez toujours pu éviter cette itération. Je fais des calculs basés sur des données dans des dataframes séparés. Je vous remercie!
--- Edit: une version simplifiée de ce que je veux exécuter a été ajoutée ci-dessous ---
import pandas as pd
import numpy as np
#%% Create the original tables
t1 = {'letter':['a','b'],
'number1':[50,-10]}
t2 = {'letter':['a','a','b','b'],
'number2':[0.2,0.5,0.1,0.4]}
table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)
#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])
#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():
t2info = table2[table2.letter == row['letter']].reset_index()
table3.ix[row_index,] = optimize(t2info,row['number1'])
#%% Define optimization
def optimize(t2info, t1info):
calculation = []
for index, r in t2info.iterrows():
calculation.append(r['number2']*t1info)
maxrow = calculation.index(max(calculation))
return t2info.ix[maxrow]
la source
apply
n'est PAS vectorisé.iterrows
c'est encore pire car il boxe tout (avec qui 'la perf diffèreapply
). Vous ne devriez l'utiliser queiterrows
dans très très peu de situations. IMHO jamais. Montrez ce que vous faites réellementiterrows
.DatetimeIndex
intoTimestamps
(a été implémenté dans l'espace python), et cela a été beaucoup amélioré dans master.Réponses:
Généralement,
iterrows
ne doit être utilisé que dans des cas très, très spécifiques. Voici l'ordre de priorité général pour l'exécution de diverses opérations:L'utilisation d'une routine Cython personnalisée est généralement trop compliquée, alors sautons cela pour l'instant.
1) La vectorisation est TOUJOURS, TOUJOURS le premier et le meilleur choix. Cependant, il existe un petit ensemble de cas (impliquant généralement une récurrence) qui ne peuvent pas être vectorisés de manière évidente. De plus, sur une petite échelle
DataFrame
, il peut être plus rapide d'utiliser d'autres méthodes.3) peut
apply
généralement être géré par un itérateur dans l'espace Cython. Ceci est géré en interne par les pandas, bien que cela dépende de ce qui se passe à l'intérieur duapply
expression. Par exemple,df.apply(lambda x: np.sum(x))
sera exécuté assez rapidement, mais bien sûr,df.sum(1)
c'est encore mieux. Cependant, quelque chose commedf.apply(lambda x: x['b'] + 1)
sera exécuté dans l'espace Python, et par conséquent est beaucoup plus lent.4)
itertuples
ne place pas les données dans un fichierSeries
. Il renvoie simplement les données sous forme de tuples.5)
iterrows
BOITE les données dans un fichierSeries
. À moins que vous n'en ayez vraiment besoin, utilisez une autre méthode.6) Mise à jour d'une image vide une seule ligne à la fois. J'ai vu cette méthode trop utilisée. C'est de loin le plus lent. C'est probablement un endroit courant (et raisonnablement rapide pour certaines structures python), mais a
DataFrame
fait un bon nombre de vérifications sur l'indexation, donc ce sera toujours très lent pour mettre à jour une ligne à la fois. Beaucoup mieux pour créer de nouvelles structures etconcat
.la source
itertuples
est plus rapide queapply
:(pd.DataFrame.apply
est souvent plus lent queitertuples
. De plus, il vaut la peine de considérer les compréhensions de listemap
, les mal nommésnp.vectorize
etnumba
(sans ordre particulier) pour les calculs non vectorisables , par exemple voir cette réponse .Les opérations vectorielles dans Numpy et pandas sont beaucoup plus rapides que les opérations scalaires dans vanilla Python pour plusieurs raisons:
Recherche de type amorti : Python est un langage typé dynamiquement, il y a donc une surcharge d'exécution pour chaque élément d'un tableau. Cependant, Numpy (et donc pandas) effectuent des calculs en C (souvent via Cython). Le type du tableau n'est déterminé qu'au début de l'itération; cette économie à elle seule est l'une des plus grandes victoires.
Meilleure mise en cache : l'itération sur un tableau C est compatible avec le cache et donc très rapide. Un pandas DataFrame est une "table orientée colonnes", ce qui signifie que chaque colonne n'est en réalité qu'un tableau. Ainsi, les actions natives que vous pouvez effectuer sur un DataFrame (comme la somme de tous les éléments dans une colonne) vont avoir quelques échecs de cache.
Plus de possibilités de parallélisme : un simple tableau C peut être utilisé via des instructions SIMD. Certaines parties de Numpy activent SIMD, en fonction de votre CPU et du processus d'installation. Les avantages du parallélisme ne seront pas aussi spectaculaires que le typage statique et une meilleure mise en cache, mais ils restent une victoire solide.
Morale de l'histoire: utilisez les opérations vectorielles dans Numpy et pandas. Elles sont plus rapides que les opérations scalaires en Python pour la simple raison que ces opérations sont exactement ce qu'un programmeur C aurait écrit à la main de toute façon. (Sauf que la notion de tableau est beaucoup plus facile à lire que les boucles explicites avec des instructions SIMD intégrées.)
la source
Voici comment résoudre votre problème. Tout cela est vectorisé.
la source
Une autre option consiste à utiliser
to_records()
, qui est plus rapide que les deuxitertuples
etiterrows
.Mais pour votre cas, il y a beaucoup de place pour d'autres types d'améliorations.
Voici ma dernière version optimisée
Test de référence:
Code complet:
La version finale est presque 10 fois plus rapide que le code d'origine. La stratégie est:
groupby
pour éviter la comparaison répétée des valeurs.to_records
d'accéder aux objets numpy.records bruts.la source
Oui, Pandas itertuples () est plus rapide que iterrows (). vous pouvez consulter la documentation: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html
"Pour conserver les dtypes lors de l'itération sur les lignes, il est préférable d'utiliser itertuples () qui retourne des noms nommés des valeurs et qui est généralement plus rapide que iterrows."
la source
Détails dans cette vidéo
Référence
la source