ぺーぱーふぇいす

雑記と備忘録。私はプログラマ。

【Python】pandasでCSVとかTSVとかやるカンペ

PythonCSV(TSV)ファイルの入出力用の自分用のカンニングペーパー。
ついでにカンマやタブ文字以外での区切り文字についてちょこっと。

モジュールとしてpandasを使ってDataFrameとして読み込み、主にループとかで処理する想定。
あと、補足とかはぼちぼち。

もっと詳しいDataFrameの扱いについては、別のエントリを観た方が参考になると思う。このエントリの最後にいくつか参考先を記載。

カンペスクリプト

import pandas as pd

# CSVファイルをDataFrameに
df_csv = pd.read_csv(r'test_csv001.csv')
print(df_csv)
#=>      名称        分類      製造国
#=> 0   M16  アサルトライフル  アメリカ合衆国
#=> 1  AK47  アサルトライフル   ソビエト連邦
#=> 2    G3   バトルライフル      ドイツ
#=> 3   P90       PDW     ベルギー
#=> 4    M9     ハンドガン     イタリア

# 行、列数を取得
print(u'行数:' + str(len(df_csv.index)))   #=>行数:5
print(u'列数:' + str(len(df_csv.columns))) #=>列数:3

# 0行目を取得
row = df_csv.ix[0,:]
print(row)
#=>名称          M16
#=>分類     アサルトライフル
#=>製造国     アメリカ合衆国
#=>Name: 0, dtype: object

# 列に列名でアクセス
print(row[u'製造国']) #=>アメリカ合衆国

# 列にインデックスでアクセス
print(row[1]) #=>アサルトライフル

# 行を削除
df_csv.drop(1, inplace=True)
print(df_csv)
#=>    名称        分類      製造国
#=>0  M16  アサルトライフル  アメリカ合衆国
#=>2   G3   バトルライフル      ドイツ
#=>3  P90       PDW     ベルギー
#=>4   M9     ハンドガン     イタリア

# 列を削除
df_csv.drop(u'製造国', axis=1, inplace=True)
print(df_csv)
#=>    名称        分類
#=>0  M16  アサルトライフル
#=>2   G3   バトルライフル
#=>3  P90       PDW
#=>4   M9     ハンドガン

# 列を追加
df_csv[u'使用弾薬'] = [u'5.56x45mm NATO弾', u'7.62x51mm NATO弾', u'5.7x28mm弾', u'9x19mmパラベラム弾']
print(df_csv)
#=>    名称        分類             使用弾薬
#=>0  M16  アサルトライフル  5.56x45mm NATO弾
#=>2   G3   バトルライフル  7.62x51mm NATO弾
#=>3  P90       PDW        5.7x28mm弾
#=>4   M9     ハンドガン     9x19mmパラベラム弾

# indexの振り直し
print(df_csv.reset_index(drop=True))
#=>    名称        分類             使用弾薬
#=>0  M16  アサルトライフル  5.56x45mm NATO弾
#=>1   G3   バトルライフル  7.62x51mm NATO弾
#=>2  P90       PDW        5.7x28mm弾
#=>3   M9     ハンドガン     9x19mmパラベラム弾

解説と補足と応用と

ヘッダーがない場合

# ヘッダーがない場合(列名は0からの連番となる)
df = pd.read_csv(r'test_csv001.csv', header=None)

何行か読み飛ばす場合

# 2行読み飛ばす場合
df = pd.read_csv(r'test_csv001.csv', skiprows=2)

ファイルではなく文字列を読み込む場合

import io

csv_str = u"""\
名称,分類,製造国
M16,アサルトライフル,アメリカ合衆国
AK47,アサルトライフル,ソビエト連邦
G3,バトルライフル,ドイツ
P90,PDW,ベルギー
M9,ハンドガン,イタリア
"""

pd = pd.read_csv(io.StringIO(str))

StringIOは文字列を仮想的にファイルのように扱う。
ファイルをパラメタとして指定するようなメソッドに対してよく使う。pandasの場合も同様に、StringIOで仮想ファイル化(?)したオブジェクトを渡せば、CSVフォーマットの文字列も同様にpandasでDataFrame化できる。

TSVとか他の区切り文字を読み込む場合

# TSVの場合
pd = pd.read_table(r'test_tsv.tsv')

# パイプで区切られている場合
pd = pd.read_csv(r'test_pipe.txt', delimiter='|')

# スペースもしくはタブで区切られている場合
pd = pd.read_csv(r'test_space.txt', delim_whitespace=True)

TSVのようにタブ区切りのフォーマットの場合はread_table()を使う。
パイプ記号(|)などを区切り文字としているフォーマットの場合は、区切り文字としてdelimiterに指定する。
スペースもしくはタブで区切られているフォーマットの場合は、delim_whitespace=Trueと指定する。まあ、当たり前かもしれないが全角スペースはここに含まれないのであしからず(そんなデータ作るような変態は居ないと思うけど)。

行、列へのアクセスについて

# loc[rows, columns]
df_csv.loc[:, u'名称']          #すべての行の名称
df_csv.loc['0', u'名称']        #1行目の名称
df_csv.loc[['0','2'], u'名称']  #0~2行目の名称

# iloc[rows_index, columns_index]
df_csv.iloc[:, 0]   #すべての行の名称
df_csv.iloc[1, 0]   #1行名の名称
df_csv.iloc[1:2, 0] #1~2行目の名称

# ix
df_csv.ix[1, u'名称']   #1行目の名称
df_csv.ix['1', 0]       #1行目の名称

locカラム名による指定、ilocがカラムインデックスによる指定。
カンペスクリプトで使用したixは名称、インデックスのどちらでもアクセスできるため、基本的にはixを使うとことが多いと思う。
ここまででわかるけど、DataFrameは行、列を柔軟に指定して必要な範囲を取得できる。

私のC#脳がDataFrameをDataTableみたいな表……つまりは「行の集合体」というイメージで解釈しようとするので、ロジックも「forループでくるくる回すぜ!行単位で!」って感じだけど、実際のDataFrameはもっと柔軟でいろいろと便利かつエレガントな使い方ができるがこのエントリでは書ききれないので割愛。

行と列の削除について

行、列どちらの作業ともにinplace=Trueを指定しないと、呼び出しているインスタンスから要素を消すのではなく、要素を消した状態のDataFrameを戻し値として返す
オブジェクトのインスタンスそのものに影響を与える場合は明示的に指定する。

また、axis=1が列で、axis=0が行を意味する。
axis=0の場合は省略可能なので、列の削除だけ明示的にaxis=1と指定している。

それと、行を削除した場合は行のindexがズレるので、振り直す必要がある点については注意。
for文をカウンタで回している時なんかは、この振り直すタイミングについても留意しないとかな。

行と列の追加について

まず、行の追加として、1行分のデータをSeriesオブジェクトとして作成する。
今回は既存のDataFrameに1行追加するので、同じカラム要素を持ったSeriesを作成し、それをappend()する。 他にもいろいろ方法はあるけど(DataFrame同士を結合するとか)、CSVという2次元の構造に対する操作なので、イメージ的にはこんな感じで「1行追加する」というイメージが理解しやすいかな。

次に列の追加は、DataFrame[<新規列名>] = [<新規列の値>, ...]の形で行う。ちょっと癖を感じるかもしれない。
もちろん他の方法もあるけどそれは割愛。
それとは別に、「とりあえず列を追加しておきたい」って場合は以下。数値項目だったら[0]*len(df_csv.index)とか。

# 既存レコードに空文字(empty)を追加
df_csv[u'使用弾薬'] = [u'']*len(df_csv.index)

余談

このpandasといえば、DataFrame。PythonとpandasでDataFrameといえば機械学習……みたいな感じでもあるけど、このDataFrame、機械学習で利用とか以前に普通に便利だ。
機械学習にかぎらず、CSVのようなデータをループして回すのにも、行や列にアクセスするのにも感覚的に楽。
私が元々C#erなだけあって、2次元データを扱う時によく利用していたDataTableに感覚が近いからだろうか。

DataFrameは他にも各列の型とか、いろいろなプロパティやメソッドを持っている。このエントリではDataFrame自体のカンペも実は少し兼ねているのだけど、DataFrame単体でグルーピングやソートもできるので、気が向いたらそこらへんのDataFrame自体のカンペも書くかもしれない。
ただ、「目指せ!pandas/DataFrameマスター!」的なことを志すのであれば、もっと有用なエントリがあると思うからまー別にいいかな、とも。そこら辺はだいたいQiitaあたりにどっさりあったりするので(そんなこと言ったらこのエントリみたいなCSVとpandasというのもQiitaにいっぱいあるけど)。

まあそれは別として、近いうちにExcelブックとかSQLiteに対してpandasで入出力を行うカンペは書いとこうと思う。
気が向いたら固定長とかも書くかなぁ……

参考

pandasでcsv/tsvファイル読み込み(read_csv, read_table) | Python / note.nkmk.me https://note.nkmk.me/python-pandas-read-csv-tsv/

Python pandasでのデータ操作の初歩まとめ − 前半:データ作成&操作編 - Qiita https://qiita.com/hik0107/items/d991cc44c2d1778bb82e

<Python, pandas> 行(row)を追加する。 - ねこゆきのメモ http://nekoyukimmm.hatenablog.com/entry/2016/11/16/221349