pandas はデータを読み込むとき、よきに計らってカラムに型を付与してくれる。 ただ、その内容が意図しない場合もある。 そんなとき、どうやってカラムの型を直すか、ということについて。
使った環境は次の通り。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.12.6 BuildVersion: 16G29 $ python --version Python 3.6.3
もくじ
下準備
まずは下準備として pandas をインストールしておく。
$ pip install pandas $ pip list --format=columns | grep pandas pandas 0.21.0
サンプルデータ
続いてサンプルとなるデータフレームを次のように用意する。
>>> columns = ['name', 'innings'] >>> data = [ ... ['Sugano', 187.1], ... ['Mikolas', 188], ... ['Messenger', 143], ... ['Nomura', 155.1], ... ['Imanaga', 148], ... ] >>> import pandas as pd >>> df = pd.DataFrame(data, columns=columns) >>> df name innings 0 Sugano 187.1 1 Mikolas 188.0 2 Messenger 143.0 3 Nomura 155.1 4 Imanaga 148.0
カラムの型は DataFrame#dtype
プロパティで確認できる。
>>> df['innings'].dtype dtype('float64')
例えば、この innings
カラムを整数型 (int64) に変換してみよう。
カラムの型を変換する
特定のカラム (Series) の型を変換するには Series#astype()
メソッドを使う。
このメソッドを使うと同じ値で指定された型の Series
オブジェクトができる。
>>> import numpy as np >>> df['innings'].astype(np.int64) 0 187 1 188 2 143 3 155 4 148 Name: innings, dtype: int64
型を変換したカラムを非破壊的に追加する
元々のデータフレームに、型を変換したカラムを追加したものを作りたいときは DataFrame#assign()
メソッドを使うと良い。
>>> df.assign(innings_int = df['innings'].astype(np.int64)) name innings innings_int 0 Sugano 187.1 187 1 Mikolas 188.0 188 2 Messenger 143.0 143 3 Nomura 155.1 155 4 Imanaga 148.0 148
このメソッドは非破壊的な処理で、カラムを追加した新しいデータフレームを作って返す。
そのため、元々のデータフレームを確認してもカラムは追加されていない。
>>> df name innings 0 Sugano 187.1 1 Mikolas 188.0 2 Messenger 143.0 3 Nomura 155.1 4 Imanaga 148.0
つまり、新しくできたデータフレームを別の変数とかに格納して使えば良い。
>>> df2 = df.assign(innings_int = df['innings'].astype(np.int64))
型を変換したカラムを破壊的に追加する
破壊的に追加したいときは、元々のデータフレームにブラケット演算子で名前を指定して代入すれば良い。
>>> df['innings_int'] = df['innings'].astype(np.int64) >>> df name innings innings_int 0 Sugano 187.1 187 1 Mikolas 188.0 188 2 Messenger 143.0 143 3 Nomura 155.1 155 4 Imanaga 148.0 148
もっと柔軟に変換する
これまでの例のように浮動小数点数型を整数型にするくらいの処理ならいいんだけど、もっと柔軟に型を変換したいという場合もある。 例えば、小数点の端数を切り上げた上で整数型に変換したい、という場合を考えてみよう。
こういった場合には DataFrame#apply()
メソッドを使って、その処理を一行ずつに適用していくことが考えられる。
ただし、このやり方はカラム単位ではなく行単位の処理になるので遅い。
これに限らず axis=1
となるような処理は、なるべく避けた方が良いと思う。
>>> import math >>> df.apply(lambda x: math.ceil(x['innings']), axis=1) 0 188 1 188 2 143 3 156 4 148 dtype: int64
作った Series
をデータフレームに追加する方法については先ほどと同じ。
>>> df.assign(innings_ceil = df.apply(lambda x: math.ceil(x['innings']), axis=1)) name innings innings_ceil 0 Sugano 187.1 188 1 Mikolas 188.0 188 2 Messenger 143.0 143 3 Nomura 155.1 156 4 Imanaga 148.0 148
値として NaN が入っている場合
ちなみに、値として NaN が入っているときは、ちょっと話が変わってくる。
>>> columns = ['name', 'innings'] >>> data = [ ... ['Sugano', 187.1], ... ['Mikolas', 188], ... ['Messenger', 143], ... ['Nomura', 155.1], ... ['Imanaga', 148], ... ['Sawamura', np.nan], ... ] >>> import pandas as pd >>> df = pd.DataFrame(data, columns=columns)
NaN が入っている場合に、最初の例のように Series#astype()
メソッドを使うと ValueError
例外になってしまう。
>>> df['innings'].astype(np.int64) Traceback (most recent call last): File "<stdin>", line 1, in <module> ...(省略)... ValueError: Cannot convert non-finite values (NA or inf) to integer
そういったときは Series#fillna()
メソッドなどを使って NaN をそれ以外の値に置き換えてやる必要がある。
もちろん Series#dropna()
などで、そもそも集計の対象外としてしまうことも考えられる。
>>> df['innings'].fillna(0.0) 0 187.1 1 188.0 2 143.0 3 155.1 4 148.0 5 0.0 Name: innings, dtype: float64
Series#fillna()
メソッドなどを使って NaN さえ無くすことができれば、ちゃんと変換できる。
>>> df['innings'].fillna(0.0).astype(int) 0 187 1 188 2 143 3 155 4 148 5 0 Name: innings, dtype: int64
変換した結果を元のデータフレームに追加する方法については、これまでと変わらない。
>>> df.assign(innings_int = df['innings'].fillna(0.0).astype(np.int64)) name innings innings_int 0 Sugano 187.1 187 1 Mikolas 188.0 188 2 Messenger 143.0 143 3 Nomura 155.1 155 4 Imanaga 148.0 148 5 Sawamura NaN 0
めでたしめでたし。
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者:もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版