Python初心者です。
Pandasでのデータフレーム操作に関して、操作説明単体では豊富に記事があるものの、前処理に関しての勘所・目的をあわせて解説された記事がないように感じたので、
学習メモとして作成することにしました。
想定読者
- Python初心者 ∋ わし
- Pandasに触りはじめた方
本稿を読んだら出来るようになること
- pandasライブラリを使ってデータフレームを読みこむ際、最初に何をするべきか、前処理の目的とその具体的手順を両方とも理解できる
- 特に、CSVファイルを読み込んだ後の処理がラクに行えるようになる
前提
- 本論でのコードの記述は、すべて下記を書いた後に行っています。dfは適宜読者様のデータフレームに置き換えてご覧いただきたいです。
- 統計学の入門コンテンツでよく用いられる、タイタニック号の乗客データをイメージしていますが、出てくるデータは本稿作成用のフィクションです。
- データフレームそのものの作り方や読み込み方、行列の編集方法についてはふれていません。後日記事化する予定です。
import pandas as pd
df = pd.read_csv("hogehoge/test.csv", usecols = ['PassengerId','Sex','Age'], header = 1)
本論I | データを概観する
1.目視での確認
- headメソッド、tailメソッドを使って目視でデータの中身を確認する
- columnsメソッド、indexメソッドを用いて、行と列の名前を確認しておく
- 目的:誤ったファイルの読み込みをしていないか、想定通りにデータを読み込めているか確認する
# 先頭 / 末尾の2行を列挙。2には確認したい行数を指定(省略すると6が指定される)
print(df.head(2))
print(df.tail(2))
print("列名:",df.columns)
print("行名(index):"df.index)
"""
↓のように表示される:
# head
PassengerId Sex Age
0 1 female 23.0
1 2 male 48.0
# tail
PassengerId Sex Age
998 999 female 41.0
999 1000 male 15.0
列名:Index(['PassengerId', 'Sex', 'Age'], dtype='object')
行名:RangeIndex(start=0, stop=1000, step=1)
"""
- この結果から、例えば以下のような確認ができる:
- Sexは文字列で格納されているんだな、
- 行名にRangeIndexうんたらと返されたので、行名には通し番号のインデックスが付いているだけだな(特に名前はついていないんだな)、またデータの個数が1000個あるんだな
- RangeIndex(start=0, stop=1000, step=1)は、「0から初めて1ごとに、1000の数値未満でインデックスを付ける」なので、0から999までのインデックスでデータ個数(行数)が1000個です
2.データ型の確認
- dtypesアトリビュートを用いる
- アトリビュート -> メソッドのようにデータフレームの後に
.hoge
とくっつける - 目的:使用するライブラリによってはデータ型を混合させての計算はエラーを起こす原因となるので、後にそれを取り除くため(後述)。
print(df.dtypes)
"""
以下のように表示されます
PassengerId int64
Sex object
Age float64
"""
- この結果から、例えば以下のような争点を生み出せると思います:
- 1)Sexはmaleやfemaleのような文字列として格納されている。計算に用いるために、0 / 1のようなダミー値をふったほうが良いのではないか。
- 2)Ageはfloat(浮動小数点型)に対しPassengerIdはint(整数型)。どちらも計算に使うし、どちらかに統一したほうが良いのではないか。
3.欠損値(NaN)の確認と置換
- isnullメソッド、anyメソッドを組み合わせて用い、除外する
- これらの組み合わせにより「NaNが一つでも含まれる列」を検知できる
- 目的:欠損値は計算結果全体に悪影響を及ぼすのでそれを除外しておくため(後述)。
print(df.isnull().any())
"""
結果は以下のように表示されます
PassengerId False
Sex False
Age True
dtype: bool
"""
- ここからの示唆としては、「Age列にNaNが存在するので、これを取り除く可能性があるようだ」ということです。
- 処理方法(NaNの存在する行を消すのか、NaNを0とかで置き換えるのか、Age列そのものを消すのか、等)はケースによります。
4.基礎統計量の確認
- describeメソッドを用いて基礎統計量を確認しよう
- 各列の合計値、算術平均値、標準偏差、四分位数を教えてくれる
- 目的:分析対象データの概観をし、外れ値がないか確認する
print(df.describe())
"""
PassengerId Age
count 1000.000000 884.000000
mean 446.000000 29.699118
std 257.353842 14.526497
min 1.000000 3.100000
25% 215.500000 20.125000
50% 430.000000 27.000000
75% 703.500000 39.000000
max 1000.000000 80.000000
"""
- 得られる示唆:
- Ageのminが3.1とあるが、head/tailで確認した通り年齢は整数(浮動小数点型だけど)で記録されているようだ。この3.1はデータ取得者の31のミスではないか?確認が必要だ。
- 統計量の読み方に注意しましょう
- PassengerId(乗客番号)の統計量に意味はない
- Sex列はobject型なので、自動で除外してくれています
本論II | 基本的な処理を行う
1.欠損値を処理する
- 今回の場合、例えば「年齢のNaNは0にしよう。今後年齢の平均値を算出する際などは、0以外の値を分析対象にしよう」などと考え、NaNを0に変換する。
- locにて「Age列の値がNaNである行のAge列全部」(日本語ややこしいけど)を抽出し0を代入する
# 前章にてNaNの存在が確認された列に対して変換をほどこす
df.loc[df['Age'].isnull(), 'Age'] = 0
# 正しく処理が行えたか確認
print(df.isnull().any())
"""
以下のように表示されます。前章cと比較されたい。
PassengerId False
Sex False
Age False
dtype: bool
"""
2.データの種類やデータ型を統一する
- 前章に基づき、データ型を統一する作業を行う
- astypeメソッドを用いて列単位でデータ型を変換する
- 今回の場合、①PassengerIdをfloat64型に変更し、②Sexにダミー変数として0 / 1を割り当て(てそれもfloat64型にす)る、の2つが必要
# PassengerIdの型変更
df.PassengerId = df.PassengerId.astype('float64')
# Sexのダミー値割当(maleは0、femaleは1とする) & float64化
df.Sex[df.Sex=='male'] = 0
df.Sex[df.Sex=='female'] = 1
df.Sex = df.Sex.astype('float64')
#正しく処理が行えたか確認
print(df.dtypes)
"""
以下のように表示されます:
PassengerId float64
Sex float64
Age float64
"""
おわりに
- 基本的な前処理の流れと手順をまとめました。どんなデータを分析する場合においても、このような前処理の必要性は必ず出てくるものと思われます。ご感想などあればお寄せいただけますと幸いです。筆者も初心者なので一層勉強していきます。
- 3/27追記:この前処理の手順を実際に行ってみたのが こちらです。よかったらご覧くださいませ!