J'ai un DataFrame de pandas:

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df

Production:

   c1   c2
0  10  100
1  11  110
2  12  120

Maintenant, je veux parcourir les lignes de ce cadre. Pour chaque ligne, je veux pouvoir accéder à ses éléments (valeurs dans les cellules) par le nom des colonnes. Par exemple:

for row in df.rows:
   print row['c1'], row['c2']

Est-il possible de faire cela chez les pandas?

J'ai trouvé cette question similaire . Mais cela ne me donne pas la réponse dont j'ai besoin. Par exemple, il est suggéré d'utiliser ici:

for date, row in df.T.iteritems():

Ou

for row in df.iterrows():

Mais je ne comprends pas ce qu'est l'objet row et comment je peux travailler avec.

1754
Roman 10 mai 2013 à 11:04

18 réponses

Meilleure réponse

DataFrame.iterrows est un générateur qui produit à la fois l'index et la ligne

import pandas as pd
import numpy as np

df = pd.DataFrame([{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}])
for index, row in df.iterrows():
    print(row['c1'], row['c2'])

Output: 
   10 100
   11 110
   12 120
2347
cs95 21 juin 2019 à 03:26

Vous pouvez utiliser la fonction df.iloc comme suit:

for i in range(0, len(df)):
    print df.iloc[i]['c1'], df.iloc[i]['c2']
75
PJay 7 nov. 2016 à 09:09

Pour afficher et modifier des valeurs, j'utiliserais iterrows(). Dans une boucle for et en utilisant le déballage du tuple (voir l'exemple: i, row), j'utilise le row pour afficher uniquement la valeur et j'utilise i avec la méthode loc lorsque Je souhaite modifier des valeurs. Comme indiqué dans les réponses précédentes, vous ne devez pas modifier ici quelque chose que vous êtes en train d'itérer.

for i, row in df.iterrows():
    if row['A'] == 'Old_Value':
        df.loc[i,'A'] = 'New_value'  

Ici, le row dans la boucle est une copie de cette ligne, et non une vue de celle-ci. Par conséquent, vous ne devez PAS écrire quelque chose comme row['A'] = 'New_Value', cela ne modifiera pas le DataFrame. Cependant, vous pouvez utiliser i et loc et spécifier le DataFrame pour effectuer le travail.

3
Hossein 27 févr. 2019 à 00:35

Vous devez utiliser df.iterrows(). Bien que l'itération ligne par ligne ne soit pas particulièrement efficace car des objets Series doivent être créés.

194
cs95 11 déc. 2019 à 18:42

Vous pouvez écrire votre propre itérateur qui implémente namedtuple

from collections import namedtuple

def myiter(d, cols=None):
    if cols is None:
        v = d.values.tolist()
        cols = d.columns.values.tolist()
    else:
        j = [d.columns.get_loc(c) for c in cols]
        v = d.values[:, j].tolist()

    n = namedtuple('MyTuple', cols)

    for line in iter(v):
        yield n(*line)

Ceci est directement comparable à pd.DataFrame.itertuples. Je vise à effectuer la même tâche avec plus d'efficacité.


Pour le dataframe donné avec ma fonction:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

Ou avec pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

Un test complet
Nous testons la disponibilité de toutes les colonnes et le sous-ensemble des colonnes.

def iterfullA(d):
    return list(myiter(d))

def iterfullB(d):
    return list(d.itertuples(index=False))

def itersubA(d):
    return list(myiter(d, ['col3', 'col4', 'col5', 'col6', 'col7']))

def itersubB(d):
    return list(d[['col3', 'col4', 'col5', 'col6', 'col7']].itertuples(index=False))

res = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    columns='iterfullA iterfullB itersubA itersubB'.split(),
    dtype=float
)

for i in res.index:
    d = pd.DataFrame(np.random.randint(10, size=(i, 10))).add_prefix('col')
    for j in res.columns:
        stmt = '{}(d)'.format(j)
        setp = 'from __main__ import d, {}'.format(j)
        res.at[i, j] = timeit(stmt, setp, number=100)

res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);

enter image description here

enter image description here

17
piRSquared 7 nov. 2017 à 04:29

Alors que iterrows() est une bonne option, parfois itertuples() peut être beaucoup plus rapide:

df = pd.DataFrame({'a': randn(1000), 'b': randn(1000),'N': randint(100, 1000, (1000)), 'x': 'x'})

%timeit [row.a * 2 for idx, row in df.iterrows()]
# => 10 loops, best of 3: 50.3 ms per loop

%timeit [row[1] * 2 for row in df.itertuples()]
# => 1000 loops, best of 3: 541 µs per loop
146
e9t 1 juin 2016 à 09:00

Pour boucler toutes les lignes d'un dataframe, vous pouvez utiliser:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
13
Pedro Lobito 4 avril 2017 à 20:46

Pour boucler toutes les lignes dans un dataframe et utiliser les valeurs de chaque ligne facilement , namedtuples peut être converti en ndarray s. Par exemple:

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])

Itération sur les rangées:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

Résulte en:

[ 1.   0.1]
[ 2.   0.2]

Veuillez noter que si index=True, l'index est ajouté en tant que premier élément du tuple , ce qui peut être indésirable pour certaines applications.

6
Herpes Free Engineer 24 avril 2018 à 08:48

Vous pouvez également effectuer l'indexation numpy pour des accélérations encore plus importantes. Ce n'est pas vraiment une itération, mais cela fonctionne beaucoup mieux que l'itération pour certaines applications.

subset = row['c1'][0:5]
all = row['c1'][:]

Vous pouvez également le caster dans un tableau. Ces index / sélections sont censés agir déjà comme des tableaux Numpy mais j'ai rencontré des problèmes et je devais lancer

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) #resize every image in an hdf5 file
1
James L. 1 déc. 2017 à 18:22

Comment parcourir les lignes d'un DataFrame dans Pandas?

Réponse: NE PAS!

L'itération chez les pandas est un anti-modèle, et c'est quelque chose que vous ne devriez faire que lorsque vous avez épuisé toutes les autres options. Vous ne devez utiliser aucune fonction avec "iter" dans son nom pendant plus de quelques milliers de lignes ou vous devrez vous habituer à un lot d'attente.

Voulez-vous imprimer un DataFrame? Utilisez DataFrame.to_string() .

Voulez-vous calculer quelque chose? Dans ce cas, recherchez les méthodes dans cet ordre (liste modifiée depuis ici):

  1. Vectorisation
  2. Routines Cython
  3. Liste des compréhensions (boucle for vanille)
  4. DataFrame.apply() : i) Réductions pouvant être effectuées dans le cython, ii) Itération dans l'espace python
  5. DataFrame.itertuples() et iteritems()
  6. DataFrame.iterrows()

iterrows et itertuples (tous deux recevant de nombreux votes dans les réponses à cette question) devraient être utilisés dans de très rares circonstances, telles que la génération d'objets de ligne / nametuples pour le traitement séquentiel, qui est vraiment la seule chose que ces fonctions sont utile pour.

Appel à l'autorité
La page des documents sur l'itération a une énorme boîte d'avertissement rouge ça dit:

L'itération à travers des objets pandas est généralement lente. Dans de nombreux cas, l'itération manuelle sur les lignes n'est pas nécessaire [...].


Plus rapide que le bouclage: Vectorisation, Cython

Un bon nombre d'opérations et de calculs de base sont "vectorisés" par des pandas (soit via NumPy, soit via des fonctions Cythonisées). Cela comprend l'arithmétique, les comparaisons, (la plupart) des réductions, le remodelage (comme le pivotement), les jointures et les opérations groupées. Parcourez la documentation sur Fonctionnalité de base essentielle pour trouver une méthode vectorisée adaptée à votre problème.

S'il n'en existe pas, n'hésitez pas à écrire le vôtre en utilisant extensions cython.


Meilleure chose suivante: Répertorier les listes

La compréhension des listes devrait être votre prochain port d'escale si 1) il n'y a pas de solution vectorisée disponible, 2) les performances sont importantes, mais pas assez importantes pour passer par les tracas de la cythonisation de votre code, et 3) vous essayez d'effectuer une transformation élémentaire sur votre code. Il existe une bonne quantité de preuves pour suggérer que la liste des compréhensions sont suffisamment rapides (et parfois même plus rapides) pour de nombreuses tâches courantes de pandas.

La formule est simple,

# iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# iterating over multiple columns
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].values]

Si vous pouvez encapsuler votre logique métier dans une fonction, vous pouvez utiliser une compréhension de liste qui l'appelle. Vous pouvez faire fonctionner des choses arbitrairement complexes grâce à la simplicité et la vitesse du python brut.


Un exemple évident

Montrons la différence avec un exemple simple d'ajout de deux colonnes pandas A + B. Il s'agit d'une opération vectorisable, il sera donc facile de comparer les performances des méthodes décrites ci-dessus.

enter image description here

Code de référence, pour votre référence.

Je dois cependant mentionner que ce n'est pas toujours aussi sec et sec. Parfois, la réponse à "quelle est la meilleure méthode pour une opération" est "cela dépend de vos données". Mon conseil est de tester différentes approches sur vos données avant d'en choisir une.


Les références

* Les méthodes de chaîne Pandas sont "vectorisées" dans le sens où elles sont spécifiées sur la série mais fonctionnent sur chaque élément. Les mécanismes sous-jacents sont toujours itératifs, car les opérations de chaîne sont intrinsèquement difficiles à vectoriser.

292
d-cubed 31 déc. 2019 à 16:16

Il existe un moyen de parcourir les lignes tout en obtenant un DataFrame en retour, et non une série. Je ne vois personne mentionner que vous pouvez passer l'index en tant que liste pour la ligne à renvoyer en tant que DataFrame:

for i in range(len(df)):
    row = df.iloc[[i]]

Notez l'utilisation de crochets doubles. Cela renvoie un DataFrame avec une seule ligne.

5
Zeitgeist 17 oct. 2019 à 15:26

Il y a tellement de façons de parcourir les lignes dans la trame de données des pandas. Une manière très simple et intuitive est:

df=pd.DataFrame({'A':[1,2,3], 'B':[4,5,6],'C':[7,8,9]})
print(df)
for i in range(df.shape[0]):
    # For printing the second column
    print(df.iloc[i,1])
    # For printing more than one columns
    print(df.iloc[i,[0,2]])
1
shubham ranjan 19 janv. 2019 à 06:53

Parfois, un modèle utile est:

# Borrowing @KutalmisB df example
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
# The to_dict call results in a list of dicts
# where each row_dict is a dictionary with k:v pairs of columns:value for that row
for row_dict in df.to_dict(orient='records'):
    print(row_dict)

Ce qui se traduit par:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}
6
cs95 13 avril 2019 à 23:06

Certaines bibliothèques (par exemple une bibliothèque Java Interop que j'utilise) nécessitent que les valeurs soient transmises dans une rangée à la fois, par exemple, en cas de streaming de données. Pour reproduire la nature du streaming, je 'stream' mes valeurs de trame de données une par une, j'ai écrit ci-dessous, ce qui est pratique de temps en temps.

class DataFrameReader:
  def __init__(self, df):
    self._df = df
    self._row = None
    self._columns = df.columns.tolist()
    self.reset()
    self.row_index = 0

  def __getattr__(self, key):
    return self.__getitem__(key)

  def read(self) -> bool:
    self._row = next(self._iterator, None)
    self.row_index += 1
    return self._row is not None

  def columns(self):
    return self._columns

  def reset(self) -> None:
    self._iterator = self._df.itertuples()

  def get_index(self):
    return self._row[0]

  def index(self):
    return self._row[0]

  def to_dict(self, columns: List[str] = None):
    return self.row(columns=columns)

  def tolist(self, cols) -> List[object]:
    return [self.__getitem__(c) for c in cols]

  def row(self, columns: List[str] = None) -> Dict[str, object]:
    cols = set(self._columns if columns is None else columns)
    return {c : self.__getitem__(c) for c in self._columns if c in cols}

  def __getitem__(self, key) -> object:
    # the df index of the row is at index 0
    try:
        if type(key) is list:
            ix = [self._columns.index(key) + 1 for k in key]
        else:
            ix = self._columns.index(key) + 1
        return self._row[ix]
    except BaseException as e:
        return None

  def __next__(self) -> 'DataFrameReader':
    if self.read():
        return self
    else:
        raise StopIteration

  def __iter__(self) -> 'DataFrameReader':
    return self

Qui peut être utilisé:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

Et préserve le mappage des valeurs / noms pour les lignes en cours d'itération. Évidemment, c'est beaucoup plus lent que d'utiliser apply et Cython comme indiqué ci-dessus, mais c'est nécessaire dans certaines circonstances.

0
morganics 10 déc. 2019 à 09:36

Je cherchais Comment itérer sur les lignes ET les colonnes et je me suis arrêté ici donc:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)
30
Lucas B 17 janv. 2018 à 09:41

Cet exemple utilise iloc pour isoler chaque chiffre de la trame de données.

import pandas as pd

 a = [1, 2, 3, 4]
 b = [5, 6, 7, 8]

 mjr = pd.DataFrame({'a':a, 'b':b})

 size = mjr.shape

 for i in range(size[0]):
     for j in range(size[1]):
         print(mjr.iloc[i, j])
0
mjr2000 16 mars 2019 à 22:33

Vous pouvez également utiliser df.apply() pour parcourir les lignes et accéder à plusieurs colonnes pour une fonction.

documents: DataFrame.apply ()

def valuation_formula(x, y):
    return x * y * 0.5

df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)
85
cheekybastard 1 juin 2015 à 06:24
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]
14
cs95 7 mai 2019 à 06:37