.. _sec_pandas: データ前処理 ============ | これまで、私たちはすぐに使えるテンソルとして届く合成データを扱ってきた。 | 現実世界で深層学習を適用するには、さまざまな形式で保存された雑多なデータを取り込み、必要に応じて前処理を行う必要がある。 幸いなことに、\ *pandas* `ライブラリ `__ はその大部分を自動化・簡略化してくれる。 この節は pandas の包括的な `チュートリアル `__ ではないが、よく使われる基本的なデータ処理についての速習になる。 データセットの読み込み ---------------------- | カンマ区切り値(CSV)ファイルは、表形式(スプレッドシートのような)データを保存するために広く使われている。 | CSV では、各行が1つのレコードに対応し、いくつかの(カンマ区切りの)フィールドから構成される。たとえば、“Albert Einstein,March 14 1879,Ulm,Federal polytechnic school,field of gravitational physics” のようなものである。 | ``pandas`` を使って CSV ファイルを読み込む方法を示すために、ここでは以下で CSV ファイルを作成する ``../data/house_tiny.csv`` を用いる。 | このファイルは住宅データセットを表しており、各行が1つの住宅に対応し、列は部屋数(\ ``NumRooms``\ )、屋根の種類(\ ``RoofType``\ )、価格(\ ``Price``\ )を表す。 .. raw:: latex \diilbookstyleinputcell .. code:: python import os os.makedirs(os.path.join('..', 'data'), exist_ok=True) data_file = os.path.join('..', 'data', 'house_tiny.csv') with open(data_file, 'w') as f: f.write('''NumRooms,RoofType,Price NA,NA,127500 2,NA,106000 4,Slate,178100 NA,NA,140000''') では、\ ``pandas`` をインポートして ``read_csv`` でデータセットを読み込みよう。 .. raw:: latex \diilbookstyleinputcell .. code:: python import pandas as pd data = pd.read_csv(data_file) print(data) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output NumRooms RoofType Price 0 NaN NaN 127500 1 2.0 NaN 106000 2 4.0 Slate 178100 3 NaN NaN 140000 データの準備 ------------ | 教師あり学習では、ある一連の *入力* 値が与えられたときに、指定された *目標* 値を予測するようにモデルを訓練する。 | データセットを処理する最初のステップは、入力値に対応する列と目標値に対応する列を分けることである。 | 列は名前で選択することも、整数位置に基づくインデックス指定(\ ``iloc``\ )で選択することもできる。 | お気づきかもしれないが、\ ``pandas`` は CSV の ``NA`` の値をすべて、特別な ``NaN``\ (\ *not a number*\ )値に置き換えた。 | これは、たとえば “3,,,270000” のように、項目が空欄の場合にも起こりる。 | これらは *欠損値* と呼ばれ、データサイエンスにおける難所の一つである。あなたのキャリアを通じて向き合い続けることになる厄介な問題と言えるだろう。 文脈に応じて、欠損値は *補完* (imputation) か *削除* (deletion) によって処理される。 補完では欠損値を推定値で置き換え、削除では欠損値を含む行または列をデータセットから除外する。 | 以下に、よく使われる補完のヒューリスティックを示す。 | カテゴリ型の入力欄では、\ ``NaN`` を1つのカテゴリとして扱うことができる。 | ``RoofType`` 列は ``Slate`` と ``NaN`` の値を取るので、\ ``pandas`` はこの列を ``RoofType_Slate`` と ``RoofType_nan`` の2列に変換できる。 | 屋根の種類が ``Slate`` の行では、\ ``RoofType_Slate`` と ``RoofType_nan`` の値はそれぞれ 1 と 0 になる。 | ``RoofType`` の値が欠損している行では、その逆になる。 .. raw:: latex \diilbookstyleinputcell .. code:: python inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2] inputs = pd.get_dummies(inputs, dummy_na=True) print(inputs) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output NumRooms RoofType_Slate RoofType_nan 0 NaN False True 1 2.0 False True 2 4.0 True False 3 NaN False True 数値の欠損値については、よく使われるヒューリスティックとして、\ ``NaN`` の項目を対応する列の平均値で置き換える 方法がある。 .. raw:: latex \diilbookstyleinputcell .. code:: python inputs = inputs.fillna(inputs.mean()) print(inputs) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output NumRooms RoofType_Slate RoofType_nan 0 3.0 False True 1 2.0 False True 2 4.0 True False 3 3.0 False True テンソル形式への変換 -------------------- これで ``inputs`` と ``targets`` のすべての項目が数値になったので、テンソルに読み込める(:numref:`sec_ndarray` を思い出してほしい)。 .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python import torch X = torch.tensor(inputs.to_numpy(dtype=float)) y = torch.tensor(targets.to_numpy(dtype=float)) X, y .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (tensor([[3., 0., 1.], [2., 0., 1.], [4., 1., 0.], [3., 0., 1.]], dtype=torch.float64), tensor([127500., 106000., 178100., 140000.], dtype=torch.float64)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python from mxnet import np X, y = np.array(inputs.to_numpy(dtype=float)), np.array(targets.to_numpy(dtype=float)) X, y .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output [07:03:52] ../src/storage/storage.cc:196: Using Pooled (Naive) StorageManager for CPU .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (array([[3., 0., 1.], [2., 0., 1.], [4., 1., 0.], [3., 0., 1.]], dtype=float64), array([127500., 106000., 178100., 140000.], dtype=float64)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python from jax import numpy as jnp X = jnp.array(inputs.to_numpy(dtype=float)) y = jnp.array(targets.to_numpy(dtype=float)) X, y .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (Array([[3., 0., 1.], [2., 0., 1.], [4., 1., 0.], [3., 0., 1.]], dtype=float32), Array([127500., 106000., 178100., 140000.], dtype=float32)) .. raw:: html
.. raw:: html
.. raw:: latex \diilbookstyleinputcell .. code:: python import tensorflow as tf X = tf.constant(inputs.to_numpy(dtype=float)) y = tf.constant(targets.to_numpy(dtype=float)) X, y .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output (, ) .. raw:: html
.. raw:: html
議論 ---- | これで、データ列を分割し、欠損変数を補完し、\ ``pandas`` のデータをテンソルに読み込む方法がわかった。 | :numref:`sec_kaggle_house` では、さらにいくつかのデータ処理スキルを学ぶ。 | この速習では話を単純にしたが、データ処理はかなり複雑になりえる。 | たとえば、データセットが1つの CSV ファイルにまとまっているのではなく、リレーショナルデータベースから抽出された複数のファイルに分散していることがある。 | たとえば電子商取引アプリケーションでは、顧客住所はあるテーブルに、購買データは別のテーブルにあるかもしれない。 | さらに、実務ではカテゴリ型や数値型以外にも、テキスト文字列、画像、音声データ、点群など、さまざまなデータ型に直面する。 | しばしば、データ処理が機械学習パイプラインの最大のボトルネックにならないようにするために、高度なツールや効率的なアルゴリズムが必要になる。 | これらの問題は、コンピュータビジョンや自然言語処理に進むと現れてきる。 | 最後に、データ品質にも注意を払わなければならない。 | 現実世界のデータセットは、外れ値、センサーによる誤測定、記録ミスなどに悩まされることが多く、データをどのモデルに入れる前にも対処が必要である。 | `seaborn `__\ 、\ `Bokeh `__\ 、\ `matplotlib `__ などのデータ可視化ツールは、データを手作業で確認し、どのような問題に対処すべきかについて直感を養うのに役立ちる。 演習 ---- 1. たとえば UCI Machine Learning Repository の Abalone などのデータセットを読み込み、その性質を調べてみよう。欠損値を含む割合はどれくらいだろうか。変数のうち、数値型、カテゴリ型、テキスト型の割合はどれくらいだろうか。 2. 列番号ではなく列名によってデータ列をインデックス指定し、選択してみよう。pandas の `indexing `__ のドキュメントには、その方法の詳細が載っている。 3. この方法でどれくらい大きなデータセットまで読み込めると思うか。どのような制約があるだろうか。ヒント:データの読み込み時間、表現、処理、メモリ使用量を考えてみよ。自分のノートパソコンで試してみよう。サーバー上で試すとどうなるか。 4. カテゴリ数が非常に多いデータをどのように扱いますか。カテゴリラベルがすべて一意だったらどうだろうか。後者も含めるべきだろうか。 5. pandas の代替として何が考えられますか。ファイルから NumPy テンソルを読み込む方法はどうだろうか。\ `Pillow `__\ 、つまり Python Imaging Library も調べてみよう。