👨‍💻

機械学習チュートリアル① - はじめに 〜 pandas入門

2022/01/01に公開

はじめに

目的

AI(機械学習)を学んでいきます。

機械学習のディープなところに入り込むというよりは、0から初めて機械学習を動かすことを目標とします。

この教材を読むための条件

「なんらかのプログラム言語に触れたことがあること」をこの教材における必須条件とします。

本教材においては、機械学習分野で頻繁に使用される言語であるPythonを使用します。

プログラミング自体初めての場合、ProgateなどでPythonを先に学ぶとよいでしょう。

https://prog-8.com/courses/python


教材の流れと対応する章

機械学習の流れとしては、「データを集める」 → 「データの前処理」 → 「学習」 → 「新規データに対する予測」 → 「精度評価」 といった流れで進めます。

工程 基礎学習 実践
データを集める
データの前処理 ① pandas入門 ②とりあえず機械学習してみる; ③機械学習の精度を上げる; ④機械学習の精度を上げる(特徴量エンジニアリング)
学習 ②とりあえず機械学習してみる; ⑤機械学習の精度を上げる(パラメーターチューニング)
新規データに対する予測 ②とりあえず機械学習してみる
精度評価 ②とりあえず機械学習してみる

実行環境

Google colabを使います。

https://colab.research.google.com/

ソースコードの黒枠内に記載がある文字をコピペして、shift + Enterを押すと実行することができます。

  • ソースコード
print(3+4)
  • 出力

7


Pandas入門

目次

  • Pandasとは
  • この章の目的と最終コード
  • データフレームの作成
  • データフレームに関する用語説明
  • データフレーム基礎操作・ファイルを読み込み
  • データフレーム基礎操作・新たな列の作成
  • データフレーム基礎操作・値の置換
  • データフレーム基礎操作・欠損値を埋める
  • データフレーム基礎操作・列の削除

Pandasとは

Excelなどで使われるようなテーブル形式のデータをPythonで扱うためのライブラリです。

Pandasでは「データフレーム形式」というものを用いることで、高速かつ可読性の高い形でテーブルを扱うことができます。

ライブラリの読み込みは import構文で行うことができます。

  • ソースコード
import pandas as pd # 出力なし

この章の目的と最終コード

この章では、以下のテーブルをpandas経由で次のように変換してみます。

  • 変換前
性別 年齢 兄弟姉妹 親子 生存
female 53 0 0 1
male 21 1 1 0
female 31 1 3 1
male - 2 2 1
  • 変換後
性別 年齢 家族 生存
0 53 1 1
1 21 3 0
0 31 5 1
1 35 5 1

このテーブルは次の章で使うTitanicのデータをものすごく小さくしたものです。

生存の列は1が生存を、0が非生存を表します。

年齢の列の-(ハイフン)は欠損値を表します。(欠損値については後述します)

最終コードは以下のようになっています。

  • ソースコード
import numpy as np
import pandas as pd

df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])

display(df) # 編集前のdataframeの出力

# 家族列を足す
df["家族"] = df["兄弟姉妹"] + df["親子"] + 1

# 性別列の置換
df["性別"] = df["性別"].replace({"female": 0, "male": 1})

# 年齢列のNaNを年齢の平均値で埋める
df["年齢"] = df["年齢"].fillna(df["年齢"].mean())

# 兄弟姉妹、親子列の削除
df.drop(["兄弟姉妹", "親子"], axis=1, inplace=True)

display(df) # 編集後のdataframeの出力
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2
  • 出力
年齢 性別 生存 家族
1 53 0 1 1
2 21 1 0 3
3 31 0 1 5
4 35 1 1 5

データフレームの作成

pd.Dataframe() でデータフレームを作成することができます。

ここでは本番で使うデータをものすごーく小さくしたデータを作ってみます。

  • サンプルコード
import numpy as np
import pandas as pd

df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])

df
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2

np.nanは欠損値を作るための値です。ここではあまり気にする必要はありません。)

最後にdfと打つことで、変数が見やすい形式で出力されていることがわかると思います。

キーを列名, バリューを値の配列として与えることで、新規データフレームを生成することができます。

なお、dfという変数名は、データフレーム (data frame) というデータ構造の略語で、

dataframe型の変数にはdfという変数名がよく使用されます。


データフレームに関する用語説明

index, column, Seriesについて説明します。

用語 説明
index 行名のこと。df.indexで行名を配列として取得できる。
column 列名のこと。df.columnsで列名を配列として取得できる。
Series Pandasにおける1次元データ構造のこと。SeriesがつながってDataFrameができている。

イメージとしては以下です。

自分が扱っているデータ形式がデータフレームであるか、シリーズであるかは忘れやすいです。

エラーが起きたときには注意してみてください。


データフレーム基礎操作・ファイルを読み込み

今回は辞書からデータフレームを作成しましたが、外部のファイルを読み込むこともできます。

(実際のKaggleでは、入力ファイルはほとんどの場合csvで与えられます。)

pandasにはCSVファイルを読み込むための関数(pd.read_csv())が用意されています。

ここではGoogle Colaboratoryに用意されているサンプルデータを読み込みます。

  • サンプルコード
df = pd.read_csv('sample_data/california_housing_train.csv')
df.head()
  • 出力
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value
0 -114.31 34.19 15 5612 1283 1015 472 1.4936 66900
1 -114.47 34.4 19 7650 1901 1129 463 1.82 80100
2 -114.56 33.69 17 720 174 333 117 1.6509 85700
3 -114.57 33.64 14 1501 337 515 226 3.1917 73400
4 -114.57 33.57 20 1454 326 624 262 1.925 65500

df.head() は読み込んだデータフレームの内、頭5行だけ表示するメソッドです。

データフレームが巨大なときは、とりあえずdf.head()で見るとよいでしょう。


データフレーム基礎操作・新たな列の作成

まず、「兄弟姉妹」と「親子」の列から、家族の人数を出してみます。

df[列名] = 配列で新たな列を作成することができます。

  • サンプルコード
import pandas as pd
from IPython.display import display # 2つ以上DataFrameをNotebook上に出す用。実際のKaggleでは使用頻度は低い

# データの準備
df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])
display(df)

# 新たな列の作成
df["家族"] = df["兄弟姉妹"] + df["親子"] + 1
display(df)
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2
  • 出力
兄弟姉妹 年齢 性別 生存 親子 家族
1 0 53 female 1 0 1
2 1 21 male 0 1 3
3 1 31 female 1 3 5
4 2 nan male 1 2 5

pandasは列方向の操作はしやすく、列名を直接指定して四則演算などをすることができます。

df[列名] は単体で出力するとSeriesが取り出されます。

実際に df["親子"] と打つと、「親子」の列が取り出されているのがわかります。

df["親子"]の中身としては、上で説明したpandas.seriesが入っています。)

  • サンプルコード
df["親子"]
1    0
2    1
3    3
4    2
Name: 親子, dtype: int64

左側にindexが、右側に実際の値が並んで出力されていることがわかります。


データフレーム基礎操作・値の置換

次に、「性別」列の値の置換をします。

df.replace(辞書) でデータフレーム内の値を置換することができます。

今回、「性別」列のみ置換ということを意識するため、「性別」列だけを抽出して置換してみます。

  • サンプルコード
import pandas as pd
from IPython.display import display # 2つ以上DataFrameをNotebook上に出す用

# データの準備
df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])
display(df)

# 実際の操作
df["性別"] = df["性別"].replace({"female": 0, "male": 1})
display(df)
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 0 1 0
2 1 21 1 0 1
3 1 31 0 1 3
4 2 nan 1 1 2

「性別」列の値が置換されていることがわかると思います。


データフレーム基礎操作・欠損値を埋める

機械学習に限らず、データを操作するときにつきものなのが、欠損値(データの欠け)です。

Pandasの機能を用いてこの欠損値を埋めてみます。

まず、どのような時に欠損値(NaN)ができるかを見てみます。

  • サンプルコード
# ファイルを書き出す
f = open("_test_data.csv", "w")
f.write(",C1,C2,C3\n")
f.write("R1,1,2,3\n")
f.write("R2,2,,1\n")
f.write("R3,3,7,9\n")
f.close()

(上記スクリプトはテストデータを作るのが目的なので、深く理解する必要はありません)

左サイドバーに現れた_test_data.csvをクリックでも見ることはできますが、

ファイルの内容を確認することのできるcatコマンドで中身を見てみましょう。

  • サンプルコード
!cat _test_data.csv
  • 出力
    ,C1,C2,C3
    R1,1,2,3
    R2,2,,1
    R3,3,7,9

(!を頭につけることで、bashのコマンドを打つこともできます)

上記のように、2行2列目が欠損値になっていることがわかると思います。

このファイルをpandasで読み込んでみます。

  • サンプルコード
df = pd.read_csv("_test_data.csv", index_col=0)
df
  • 出力
C1 C2 C3
R1 1 2 3
R2 2 nan 1
R3 3 7 9

R2, C2の値が NaNという値になっていることがわかると思います。

このように、外部ファイルの値が空になっている部分は NaN で埋められます。

実際のデータを年齢の平均値で埋めてみます。

  • サンプルコード
import pandas as pd
from IPython.display import display # 2つ以上DataFrameをNotebook上に出す用

# データの準備
df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])
display(df)

# 実際の操作
ave_age = df["年齢"].mean()
df["年齢"] = df["年齢"].fillna(ave_age)
display(df)
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 35 male 1 2

データフレームの値が年齢の平均値で埋められた様子がわかるかと思います。

df["年齢"].mean() についてはおまけのPandas応用のページ に記載しているので、そちらもご参照ください。


データフレーム基礎操作・列の削除

「兄弟姉妹」と「親子」の列を削除してみます。

df.drop([列名], axis=1, inplace=True) で列を削除することができます。

axis=1 は列方向の操作を表し、 inplace=Trueはdfを直接書き換える(破壊的メソッド)を表しています。

  • サンプルコード
import pandas as pd
from IPython.display import display # 2つ以上DataFrameをNotebook上に出す用

# データの準備
df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])
display(df)

# 実際の操作
df.drop(["兄弟姉妹", "親子"], axis=1, inplace=True)
display(df)
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2
  • 出力
年齢 性別 生存
1 53 female 1
2 21 male 0
3 31 female 1
4 nan male 1

兄弟姉妹、親子の列が削除されていればOKです。


おわりに

上記をまとめると、最終コードと同じ様になります。

  • 入力
import numpy as np
import pandas as pd

df = pd.DataFrame({
    "性別": ["female", "male", "female", "male"],
    "年齢": [53, 21, 31, np.nan],
    "兄弟姉妹": [0, 1, 1, 2],
    "親子": [0, 1, 3, 2],
    "生存": [1, 0, 1, 1]
}, index=["1", "2", "3", "4"])

display(df) # 編集前のdataframeの出力

# 家族列を足す
df["家族"] = df["兄弟姉妹"] + df["親子"] + 1

# 性別列の置換
df["性別"] = df["性別"].replace({"female": 0, "male": 1})

# 年齢列のNaNを埋める
df["年齢"] = df["年齢"].fillna(df["年齢"].mean())

# 兄弟姉妹、親子列の削除
df.drop(["兄弟姉妹", "親子"], axis=1, inplace=True)

display(df) # 編集後のdataframeの出力
  • 出力
兄弟姉妹 年齢 性別 生存 親子
1 0 53 female 1 0
2 1 21 male 0 1
3 1 31 female 1 3
4 2 nan male 1 2
  • 出力
年齢 性別 生存 家族
1 53 0 1 1
2 21 1 0 3
3 31 0 1 5
4 35 1 1 5

全体目次

Discussion