401 votes

Quelle est la manière la plus efficace de boucler dans les cadres de données avec pandas ?

Je veux effectuer mes propres opérations complexes sur des données financières dans des cadres de données de manière séquentielle.

Par exemple, j'utilise le fichier CSV MSFT suivant, provenant de Yahoo Finance :

Date,Open,High,Low,Close,Volume,Adj Close
2011-10-19,27.37,27.47,27.01,27.13,42880000,27.13
2011-10-18,26.94,27.40,26.80,27.31,52487900,27.31
2011-10-17,27.11,27.42,26.85,26.98,39433400,26.98
2011-10-14,27.31,27.50,27.02,27.27,50947700,27.27

....

Je fais ensuite ce qui suit :

#!/usr/bin/env python
from pandas import *

df = read_csv('table.csv')

for i, row in enumerate(df.values):
    date = df.index[i]
    open, high, low, close, adjclose = row
    #now perform analysis on open/close based on date, etc..

Est-ce le moyen le plus efficace ? Étant donné l'accent mis sur la vitesse dans pandas, je suppose qu'il doit y avoir une fonction spéciale pour itérer à travers les valeurs de manière à récupérer également l'index (éventuellement à travers un générateur pour être efficace en mémoire) ? df.iteritems ne fait malheureusement qu'itérer colonne par colonne.

6 votes

Avez-vous essayé d'écrire une fonction et de la passer à df.apply() ?

0 votes

Si vous voulez une efficacité mémoire, vous devriez envisager d'utiliser des opérations vectorielles (en utilisant des matrices et des vecteurs). Mais je ne connais pas pandas, donc je ne peux pas vous dire, si de telles opérations sont possibles là-bas.

3 votes

Citant unutbu NumPy semble supporter les opérations vectorielles ( The key to speed with NumPy arrays is to perform your operations on the whole array at once ).

424voto

Nick Crawford Points 634

Les versions les plus récentes de pandas comprennent désormais une fonction intégrée permettant d'itérer sur les lignes.

for index, row in df.iterrows():

    # do some logic here

Ou, si vous voulez que ce soit plus rapide, utilisez itertuples()

Mais la suggestion d'unutbu d'utiliser les fonctions numpy pour éviter d'itérer sur les lignes produira le code le plus rapide.

70 votes

Notez que iterrows est très lent (il convertit chaque ligne en série, ce qui peut perturber vos types de données). Lorsque vous avez besoin d'un itérateur, il est préférable d'utiliser itertuples

14 votes

BTW itertuples renvoie des tuples nommés ( docs.python.org/3/library/ ) afin que vous puissiez accéder à chaque colonne par son nom avec row.high ou getattr(row, 'high')

9 votes

Sachez que, selon l'actualité docs : "Vous devriez ne jamais modifier quelque chose sur lequel on itère. Il n'est pas garanti que cela fonctionne dans tous les cas. Selon les types de données, l'itérateur renvoie une copie et non une vue, et écrire dessus n'aura aucun effet."

168voto

unutbu Points 222216

Pandas est basé sur les tableaux NumPy. La clé de la rapidité avec les tableaux NumPy est d'effectuer vos opérations sur l'ensemble du tableau en une seule fois, jamais ligne par ligne ou élément par élément.

Par exemple, si close est un tableau de 1-d, et vous voulez le changement de pourcentage de jour en jour,

pct_change = close[1:]/close[:-1]

Ceci calcule le tableau entier des pourcentages de changement en une seule déclaration, au lieu de

pct_change = []
for row in close:
    pct_change.append(...)

Essayez donc d'éviter la boucle Python for i, row in enumerate(...) entièrement, et réfléchissez à la manière d'effectuer vos calculs avec des opérations sur l'ensemble du tableau (ou du cadre de données) dans son ensemble, plutôt que ligne par ligne.

41 votes

Je suis d'accord que c'est la meilleure façon de procéder et c'est ce que je fais habituellement pour les opérations simples. Cependant, dans ce cas, ce n'est pas possible, car les opérations résultantes peuvent devenir très complexes. Plus précisément, j'essaie de backtester des stratégies de trading. Par exemple, si le prix est à un nouveau bas sur une période de 30 jours, alors nous pourrions vouloir acheter l'action et sortir dès qu'une certaine condition est remplie et cela doit être simulé sur place. Cet exemple simple pourrait encore être réalisé par vectorisation, mais plus une stratégie de trading est complexe, moins il est possible d'utiliser la vectorisation.

3 votes

Vous devrez expliquer plus en détail le calcul exact que vous essayez d'effectuer. Il est utile d'écrire d'abord le code comme vous le pouvez, puis de le profiler et de l'optimiser.

7 votes

À propos, pour certains calculs (notamment ceux qui ne peuvent pas être exprimés sous forme d'opérations sur des tableaux entiers), le code utilisant des listes Python peut être plus rapide que le code équivalent utilisant des tableaux numpy.

75voto

Wes McKinney Points 17545

Vous pouvez parcourir les lignes en boucle en transposant puis en appelant iteritems :

for date, row in df.T.iteritems():
   # do some logic here

Je ne suis pas certain de l'efficacité dans ce cas. Pour obtenir les meilleures performances possibles d'un algorithme itératif, vous pouvez envisager de l'écrire en format Cython donc vous pourriez faire quelque chose comme :

def my_algo(ndarray[object] dates, ndarray[float64_t] open,
            ndarray[float64_t] low, ndarray[float64_t] high,
            ndarray[float64_t] close, ndarray[float64_t] volume):
    cdef:
        Py_ssize_t i, n
        float64_t foo
    n = len(dates)

    for i from 0 <= i < n:
        foo = close[i] - open[i] # will be extremely fast

Je recommanderais d'écrire d'abord l'algorithme en Python pur, de s'assurer qu'il fonctionne et de voir s'il est rapide - si ce n'est pas assez rapide, convertissez les choses en Cython comme ceci avec un travail minimal pour obtenir quelque chose qui est à peu près aussi rapide que du C/C++ codé à la main.

10 votes

Je recommande également Cython ; je travaillais sur un problème similaire pour la construction de mon moteur de backtesting, et j'ai obtenu une accélération de 1 000 fois. J'ai ensuite combiné cela avec la bibliothèque de multiprocessing, qui est une très belle combinaison.

6 votes

Cette réponse doit être mise à jour pour inclure la nouvelle df.iterrows() conformément à la réponse de @NickCrawford.

1 votes

df.T.iteritems() est une excellente solution plutôt que d'utiliser df.iterrows() si vous voulez itérer sur une colonne spécifique +1

26voto

Bird Jaguar IV Points 2062

J'ai vérifié iterrows après avoir constaté Nick Crawford mais j'ai découvert qu'il produit des tuples (index, série). Je ne suis pas sûr de savoir ce qui vous conviendrait le mieux, mais j'ai fini par utiliser la fonction itertuples pour mon problème, qui produit des tuples (index, row_value1...).

Il y a aussi iterkv qui itère sur les tuples (colonne, série).

0 votes

Vous pouvez faire quelque chose comme dict(row) pour créer un ensemble à partir de la ligne avec des colonnes consultables.

4 votes

J'ai également constaté que les itertuples étaient beaucoup plus rapides (10x) dans mon cas d'utilisation car les objets de la série ne sont pas créés.

0 votes

FYI : iterkv déprécié depuis 0.13.1

21voto

Carst Points 512

Un petit complément, vous pouvez également faire une application si vous avez une fonction complexe que vous appliquez à une seule colonne :

http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html

df[b] = df[a].apply(lambda col: do stuff with col here)

1 votes

X est probablement un nom qui prête à confusion pour le nom de la colonne et la variable de la ligne, mais je suis d'accord pour dire qu'appliquer est la manière la plus simple de le faire :)

8 votes

Juste pour ajouter, apply peut également être appliquée à plusieurs colonnes : df['c'] = df[['a','b']].apply(lambda x: do stuff with x[0] and x[1] here, axis=1)

0 votes

Peut-on appliquer une fonction définie ailleurs dans le code ? C'est pour pouvoir introduire une fonction plus compliquée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X