0
1

More than 3 years have passed since last update.

pandas基本操作まとめ

Last updated at Posted at 2021-05-14

0. 本記事の概要

Kaggleのコース
https://www.kaggle.com/learn/pandas
をベースとして,pandasの基本操作を纏めました.
元コースとくらべて,若干順番が前後していたり,省略あるいは追記している部分があります.

1. データの作成,データの読み込み

pandasのデータ→DataFrame,またはSeriesで管理する.
DataFrame:一般のテーブルデータを取り扱う型.
Series:リストを取り扱う型.すなわち,単一の列データのみ保持する.

import pandas as pd

test_data1 = pd.DataFrame({'type': ['A', 'B', 'C', 'A', 'C',], 'columnA': [1, 2, 3, 4, 5], 'columnB': [6, 7, 8, 9, 10]})
test_data2 = pd.Series([1, 2, 3, 4, 5])

"""test_data1
  type  columnA  columnB
0    A        1        6
1    B        2        7
2    C        3        8
3    A        4        9
4    C        5       10
"""

print(test_data1.shape)  # (5, 3)
print(len(test_data1))  # 5

データには数値以外にも,文字列を保持させることができる.

kaggleなどで使う上では,既存のcsvデータを読み込むことが多い.
これはread_csv(データパス)で行う.

csv_data = pd.read_csv("table_data.csv")
csv_data = pd.read_csv("table_data.csv", index_col=0)  # インデックス列がcsvに含まれている場合,指定できる
# 注:インデックスは数字とは限らない(が,多くの場合は数字と思われる).

2. データの選択

DataFrameの列データにアクセスする方法が主に2つある.
1.属性として指定する方法
2.辞書型のラベルとして指定する方法

test_data1.columnA
test_data1['columnA']

"""どちらも以下のデータを返却する
0    1
1    2
2    3
3    4
4    5
"""

また,各行にアクセスするには,通常のリストと同様にインデックスを指定する.

test_data1['columnA'][0]  # 1が返却される

一方で,pandasには独自のアクセス方法が2つほど用意されている.
1つ目はilocで,行と列を数値で指定する方法.
2つ目はlocで,行と列をラベルで指定する方法.

# 以下の2つはどちらも同じ場所を指している
test_data1.iloc[0, 1]  # 1が返却される
test_data1.loc[0, 'columnA']  # 1が返却される

注意すべきはlocの書き方.locでは「ラベルを指定する」という動作の関係上,スライスの定義が通常と異なる.すなわち,loc[0:3]と書いた場合,0行目のデータから3行目のデータまで,計4行が返却される.一方,通常のpythonのスライスでは末尾のインデックスを含まないため,[0:3]と書いた場合は0から2までのデータが返却される(ilocはこちらの方式).この違いに注意する.

test_data1.loc[0:3]  # インデックス0から3までが返却される!!
test_data1.iloc[0:3]  # インデックス0から2までが返却される

3. 条件による抽出・データ同士の演算

pandasのデータはpython(やnumpy)のリストと同じような演算が可能.
例えば,

test_data1.type == 'A'

"""
0     True
1    False
2    False
3     True
4    False
"""

と書くと,各行がこの条件を満たすかどうかのブーリアンが返却される.これを利用すると,例えば,

test_data1[test_data1.type == 'A']

"""
  type  columnA  columnB  columnC  columnD
0    A        1        6        0        7
3    A        4        9        0       13
"""

とすることで,typeがAであるデータのみを抽出できる.
複数の条件を組み合わせるには"&(and)"や"|(or)"を使う.

また,各列同士を足し合わせるなども可能.

test_data1.columnA + test_data1.columnB

"""
0     7
1     9
2    11
3    13
4    15
"""

4. 列データの追加

既存のDataFrameに新たな列を追加する場合,以下のように書く.

test_data1['columnC'] = 0

"""
  type  columnA  columnB  columnC
0    A        1        6        0
1    B        2        7        0
2    C        3        8        0
3    A        4        9        0
4    C        5       10        0
"""

これでcolumnCが追加され,その全ての値が0で初期化される.
イテレータで初期化することも可能.

3.の演算と組み合わせることで,既存のデータから新たなデータ列を作ることができる.
(例えば,質量と体積というデータから,質量÷体積=密度 を新たに作る,など)

test_data1['columnD'] = test_data1.columnA + test_data1.columnB

"""
  type  columnA  columnB  columnC  columnD
0    A        1        6        0        7
1    B        2        7        0        9
2    C        3        8        0       11
3    A        4        9        0       13
4    C        5       10        0       15
"""

5. データ統計

あるデータについて平均や標準偏差を求めたい.
その場合,describe()を使う.

test_data1.columnA.describe()

"""
count    5.000000
mean     3.000000
std      1.581139
min      1.000000
25%      2.000000
50%      3.000000
75%      4.000000
max      5.000000
Name: columnA, dtype: float64
"""

データ数,平均,標準偏差,最小値,各パーセンタイル値,最大値が表示された.
describe()は対象とするデータの種類によって異なるサマリーを出力する.

test_data1.type.describe()  # 文字列の場合

"""
count     5
unique    3
top       C
freq      2
Name: type, dtype: object
"""

特定の統計値を指定して出力させることもできる.

test_data1.columnA.mean()  # 平均値が出力される
test_data1.type.unique()  # ユニークな要素がリストとして出力される
test_data1.type.value_counts()  # ユニークな要素と,その頻度が出力される

6. 関数によるデータ処理

3.では列同士の演算を行った.pandasでは更に,自作の関数を各データに適用することができる.
これには3通りの方法がある.
・map()を使う方法:ある列のデータ(Series)に対して操作を行う.

columnA_max = test_data1.columnA.max()
columnA_min = test_data1.columnA.min()
mapped = test_data1.columnA.map(lambda x: (x - columnA_min) / (columnA_max - columnA_min))

"""
0    0.00
1    0.25
2    0.50
3    0.75
4    1.00
"""

この場合,mapに渡す関数は単一の引数を受け取り,操作を施したSeriesを返却する.

・apply()を使う方法:各行(列)のデータに対して操作を行う.

def convert_columnD(row):
    row.columnD = row.columnD * row.columnA
    return row

applied = test_data1.apply(convert_columnD, axis='columns')

"""
  type  columnA  columnB  columnC  columnD
0    A        1        6        0        7
1    B        2        7        0       18
2    C        3        8        0       33
3    A        4        9        0       52
4    C        5       10        0       75
"""

この場合,applyに渡す関数は各行(列)のデータを丸ごと受け取り,操作を施したDataFrameを返却する.そのため,上の例のように,複数の列(行)データを使った演算が可能.

・applymap()を使う方法:elementwise(各要素ごと)に操作を行う(See https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.applymap.html).

7. グループ化

例えば,test_data1のデータをtype毎に分析したいとする.
3.のように条件式で指定してもよいが,typeの種類が増えるにつれて煩雑になってしまう.
こういった場合,groupby()という関数でグループ化を行うと便利.

test_data1.groupby('type').columnA.mean()  # 各typeのデータ内で平均を求める

"""
type
A    2.5
B    2.0
C    4.0
"""

groupbyにはグループ化対象のラベルを渡す.これは,リストの形で複数渡すことも可能で,この場合,データはMulti-indexed(階層化されたインデックスを持つデータ)となる.

8. ソート

データをソートするには,sort_valuesを使う.

sorted_data = test_data1.sort_values(by='columnA', ascending=False)  # データAの降順に並び替え

"""
  type  columnA  columnB  columnC  columnD
4    C        5       10        0       15
3    A        4        9        0       13
2    C        3        8        0       11
1    B        2        7        0        9
0    A        1        6        0        7
"""

インデックスで並べ直したい場合はsort_index()を使う.

データのざっくりした傾向を掴むために,各グループをそのデータ数順に表示したい場合がある.
7.と組み合わせて,以下のようにする.

group_sizes_sorted = test_data1.groupby('type').size().sort_values(ascending=False)

"""
type
A    2
C    2
B    1
"""

9. リネーム

ある列(あるいは行)をリネームするには,rename()を使う.
引数には,リネームする対象を渡す.

renamed = test_data1.rename(columns={'type': 'class'})  # 列のリネーム

"""
  class  columnA  columnB  columnC  columnD
0     A        1        6        0        7
1     B        2        7        0        9
2     C        3        8        0       11
3     A        4        9        0       13
4     C        5       10        0       15
"""

行(インデックス)のリネームもできる.

index_renamed = test_data1.rename(index={0: 'first', 1: 'second'})  # 行のリネーム

"""
       type  columnA  columnB  columnC  columnD
first     A        1        6        0        7
second    B        2        7        0        9
2         C        3        8        0       11
3         A        4        9        0       13
4         C        5       10        0       15
"""

ただし,行インデックスそのものをリネームするより,ある列をインデックス列として指定することの方が多いかも.これはset_index()で行う.

type_indexed = test_data1.set_index('type')

"""
      columnA  columnB  columnC  columnD
type                                    
A           1        6        0        7
B           2        7        0        9
C           3        8        0       11
A           4        9        0       13
C           5       10        0       15
"""

実は,pandasのDataFrameでは,行と列「そのもの」にname属性が定義されている.
これを設定するには,rename_axis()を使う.

axis_renamed = test_data1.rename_axis('product', axis='rows').rename_axis('field', axis='columns')

"""
field   type  columnA  columnB  columnC  columnD
product                                         
0          A        1        6        0        7
1          B        2        7        0        9
2          C        3        8        0       11
3          A        4        9        0       13
4          C        5       10        0       15
"""

10. データの結合

別々のテーブルデータを読み込んで,それらを1つのデータに結合したい場合がある.
pandasにはこれを実現する関数がいくつか存在する.

・concat():2つのDataFrameが同じ種類の列を持っている場合など
この場合は純粋に2つのデータをくっつける操作になる.

test_data_one = pd.DataFrame({'type': ['A', 'B', 'C'], 'columnA': [1, 2, 3], 'columnB': [11, 12, 13]})
test_data_another = pd.DataFrame({'type': ['C', 'C', 'B'], 'columnA': [2, 4, 8], 'columnB': [3, 9, 27]})
concated = pd.concat([test_data_one, test_data_another])

"""
  type  columnA  columnB
0    A        1       11
1    B        2       12
2    C        3       13
0    C        2        3
1    C        4        9
2    B        8       27
"""

インデックスを0からふり直すには,ignore_indexオプションをTrueにする.
(参考:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html)

concated = pd.concat([test_data_one, test_data_another], ignore_index=True)

"""
  type  columnA  columnB
0    A        1       11
1    B        2       12
2    C        3       13
3    C        2        3
4    C        4        9
5    B        8       27
"""

また,列方向に結合する(=データ数は同じで,各データに追加の列データを加える)ことも可能.

・join():ある共通のインデックスに従って結合したい場合など
(説明が難しいが,)例えば,同じ日に別々の地域で観測した天候を纏めたい場合を想定する.
データを日付毎に纏めたいが,地域ごとで区別できるようにしたい.

weather_region_A = pd.DataFrame({'date': ['01', '02', '03'], 'weather': ['cloudy', 'sunny', 'sunny']})
weather_region_B = pd.DataFrame({'date': ['01', '02', '03'], 'weather': ['sunny', 'rainy', 'rainy']})

この場合,まず,
・set_index()によって,日付をインデックス列に指定する.

data_left = weather_region_A.set_index('date')
data_right = weather_region_B.set_index('date')

・join()によって,データを日付のインデックス毎に結合する.
なお,join()はDataFrameの関数なので,data_left.join()という風に書く.
また,suffix(接尾辞)を指定することができる.

data_joined = data_left.join(data_right, lsuffix='_A', rsuffix='_B')

"""
     weather_A weather_B
date                    
01      cloudy     sunny
02       sunny     rainy
03       sunny     rainy
"""

11. データ型の確認・変換

データ型はdtypes属性で確認可能.

test_data1.dtypes
# test_data1.columnD.dtypes  # 各列に対しても動作する

"""
type       object
columnA     int64
columnB     int64
columnC     int64
columnD     int64
"""

ただし,文字列から成るデータはobject型と見なされる.
データの変換はastype(変換後のデータ型)で行う.

converted_columnD = test_data1.columnD.astype('float64')

"""
0     7.0
1     9.0
2    11.0
3    13.0
4    15.0
Name: columnD, dtype: float64
"""

12. 欠損値の検索・データの置き換え

テーブルデータに欠損値(データが存在しない,NaNで表される)がある場合を考える.

"""test_data_missing
  type  columnA  columnB
0    A      1.0      6.0
1    B      2.0      7.0
2    C      NaN      8.0
3    A      NaN      9.0
4    C      5.0      NaN
"""

特定の列にNaNを含むデータの抽出は以下のように行う.

test_data_missing[pd.isnull(test_data_missing.columnA)]

"""
  type  columnA  columnB
2    C      NaN      8.0
3    A      NaN      9.0
"""

pd.isnull(検索する列のラベル)は各行の該当列がNaNかどうかのブーリアンを返却する.
これによって抽出を行っている.

NaNをあるデータで置き換える(埋める)場合,fillna(置き換え後のデータ)を使う.

filled_columnA = test_data_missing.columnA.fillna('Unknown')

"""
0        1.0
1        2.0
2    Unknown
3    Unknown
4        5.0
"""

また,fillnaと同じような動作で,特定のデータを別のデータに置き換えたい場合,replace(置き換え対象,置き換え後データ)を使う.

replaced_type = test_data_missing.type.replace('A', 'AAA')

"""
0    AAA
1      B
2      C
3    AAA
4      C
"""
0
1
1

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
0
1