PandasのDataFrame内の値を使ってforループを回したい時、通常行ごとの処理ならiterrows関数を、列ごとの処理ならiteritems関数をジェネレーターとして使うことが多いです。

本記事では、

  • DataFrameで行ごとに処理する方法
  • DataFrameで列ごとに処理する方法
  • Seriesで値ごとに処理する方法

について解説します。

DataFrameのイテレーション

行ごとのforループ

行ごとにDataFrameを処理したい場合はiterrows関数を使います。

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'A':[0,1,2,3,4],'B':['a','b','c','d','e']})

In [3]: df
Out[3]:
   A  B
0  0  a
1  1  b
2  2  c
3  3  d
4  4  e

In [6]: for index,item in df.iterrows():
   ...:     print("index : ", index)
   ...:     print("item  :\n", item)
   ...:     print("----------------\n")
   ...:     # 行ごとに出力する
   ...:     
index :  0
item  :
 A    0
B    a
Name: 0, dtype: object
----------------

index :  1
item  :
 A    1
B    b
Name: 1, dtype: object
----------------

index :  2
item  :
 A    2
B    c
Name: 2, dtype: object
----------------

index :  3
item  :
 A    3
B    d
Name: 3, dtype: object
----------------

index :  4
item  :
 A    4
B    e
Name: 4, dtype: object
----------------

行ごとには番号か列ラベルの値でアクセスできます。

In [7]: for index, item in df.iterrows():
   ...:     print("A : ", item['A'])
   ...:     print("B : ", item['B'])
   ...:     print("--------------\n")
   ...:     
A :  0
B :  a
--------------

A :  1
B :  b
--------------

A :  2
B :  c
--------------

A :  3
B :  d
--------------

A :  4
B :  e
--------------

列ごとの値にアクセス

列ごとにアクセスしたい場合はiteritemsを使います。

In [14]: df_t = df.T.copy()

In [15]: df_t
Out[15]:
   0  1  2  3  4
A  0  1  2  3  4
B  a  b  c  d  e
C  0  2  4  6  8

In [16]: for label, items in df_t.iteritems():
    ...:     print("label : ", label)
    ...:     print("items :\n", items)
    ...:     print("-----------\n")
    ...:     
label :  0
items :
 A    0
B    a
C    0
Name: 0, dtype: object
-----------

label :  1
items :
 A    1
B    b
C    2
Name: 1, dtype: object
-----------

label :  2
items :
 A    2
B    c
C    4
Name: 2, dtype: object
-----------

label :  3
items :
 A    3
B    d
C    6
Name: 3, dtype: object
-----------

label :  4
items :
 A    4
B    e
C    8
Name: 4, dtype: object
-----------

値の更新

値の更新する時はloc属性で元のデータを参照する必要があります。

In [8]: df_cp = df.copy()

In [9]: for index, item in df_cp.iterrows(): # atでも可能
   ...:     df_cp.loc[index,'A'] = index*item['B']
   ...:     

In [10]: df_cp
Out[10]:
      A  B
0        a
1     b  b
2    cc  c
3   ddd  d
4  eeee  e

一部の列のみを取り出して処理

列ごとにアクセスするとそれはSeriesを返すのでforループを容易に回せるようになります。 zipを使って複数の値にアクセスしてみます。

In [11]: df['C'] = [0,2,4,6,8]

In [12]: df
Out[12]:
   A  B  C
0  0  a  0
1  1  b  2
2  2  c  4
3  3  d  6
4  4  e  8

In [13]: for a, b in zip(df['A'],df['B']):
    ...:     print("a : ", a)
    ...:     print("b : ", b)
    ...:     
a :  0
b :  a
a :  1
b :  b
a :  2
b :  c
a :  3
b :  d
a :  4
b :  e

Seriesのイテレーション

アイテムごとに処理するforループ

Seriesの場合は特別な関数を使うことなくforループを回すことができます。

In [17]: sr = pd.Series([1,2,3,4,5])

In [18]: for item in sr:
    ...:     print(item)
    ...:     print("---\n")
    ...:     
1
---

2
---

3
---

4
---

5
---

インデックスラベルごとに処理するforループ

インデックスラベルにアクセスしたい時はsr.indexとします。


In [19]: for index in sr.index:
    ...:     print(index)
    ...:     print("----\n")
    ...:     
0
----

1
----

2
----

3
----

4
----

値の更新

値の更新する時はインデックスの値で元のSeriesのデータにアクセスする形になります。 イテレータで返される値は元のデータのコピーとなるため、その値を変更しても元のデータが変更されるわけではありません。

In [24]: sr = pd.Series([1,2,3,4,5])

In [25]: for index in sr.index:
    ...:     sr[index] *= 100
    ...:     

In [26]: sr
Out[26]:
0    100
1    200
2    300
3    400
4    500
dtype: int64

forループを使わない方法

Pandasはforループを使って処理せずにやりたいことができてしまうことが多いです。これは、NumPyをベースにしていることに依る部分が大きいと考えられます。

そのため、forループを使わずに処理を実現できるなら、多くの場合はそちらの方がパフォーマンスがよくなります。

例えば、各々の値を2倍にしたいということであれば、

In [30]: df = df * 2

の1行で足りてしまいます。

また、各々の値ごとに処理をする関数としてDataFrameに対してはapplymap関数があり、Seriesに対してはmap関数を使います。

applymap関数を使って条件に応じた処理をしていきます。 applymapは全ての個々の要素について値を処理してくれる関数です。

In [33]: def function(x):
    ...:     if type(x) == int:
    ...:         if x > 0:
    ...:             return 1
    ...:         else:
    ...:             return 0
    ...:     else:
    ...:         return x * 2
    ...:     

In [39]: df
Out[39]:
   A  B  C
0  0  a  0
1  1  b  2
2  2  c  4
3  3  d  6
4  4  e  8

In [40]: df.applymap(function)
Out[40]:
   A   B  C
0  0  aa  0
1  1  bb  1
2  1  cc  1
3  1  dd  1
4  1  ee  1

複数の列に跨った処理で一括で処理できる場合はこのように処理できます。

例えば、'A'列の値に'C'列の値を足し合わせてみます。

In [42]: df
Out[42]:
   A  B  C
0  0  a  0
1  1  b  2
2  2  c  4
3  3  d  6
4  4  e  8

In [43]: df['A'] += df['C']

In [44]: df
Out[44]:
    A  B  C
0   0  a  0
1   3  b  2
2   6  c  4
3   9  d  6
4  12  e  8

このように、簡単な処理であればforループを使わずに実現できるので最初はforループを使わずに処理を実現できないか考えてみることをオススメします。

applymap関数、map関数の詳しい使い方は以下の記事で解説しています。

Pandasのデータに関数を適用させるapply、applymap、mapの使い方 /features/pandas-apply.html

まとめ

今回はDataFrame、Seriesそれぞれの値についてforループの回し方について扱いました。

複雑な処理でない限り、DataFrame,Series共にforループを使わなくても基本的には実現できますしそちらの方が速いのでforループを使わずに住むのならそれに越したことはないです。

ですがforループの方が直感的でわかりやすいため、最終手段として残しておくと何かと便利ですので使い方を覚えておくと良いと思います。

参考