pandasでcsvを読み取った際のカラム名のデータタイプ

はじめに

pythonでcsvを取り扱うには,pandasを利用するのが一番便利.お決まりのimport pandas as pdでimportし,pa.read_csv(file_path)関数で簡単に読み取ることができる.

しかしながら,DataFrameカラム名のデータタイプについて少し注意が必要なので,その点についてまとめる.

具体的には,csv_read()で読み取ると,カラムのデータタイプはstring型で読み取られてしまう.通し番号などで,カラム名もint型で扱いたい場合に少し不便.

発生する問題

pd.read_csv(file_path)関数の,必須引数はファイルパスである.これは絶対パスでも相対パスでもOK.

今回は,以下のような構造のcsvファイル(test.csv)を用意する.

0 1 2 3
0 0.0 0.1 0.2 0.3
1 1.0 1.1 1.2 1.3
2 2.0 2.1 2.2 2.3
3 3.0 3.1 3.2 3.3
4 4.0 4.1 4.2 4.3
5 5.0 5.1 5.2 5.3

このファイルのindexは[0,1,2,3,4,5]で,columnは[0,1,2,3]で,両方ともint型で扱いたいとする.各要素はfloat型のなんらかのデータである.(例としてわかりやすいように規則的に値を定めた)

In [1]:
import pandas as pd
In [2]:
#テスト用にtable1を作成し,csv出力
original_table = pd.DataFrame(index=range(6),columns=range(4))

for i in range(6):
    for c in range(4):
        original_table.loc[i,c] = i+0.1*c
original_tableのindex,columnのデータタイプを確認する.

In [3]:
print("index:",original_table.index.dtype)
print("column:",original_table.columns.dtype)
index: int64
column: int64
この時点では,index,columnともにint型となっている.

次に,このoriginal_tableを一時,csvに書き出し,再度読み込む.

In [4]:
original_table.to_csv("test.csv", encoding="utf-8-sig")
copy_table = pd.read_csv("test.csv", encoding="utf-8-sig", index_col=0)

余談だが,csvは必ずBOM付きで扱うようにしている.これは,某社の表計算ソフトでもストレスなく開けるようにするためである.

BOMの扱いについては諸説あると思うが,理解のない人にデータを渡したときに「エクセルで見れない=文字化け=お前が悪い」って言われないための防衛線である.

話を戻して,copy_tableのindex,columnのデータタイプを確認する.

In [5]:
print("index:",copy_table.index.dtype)
print("column:",copy_table.columns.dtype)
index: int64
column: object
indexはint型だが,columnはobjectとなってしまっている.そのため,以下のようにtableから値を読み取れない.

In [6]:
#確認のため,original_tableから
#int型で要素指定
print(original_table.loc[4,1])
4.1
In [7]:
#次にcopy_tableから
#int型で要素指定
print(copy_table.loc[4,1])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-6f0171749c14> in <module>()
      1 #次にcopy_tableから
      2 #int型で要素指定
----> 3 print(copy_table.loc[4,1])

TypeError: cannot do label indexing on <class 'pandas.core.indexes.base.Index'> with these indexers [1] of <class 'int'>
怒られる.(出力エラーメッセージは少し削ってる)

In [8]:
#str型で要素指定
print(copy_table.loc[4,"1"])
4.1
このようにstr型にすればOKなのだが,いらぬバグを生みそうな気がするだろう.

おそらく,カラム名として想定されているのが,id, name, stateなどの文字列を想定しているため,pd.read_csv()がこのような挙動をするのだろう.

解決策

csv読み取り時点で,カラムデータタイプの指定オプションはなさそう.(もしかしたあるのかも)

とりあえずの策としては,以下のように読み取り直後にrename()を利用してカラムを書き換えてしまう方法がある.

In [9]:
copy_table = pd.read_csv("test.csv", encoding="utf-8-sig", index_col=0)

copy_table = copy_table.rename(columns={c:int(c) for c in copy_table.columns})
In [10]:
#int型で要素指定
print(copy_table.loc[4,1])
4.1
無事解決.rename関数の引数として,{変更前:変更後}のように,keyを変更前,valueを変更後の値とした辞書型を渡す.

おわりに

カラムをint型で扱う機会は少ないかもれないが,今回の内容はpd.read_csv()のデフォルトの挙動として頭に入れといた方が良いだろう.

読み取り時点で指定できる方法があれば知りたい.

1