Pythonプログラミング(ステップ7・統計計算・CSV形式のデータ)

このページでは、統計処理の際によく使われるCSV形式のデータの扱いについて紹介する。

CSVデータ

行と列の区画の表(テーブル)の形式で表現されるデータは多く、表計算ソフト(Microsoft Excel, Google Spreadsheet等)は、表形式のデータを扱う専用ツールとも言える。

こうした表データを扱うためのファイル形式のひとつにCSV(Comma-Separated Values)フォーマットがある。 CSVは表計算ソフトはもちろん、テーブル形式のデータをやりとりする際の、事実上の標準フォーマットのひとつである。

CSVの内容はいたって簡単で、各項目がコンマで区切られ、

身長,体重,年齢,推定年俸
205,115,31,15000
195,95,31,1200
194,92,25,800
191,118,33,7000
...

のように並んでいるだけである。1行目はヘッダで、各列の表題を表す。2行目以降がデータである。 なお、ヘッダーが無く、データ(値)のみで構成される場合もある。

データ項目の区切りは、コンマ以外に、タブ(TAB)文字や空白文字が使われる場合もある。タブで区切られたデータを特に TSV と呼ぶこともある。

文字列データを扱う場合は、途中に空白やコンマを含む場合もあるので、両側をダブルクォーテーションで挟むのが「正しい」作法である。 例えば上記のデータを

"身長","体重","年齢","推定年俸"
205,115,31,15000
195,95,31,1200
194,92,25,800
191,118,33,7000
...

としても、同様に解釈される。ダブルクォーテーションで挟まれた文字列中でのダブルクォーテーションは""で表現する。また、 文字列中のTABは\t、改行は\nで表現する。

CSVの中の文字列の文字コードはさまざまで紛らわしい。それを判別するために、先頭にBOM(byte order mark)と呼ばれるデータが付加されている場合がある。 データの先頭に「ゴミ」のような文字が見える場合は、BOM付きのCSVの可能性がある。

以下に、1行目がラベルで、コンマ区切りの整数値の表をリストに読み込むコードの例を示す。 各列のデータの形式(整数、実数、文字列)が異なっていたりする場合でも対応できるようなコードを書くのは(かなり)面倒なので、 次節のような専用のモジュールを用いるのが簡単で確実である。

CSVデータ rakuten.csv

# coding: utf-8

with open('rakuten.csv',mode='r',encoding='utf-8') as file:
    lines = file.readlines()

data=[ ]
cnt=0
for line in lines:
    if cnt>0 and len(line.strip())>0: # skip first and empty line
        data.append( list(map(int,line.split(','))) )
    cnt += 1

print(data)

pandasを使ったCSVの読み込み

上記のとおり、CSVはよく使われるので、それを操作するためのライブラリが流通している。中でもデータ解析でよく使われるのがpandasで、CSVに限らず、様々なフォーマットのデータを扱う機能が提供されている。

以下は、あるプロ野球球団の選手の体重、身長、年齢、推定年俸のCSVデータを、 pandasを使って読み込み、散布図をプロットするコードの例である。

コードの補足説明

CSVデータの取り込むにはファイル名(あるいはURL)を指定して

import pandas as pd
...
data = pd.read_csv('rakuten.csv')

のように書くだけで、DataFrameと呼ばれるオブジェクトとして内容が得られる。あるいは、DataFrameのvalues属性を参照して、

array = pd.read_csv('rakuten.csv').values

のように書くと、すべての値をNumPyの二次元配列(ndarray)で取得することができる。

各列のラベルとデータ形式は オブジェクト名.dtypesで得ることができる。

DataFrameオブジェクトで読み込んだデータの各列(コラム)は オブジェクト名['ラベル名']で参照できる。さらに、.tolist()によってその内容の標準リストが得られる。

推定年俸は、list(map(math.log10,data['推定年俸'].tolist()))の箇所で、常用対数を取った値のリストに変換している。 map(関数, リスト)で、リストに関数を作用させ、mapオブジェクトを生成。それをlist( )でリストに変換。

なお、DataFrameオブジェクトを行毎に処理したい場合は、イテレータを使って

for n,cols in data.iterrows():
    print("行番号=",n, cols['年齢'], cols['推定年俸'])

のような記述が可能である。

CSVデータ rakuten.csv

# coding: utf-8

import math
import pandas as pd
import matplotlib.pyplot as plt

data = pd.read_csv('rakuten.csv')

print(data.dtypes)

wt = data['体重'].tolist()
ht = data['身長'].tolist()
age = data['年齢'].tolist()
wage = list(map(math.log10,data['推定年俸'].tolist()))

fig = plt.figure()

ax1 = fig.add_subplot(2, 2, 1)
ax1.scatter(wt, wage)
ax1.set_xlabel("Weight [kg]")
ax1.set_ylabel("LOG Wage[10000 JPY]")

ax2 = fig.add_subplot(2, 2, 2)
ax2.scatter(ht, wage)
ax2.set_xlabel("Height [cm]")
ax2.set_ylabel("LOG Wage[10000 JPY]")

ax3 = fig.add_subplot(2, 2, 3)
ax3.scatter(age, wage)
ax3.set_xlabel("Age")
ax3.set_ylabel("LOG Wage[10000 JPY]")

ax4 = fig.add_subplot(2, 2, 4)
ax4.scatter(wt, ht)
ax4.set_xlabel("Weight [kg]")
ax4.set_ylabel("Height [cm]")

plt.show()

実行結果