書籍やネット上のscikit-learn教材には、irisやcancer等の付属データセットを使った解説が多いです。もちろん手軽で 同じ結果が得られる安心感もある一方、予定調和的で 深い学びが得にくいと感じる方も多いのではないかと思います。
この記事では、自作データやネット上にある外部データを読み込んで、scikit-learnで分析する方法をご紹介します。
(検証環境:Windows10, Anaconda3, Python3.7.6, Jupyter Notebook6.0.3)初稿公開2020/3/23
## CSVファイルの準備
この記事では、例として機械学習・データサイエンス コミュニティーである Kaggle が公開しているデータ World Happiness Report←リンク を使います。機械学習に使いやすいデータセットが揃っているので、ユーザー登録が必要ですが Kaggleを選択しました。
Download (79 KB)
というボタンからダウンロードして下さい。zipファイルを解凍すると 5つのCSVファイルが見つかりますが、ここでは 2019.csv を使います。
他のファイルを使う場合
-
2019.csv はPythonデータ分析ライブラリである pandasで読み込み易いような「一行目に特徴量の名前、二行目以降にデータ」という配置になっています。そうでないデータは、エクセルの行削除などで成型して下さい。
-
エクセルファイル(.xls) など形式が違う場合は、エクセル等で読み込んだ後「ファイル-別名で保存」を行い、CSVファイル形式を選択します。区切り記号が選択可能な場合は ,(カンマ)にしておきます。
-
保存場所はPythonの実行ファイル(pyやipynbファイル)があるフォルダにしておくと楽です。
## CSVファイルの読み込み
ライブラリを使わずに 直接読み込む方法もありますが、以降の作業を楽にするため、この記事では pandasを使った方法を紹介します。
(pandasがまだインストールされていない場合は、[この記事]
(https://www.sejuku.net/blog/75508) 等を参考に行って下さい。)
import pandas as pd
df = pd.read_csv('2019.csv')
区切り記号がタブの場合は引数sep='\t'を、日本語が入っている場合は引数encoding='shift_jis'を加えます
df = pd.read_csv('ファイル名.csv', sep='\t', encoding='shift_jis')
実行ファイルと別の場所にデータファイルを置く場合は df=pd.read_csv('data/2019.csv')
と相対パスを加えるなどします。参考→Python, pathlibで絶対パスと相対パスを相互変換・判定
## 特徴量名とデータ数の確認
print("データセットのキー(特徴量名)の確認==>:\n", df.keys())
print('dataframeの行数・列数の確認==>\n', df.shape)
上記のコマンドを実行すると、
データセットのキー(特徴量名)の確認==>:
Index(['Overall rank', 'Country or region', 'Score', 'GDP per capita',
'Social support', 'Healthy life expectancy',
'Freedom to make life choices', 'Generosity',
'Perceptions of corruption'],
dtype='object')
dataframeの行数・列数の確認==>
(156, 9)
と9つの特徴量があるサンプル数156のデータが読み込めたことが確認できます。
## 欠損値等の処理をする(特徴量エンジニアリング)
データ中に欠損値(Null)が無いか、またデータ型を確認し、整数値のみ(int)/小数を含む数値(float)/文字列または文字列と数値の混在(object)なのかを見ます。
# dataframe各列の欠損値でないデータ数、データ型を確認
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 156 entries, 0 to 155
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Overall rank 156 non-null int64
1 Country or region 156 non-null object
2 Score 156 non-null float64
3 GDP per capita 156 non-null float64
4 Social support 156 non-null float64
5 Healthy life expectancy 156 non-null float64
6 Freedom to make life choices 156 non-null float64
7 Generosity 156 non-null float64
8 Perceptions of corruption 156 non-null float64
dtypes: float64(7), int64(1), object(1)
memory usage: 11.1+ KB
Nullではないデータが156個⇒欠損値なし と確認できます。
Overall rankは整数、Country or regionは文字列、それ以外は小数を含む数値 と意図通りのデータ型になっています。
数値が入っているはずの列が objectになってしまっている場合は、
# 数値ではない型の要素の抽出
objectlist = df[['特徴量名を入れる']][df['特徴量名を入れる'].apply(lambda s:pd.to_numeric(s, errors='coerce')).isnull()]
objectlist
↑ を実行すると、文字列扱いになっているデータが抽出できます。
このデータに文字列と数値の混在や欠損値はありませんでしたが、他のデータには様々な理由で、「空欄」「ゼロを意味する"0"以外の文字・記号」「単位付きの数字」等 そのままでは分析に不適当な値が入っていることがよくあります。
こちらの記事 等を参考に、適切な処理(特徴量エンジニアリング)をして下さい。
## Scikit-learn用のデータクラスを持つオブジェクト(空のデータセット)を作る
import sklearn
worldhappiness = sklearn.utils.Bunch()
worldhappiness
の部分はデータセット名を表すよう、適宜変えて下さい。
## データセットにデータを入れていく
# 'Score'(幸福スコア)を目的変数'target'とする
worldhappiness['target'] = df['Score']
# 説明変数を'data'に入れる
worldhappiness['data'] = df.loc[:, ['GDP per capita',
'Social support', 'Healthy life expectancy',
'Freedom to make life choices', 'Generosity',
'Perceptions of corruption']]
↑ 最初の3列(目的変数および ID等の分析に使わない特徴量)以外の6列を指定します。「特徴量名の確認」で出力したデータをコピペすると楽です。
# 特徴量の名前も入れておくと、グラフの凡例等に使えます(無くても可)
worldhappiness['feature_names'] = ['GDP per capita',
'Social support', 'Healthy life expectancy',
'Freedom to make life choices', 'Generosity',
'Perceptions of corruption']
## 訓練セットとテストセットに分割
# 訓練セットとテストセットに分割
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
worldhappiness['data'], worldhappiness['target'], random_state=0)
print("X_train shape:", X_train.shape)
print("X_test shape:", X_test.shape)
X_train shape: (117, 6)
X_test shape: (39, 6)
訓練データ117個、テストデータ39個 に分割できました。(6は説明変数の項目数です)
## 結び
これで機械学習に進めると思います。間違い指摘・質問等ございましたら、お気軽にコメント下さい。