3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pandasでよくやる処理まとめ2 (データ参照、編集操作)

Last updated at Posted at 2020-04-12

前回に引き続き、今回はデータの参照、編集をする際によく使う処理をまとめてみたいと思います。
※動作検証には、Pandasのバージョン0.25.1を使用しています。

最初に、下記コードを実行してデータフレームのサンプルを作成します。

import pandas as pd

df = pd.DataFrame({
    'name' : ['userA', 'userB', 'userC', 'userD', 'userE'],
    'age' : [20, 25, 41, 33, 22],
    'sex' : ['male', 'female', 'female', 'male', 'male'],
    'price' : 10000
})
name age sex price
0 userA 20 male 10000
1 userB 25 female 10000
2 userC 41 female 10000
3 userD 33 male 10000
4 userE 22 male 10000

特定のカラムを取得する場合

カラム名(列名)で指定すると、対応するカラムのデータが
Seriesの形式で取得することが出来ます。

df['name']
name
0 userA
1 userB
2 userC
3 userD
4 userE

複数のカラムを取得する場合

カラム名(列名)のリストで指定すると、対応するカラム(複数)のデータが
DataFrameの形式で取得することが出来ます。

target_columns_list = ['name', 'age']
df[target_columns_list]
name age
0 userA 20
1 userB 25
2 userC 41
3 userD 33
4 userE 22

新規カラムを追加する場合

新規のカラム名を指定して代入操作を行うと、カラムが追加されます。
※既存のカラム名を指定すると、そのカラムが上書きされます。

df['new'] = df['age'] * 10
name age sex price new
0 userA 20 male 10000 200
1 userB 25 female 10000 250
2 userC 41 female 10000 410
3 userD 33 male 10000 330
4 userE 22 male 10000 220

特定の行データのみ取得する場合

特定の行データのみ取得したい場合は、locプロパティにインデックスを指定すると
指定した行のデータをSeriesの形式で取得することが出来ます。

df.loc[0]
name age sex price
0 userA 20 male 10000

複数行のデータを取得する場合

複数行のデータを取得したい場合は、locプロパティにインデックスのリストを指定すると
指定した行のデータをまとめてDataFrameの形式で取得することが出来ます。

target_index_list = [0, 1, 2]
df.loc[target_index_list]
name age sex price
0 userA 20 male 10000
1 userB 25 female 10000
2 userC 41 female 10000

行とカラムを指定してデータを取得する場合

あまり頻繁に使う操作ではありませんが、locプロパティでインデックスとカラム名を指定すると
指定した要素のみを取得することが出来ます。


df.loc[1, 'sex']
# female

# 上記で取得した要素に対して代入を行うと、更新される
df.loc[1, 'sex'] = 'updated'
name age sex price
0 userA 20 male 10000
1 userB 25 updated 10000
2 userC 41 female 10000
3 userD 33 male 10000
4 userE 22 male 10000

条件を指定して行を抽出する場合

# 例えばageが25以上の行を抽出する場合
df[(df['age'] >= 25)]

# 先頭に~をつけるとNOTになる
df[~(df['age'] >= 25)]

# ANDで行を抽出する場合
df[(df['age'] >= 25) & (df['sex'] == 'female')]

# ORで行を抽出する場合
df[(df['age'] < 25) | (df['age'] > 40)]

以下にANDで行を抽出する場合を例示

name age sex price
1 userB 25 female 10000
2 userC 41 female 10000

groupbyで統計量を計算する場合

# カテゴリー毎に統計量を計算した結果を元のデータフレームに追加したい場合などに便利
# 以下は性別毎に年齢の平均を計算している
# transformに指定する引数を変更すれば、他の統計量も計算出来る
df['average_age_by_sex'] = df.groupby('sex')['age'].transform('mean')

# jupyter notebook などで平均を見たいだけの場合は以下でOK
# df.groupby('sex')['age'].mean()
name age sex price new average_age_by_sex
0 userA 20 male 10000 200 25
1 userB 25 female 10000 250 33
2 userC 41 female 10000 410 33
3 userD 33 male 10000 330 25
4 userE 22 male 10000 220 25

Pandasで一番単純なループ処理を実装する場合

iterrowsを利用すると、1行ずつインデックスとその行のデータがSeriesの形式
で取得することが出来ます。ただし、データフレームの行数が多くなると処理が遅くなる為、データフレームが100行ぐらいで、ちょっとサボりたいときに使うくらいが限度だと思います。

for index, row in df.iterrows():
    print(index)
    print(type(row))

特定のカラムを参照して新規のカラムを作成する場合

表題のようなケースで、かつif文を使いたくなるような時は、iterrowsを利用して1行ずつ処理をしたくなると思いますが、大抵の場合は処理速度の観点から止めた方が良いです。
その場合は、以下のようにapplyメソッドを使うと処理を高速化出来ます。

df['age_boundary'] = df['age'].apply(lambda x: '25才以上' if x >=25 else '25才未満')

# 関数を定義してapplyメソッドに渡しても結果は同じ
# def get_age_boundary(age):
#     if age >= 25:
#         return '25才以上'
#     else:
#         return '25才未満'

# df['age_boundary'] = df['age'].apply(get_age_boundary)

name age sex price age_boundary
0 userA 20 male 10000 25才未満
1 userB 25 female 10000 25才以上
2 userC 41 female 10000 25才以上
3 userD 33 male 10000 25才以上
4 userE 22 male 10000 25才未満

複数のカラムを参照して新規のカラムを作成する場合

新しいカラムを作成する為に、複数のカラムを参照する必要があるケースもあると思います。
その場合は、applyメソッドの引数axis=1を指定することで、行毎に各カラムの値を参照することが出来ます。
※この書き方も処理速度的な意味で言うと出来るだけ避けた方が良いです。

def get_price_with_discount_rate(row):

    age = row['age']
    price = row['price']
    
    discount_rate = 1.0
    
    if age >= 40:
        discount_rate = 0.5
            
    return int(price * discount_rate)

df['price_with_discount_rate'] = df.apply(get_price_with_discount_rate, axis=1)

name age sex price price_with_discount_rate
0 userA 20 male 10000 10000
1 userB 25 female 10000 10000
2 userC 41 female 10000 5000
3 userD 33 male 10000 10000
4 userE 22 male 10000 10000

np.vectorizeを利用する場合

上記の、複数のカラムを参照して新規のカラムを作成するケースでは、applyメソッドの引数axis=1を指定する方法を取りましたが、このやり方は処理速度の面で問題があります。
上記の代案として、私は実務で利用した事が無いのですが、Numpyのvectorizeメソッドの利用も検討して見ると良いかもしれません。

import numpy as np

def get_price_with_discount_rate(age, price):
        
    discount_rate = 1.0
    
    if age >= 40:
        discount_rate = 0.5
            
    return int(price * discount_rate)

vectorized_func=np.vectorize(get_price_with_discount_rate)
df['price_with_discount_rate'] = vectorized_func(df['age'], df['price'])

name age sex price price_with_discount_rate
0 userA 20 male 10000 10000
1 userB 25 female 10000 10000
2 userC 41 female 10000 5000
3 userD 33 male 10000 10000
4 userE 22 male 10000 10000

Series、ndarray、listの相互変換

PandasのSeries、NumPyのndarray、Python標準のlistの相互変換のやり方も
よく忘れるところなので、ここでまとめておきます。
※これ以外のパターンも組み合わせで実現可能です。


import numpy as np

# Series → ndarray
df['name'].values

# ndarray → list
df['name'].values.tolist()

# list → Series
pd.Series([1, 2, 3, 4, 5])

# list → ndarray
np.array([1, 2, 3, 4, 5])


本記事を執筆するにあたり、以下のサイトを参考にさせて頂きました。
http://sinhrks.hatenablog.com/entry/2015/07/11/223124
https://qiita.com/3x8tacorice/items/3cc5399e18a7e3f9db86
https://note.nkmk.me/python-pandas-numpy-conversion/

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?