##前置き
機械学習の案件を実際にこなしてみて気づくことといえば、何よりも「データの前処理がものすごく大変」ということです。
実際に組んだスケジュール全体の中でも、結構なウエイトを占めます。地味な作業なのに。
特に自然言語処理の案件などでは多いかと思いますが。お客さんからDB抽出した超大容量のcsvをドカッと渡されて「これがデータです」と。
Wind〇ws病を患う多くの開発民は「嗚呼エクセルガオモイ」と口にします。
というか、もはやデカすぎて開けません。ちょっと中を見て感覚をつかみたいだけなのに。
その瞬間からPythonの出番です。
###この記事は…
Pythonという言語と、それに付随するライブラリを使ってcsvデータをさばいていこうという趣旨のものです。
サンプルも、数行数列ではなくもう少し大きくして(10行×10列)、もう少し処理イメージがわきやすいようにしてみます。
自分が書プログラムはあくまでもルールに則った、ロジックが正しいものでなければなりませんが、その中でのデータの動きは頭の中でイメージできるように意識する必要があります。
####今回使うサンプル(sample.csv)
BLANK_ | HEADER_1 | HEADER_2 | HEADER_3 | HEADER_4 | HEADER_5 | HEADER_6 | HEADER_7 | HEADER_8 | HEADER_9 | HEADER_10 |
---|---|---|---|---|---|---|---|---|---|---|
INDEX_1 | 1_1 | 1_2 | 1_3 | 1_4 | 1_5 | 1_6 | 1_7 | 1_8 | 1_9 | 1_10 |
INDEX_2 | 2_1 | 2_2 | 2_3 | 2_4 | 2_5 | 2_6 | 2_7 | 2_8 | 2_9 | 2_10 |
INDEX_3 | 3_1 | 3_2 | 3_3 | 3_4 | 3_5 | 3_6 | 3_7 | 3_8 | 3_9 | 3_10 |
INDEX_4 | 4_1 | 4_2 | 4_3 | 4_4 | 4_5 | 4_6 | 4_7 | 4_8 | 4_9 | 4_10 |
INDEX_5 | 5_1 | 5_2 | 5_3 | 5_4 | 5_5 | 5_6 | 5_7 | 5_8 | 5_9 | 5_10 |
INDEX_6 | 6_1 | 6_2 | 6_3 | 6_4 | 6_5 | 6_6 | 6_7 | 6_8 | 6_9 | 6_10 |
INDEX_7 | 7_1 | 7_2 | 7_3 | 7_4 | 7_5 | 7_6 | 7_7 | 7_8 | 7_9 | 7_10 |
INDEX_8 | 8_1 | 8_2 | 8_3 | 8_4 | 8_5 | 8_6 | 8_7 | 8_8 | 8_9 | 8_10 |
INDEX_9 | 9_1 | 9_2 | 9_3 | 9_4 | 9_5 | 9_6 | 9_7 | 9_8 | 9_9 | 9_10 |
INDEX_10 | 10_1 | 10_2 | 10_3 | 10_4 | 10_5 | 10_6 | 10_7 | 10_8 | 10_9 | 10_10 |
エディター上ではこんな感じに書いてあります↓
BLANK_,HEADER_1,HEADER_2,HEADER_3,HEADER_4,HEADER_5,HEADER_6,HEADER_7,HEADER_8,HEADER_9,HEADER_10
INDEX_1,1_1,1_2,1_3,1_4,1_5,1_6,1_7,1_8,1_9,1_10
INDEX_2,2_1,2_2,2_3,2_4,2_5,2_6,2_7,2_8,2_9,2_10
INDEX_3,3_1,3_2,3_3,3_4,3_5,3_6,3_7,3_8,3_9,3_10
INDEX_4,4_1,4_2,4_3,4_4,4_5,4_6,4_7,4_8,4_9,4_10
INDEX_5,5_1,5_2,5_3,5_4,5_5,5_6,5_7,5_8,5_9,5_10
INDEX_6,6_1,6_2,6_3,6_4,6_5,6_6,6_7,6_8,6_9,6_10
INDEX_7,7_1,7_2,7_3,7_4,7_5,7_6,7_7,7_8,7_9,7_10
INDEX_8,8_1,8_2,8_3,8_4,8_5,8_6,8_7,8_8,8_9,8_10
INDEX_9,9_1,9_2,9_3,9_4,9_5,9_6,9_7,9_8,9_9,9_10
INDEX_10,10_1,10_2,10_3,10_4,10_5,10_6,10_7,10_8,10_9,10_10
###データの読み込み
まずはcsvモジュールから。
csvファイルを、ヘッダーもインデックスもおかまいなしに1行ずつ読み込む処理です。(出力形式はlistです)
# csvをとにかく頭から1行ずつ読み込む(csvモジュールをimportする)
with open('../data/sample.csv', 'r') as f:
# csvをとにかく頭から1行ずつ読み込む
reader = csv.reader(f)
for r in reader:
print(r)
"""
output=>
['BLANK_', 'HEADER_1', 'HEADER_2', 'HEADER_3', 'HEADER_4', 'HEADER_5', 'HEADER_6', 'HEADER_7', 'HEADER_8', 'HEADER_9', 'HEADER_10']
['INDEX_1', '1_1', '1_2', '1_3', '1_4', '1_5', '1_6', '1_7', '1_8', '1_9', '1_10']
['INDEX_2', '2_1', '2_2', '2_3', '2_4', '2_5', '2_6', '2_7', '2_8', '2_9', '2_10']
['INDEX_3', '3_1', '3_2', '3_3', '3_4', '3_5', '3_6', '3_7', '3_8', '3_9', '3_10']
['INDEX_4', '4_1', '4_2', '4_3', '4_4', '4_5', '4_6', '4_7', '4_8', '4_9', '4_10']
['INDEX_5', '5_1', '5_2', '5_3', '5_4', '5_5', '5_6', '5_7', '5_8', '5_9', '5_10']
['INDEX_6', '6_1', '6_2', '6_3', '6_4', '6_5', '6_6', '6_7', '6_8', '6_9', '6_10']
['INDEX_7', '7_1', '7_2', '7_3', '7_4', '7_5', '7_6', '7_7', '7_8', '7_9', '7_10']
['INDEX_8', '8_1', '8_2', '8_3', '8_4', '8_5', '8_6', '8_7', '8_8', '8_9', '8_10']
['INDEX_9', '9_1', '9_2', '9_3', '9_4', '9_5', '9_6', '9_7', '9_8', '9_9', '9_10']
['INDEX_10', '10_1', '10_2', '10_3', '10_4', '10_5', '10_6', '10_7', '10_8', '10_9', '10_10']
"""
'''
ただし、一回csvファイル全体を読み込んでからイテレーションをまわして1行ずつ、という挙動なので当然大容量ファイルに対しては歯が立ちません。
その場合は1行ずつ処理を行うというのが定石です。読み込んだ後に少々文字列・リストの操作が必要ですが生のPythonで書けます。
# csvをとにかく頭から1行ずつ読み込む(生python)
with open('../data/sample.csv', 'r') as f:
for line in f:
line = line.rstrip('\n').split(',')
print(line)
"""
output=>上と同じ
"""
'''
####参考:「ヘッダーとインデックスが邪魔…中身だけほしいのに…マヂムリ…」
それそれ。超わかる。csvモジュール使ってるときはこんな書き方で回避できます。
# ヘッダー・インデックス部分をスキップして残りを読み込む方法
with open('../data/sample.csv', 'r') as f:
reader = csv.reader(f)
# ヘッダー部分を抽出しておいておく
header = next(reader)
for r in reader:
# 残りの各行のインデックス部分を省く
print(r[1:])
"""
output=>
['1_1', '1_2', '1_3', '1_4', '1_5', '1_6', '1_7', '1_8', '1_9', '1_10']
['2_1', '2_2', '2_3', '2_4', '2_5', '2_6', '2_7', '2_8', '2_9', '2_10']
['3_1', '3_2', '3_3', '3_4', '3_5', '3_6', '3_7', '3_8', '3_9', '3_10']
['4_1', '4_2', '4_3', '4_4', '4_5', '4_6', '4_7', '4_8', '4_9', '4_10']
['5_1', '5_2', '5_3', '5_4', '5_5', '5_6', '5_7', '5_8', '5_9', '5_10']
['6_1', '6_2', '6_3', '6_4', '6_5', '6_6', '6_7', '6_8', '6_9', '6_10']
['7_1', '7_2', '7_3', '7_4', '7_5', '7_6', '7_7', '7_8', '7_9', '7_10']
['8_1', '8_2', '8_3', '8_4', '8_5', '8_6', '8_7', '8_8', '8_9', '8_10']
['9_1', '9_2', '9_3', '9_4', '9_5', '9_6', '9_7', '9_8', '9_9', '9_10']
['10_1', '10_2', '10_3', '10_4', '10_5', '10_6', '10_7', '10_8', '10_9', '10_10']
"""
###データの書き込み
では、csvに書き込みを行いたい場合。
# 文字列をとにかく1行ずつcsvに書き出す(生python wは上書き、aは追記)
with open('../data/sample2.csv', 'w') as f:
for i in range(10):
f.write(str(i + 1) + '行目1列目の書き込みです')
f.write(',') # csvなのでカンマで列を区切る
f.write(str(i + 1) + '行目2列目の書き込みです')
# 行の最後には改行を忘れないこと
f.write('\n')
'''
output=>
1行目1列目の書き込みです,1行目2列目の書き込みです
2行目1列目の書き込みです,2行目2列目の書き込みです
3行目1列目の書き込みです,3行目2列目の書き込みです
4行目1列目の書き込みです,4行目2列目の書き込みです
5行目1列目の書き込みです,5行目2列目の書き込みです
6行目1列目の書き込みです,6行目2列目の書き込みです
7行目1列目の書き込みです,7行目2列目の書き込みです
8行目1列目の書き込みです,8行目2列目の書き込みです
9行目1列目の書き込みです,9行目2列目の書き込みです
10行目1列目の書き込みです,10行目2列目の書き込みです
'''
csvモジュールを使って書いてみるとこんな感じ。
with open('../data/sample2.csv', 'w') as f:
writer = csv.writer(f, lineterminator='\n') # 改行コード(\n)を指定しておく
for i in range(10):
one_row = [str(i + 1) + '行目1列目の書き込みです', str(i + 1) + '行目2列目の書き込みです']
writer.writerow(one_row) # listを渡す
'''
output=>上と同じ
'''
文字列やリストの操作を逐一行わなければなりませんが、生のpythonで書く場合とcsvモジュールを使う場合で様子が変わってきます。
どっちがより優れているというよりかは、自身の状況に応じて使い分けるのがいいかと思います。それぞれに便利なポイントが違ってくるので。
…とか何とか言ってきれいに締めようとしていますが、言いたいことはこれで終わりではありません。
生pythonとcsvモジュールの違いを並べてみたうえで、次回の記事では最近データ処理で使ったPandasについて書こうと思います。
大容量ファイルをさばくのにまさにうってつけ、って感じだったので。
あと、メモリ消費との関係なんかについても言及できたらと思います。
それでは。