Kuroyagi飼育日誌

学んだことの備忘録

Pythonの基本~csvファイルあれこれ~

csvファイルを読み込んでグラフを描いてみます。


今回使うのはPandasというライブラリです。numpyで読み込もうとしたのですが、扱うデータが16進数で上手くいかず、pandasなら楽という記事を見かけたのでpandasにしました。ちなみに今回のデータは以下のような形です。


以下が読み込み部分です。

# -*- coding: utf-8 -*-
import pandas as pd

data = pd.read_csv("ファイル名",
                   index_col=False, delimiter=r",\s*", engine="python")

 
 
確認としてprint(data.shape)とやると[2400297 rows x 16 columns]と出てきます。また、print(data)を実行するとダバァとデータが実行結果に表示されます。


続いてファイルの型を確認しておきます。print( type(data) )でと分かります。


読み込んだデータは使うためにあります。早速【記事1】を参考にして1列取り出してみます。


【記事1】
Pandas でデータフレームから特定の行・列を取得する – Python でデータサイエンス


追加したのは以下のコードです。

print(data.iloc[:,1])

 

数値は諸事情でお見せできませんが、行数とともに格納データが表示されます。

ただ、今回のデータ列はヘッダ情報がなく、いきなり1行目からデータが格納されているのでそのあたりを調整しなければなりません。ちなみにそのまま読み込むと列のNameが1行目のデータとなります。よって、ひとまずヘッダをNoneとする以下の読み込み方を試して見ます。

data = pd.read_csv("ファイル名",
                   index_col=False, delimiter=r",\s*", header=None, engine="python")

 
 
するとNameが2となりました。これはindexが2の列はnameが2となっているようです。Nameを設定する方法があるみたいなのでそれをやってみます。やり方は以下の通り。

data = pd.read_csv("ファイル名",
                   index_col=False, delimiter=r",\s*",
                   names=('c01','c02','c03','c04','c05','c06','c07','c08','c09','c10','c11','c12','c13','c14','c15','c16'),
                   engine="python")

 
 
とりあえず連番でつけてみました。ilocのindexは2で出力結果はName: c03と出てくるのでilocのindexは1列目に0が割りあててあるようです。


さて取り出したデータが16進数ですので、intあたりに直してグラフに描画してみます。


試しにint()に突っ込んでみましたがエラーで怒られました。多分seriesやlist形式では受け入れてくれないのかな?ということで, 配列を受けいれらるようにmap()を使います。

map(int,y)


とint関数を適用しましたが上手くいかず。と考えてみると、intは16進数の場合、基数を指定しないといけません。したがって、int(y,16)の形にしたいのですが、上記の表記ではそうできません。正しくは以下の通りです。

y_int = map(lambda y: int(y,16), y_array)



すると
 
 

<map object at ほにゃほにゃほにゃ


 
という結果が得られます。
 
 
mapはPython3から計算結果の数値を表示するのではなく、iterator(要素を反復して返すことの出来るインターフェース)を返すらしいです。iterationについては詳しく知らないので、困ったらまた調べます。


関数にiterationableなobjectを渡すときには展開した形で渡す必要があり、そのときにはlist()を介して渡します。ちなみに、一度iterationするとiteratorは消えます。したがって、何回もlist()を介して取り出そうとしても初回以降は空っぽの結果になってしまうことが注意点でした。


長さの取得にlen()を使います。長さを取得したらとりあえず横軸データxを生成し、グラフにプロットします。

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

sns.set()


data = pd.read_csv("ファイル名",
                   index_col=False, delimiter=r",\s*",
                   names=('c01','c02','c03','c04','c05','c06','c07','c08','c09','c10','c11','c12','c13','c14','c15','c16'),
                   engine="python")

y_series = data.iloc[:,2]
y_array = np.array(y_series)

y_int = map(lambda y: int(y,16), y_array)
y_list = list(y_int)
y_len = len(y_list)

x = np.arange(y_len)
x_list = x.tolist()

plt.plot(x_list, y_list)
plt.grid(True)
plt.show()


ひとまずこれで描画まで行きます。ついでにseabornというライブラリでグラフが綺麗になるようにしています。注意点はsns.set()を忘れないようにするところです。


無事にグラフを描けましたが、これは入り口…


値を処理して色々と処理を実装していきましょう。


と、その前にread_csvを使って一括で読み込んでいると1ファイル読み込みに数十秒かかるので高速化します。


raed_csvにはchuksizeを指定して分割読み込みする機能があります。これによって一括で確保するメモリが小さくなって高速化できるそうです。


参考にしたのは【記事2】です。


【記事2】
pandas でメモリに乗らない 大容量ファイルを上手に扱う - StatsFragments


表現は以下の通りです。

data = pd.read_csv("ファイル名",
                   index_col=False, delimiter=r",\s*",
                   names=('c01','c02','c03','c04','c05','c06','c07','c08','c09','c10','c11','c12','c13','c14','c15','c16'),
                   engine="python",
                   chunksize=10000)

 
 
読み込みは4秒くらいになりました。激速!!笑


読み込んだ結果を表示させる以下のコードを足して実行すると…

for d in data:
    print(type(d),d.shape)

 
 
結果は以下の通りです。

<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)
<class 'pandas.core.frame.DataFrame'> (10000, 16)



こんな感じで全列が読み込まれています。


ただ、今回は使う列が限られているのでこの後行を連結する前に必要な列だけを取り出す必要があります。


というこで、dataframeからの抽出は以下の通りです。

data = pd.read_csv("ファイル名",
                   index_col=False, delimiter=r",\s*",
                   names=('c01','c02','c03','c04','c05','c06','c07','c08','c09','c10','c11','c12','c13','c14','c15','c16'),
                   engine="python",
                   chunksize=10000)

for d in data:
    print(type(d),d['c01'].shape)

 
 
column名で指定してあげると良いようです。結果は以下の通り。

<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)
<class 'pandas.core.frame.DataFrame'> (10000,)



一列だけ抽出されています。


それでは、結合していきます。結合にはpnadasのconcatを使います。読み込んだdataはiteratorであり、ポインタでどこまで読み込んだかを管理できます。したがって、data内で繰り返してねという指示を出せば、読み込みが終われば処理を終わらせてくれます。

df = pd.concat((r['c01'] for r in data), ignore_index=True)


これでc01列の全結合したdataframeであるdfが得られました。


ただし、結合処理の部分で結局40秒くらいかかってしまうのでどうにかそれを回避して処理が進むようにしたいところです。


行き当たりばったりのコーディングではなく、フローチャートをしっかり描いてコーディング出来るようにしたいところです。


取り合えず、この後の処理も色々やらなければいけないのですが、csvの処理の基本ということでここまでにしておきます。
 
 
今後の課題は以下の通り。


・daskを使った並列読み込み処理を実装する
・処理のフローチャートを作る
pythonの標準配列とその他ライブラリの配列表現を行き来できるようにする
pythonのコミュニティに参加する(自力でやるのも良いけどやっぱり色々聞いたりしたい!)