post Image
Pythonでcsvをいじくりまわす①


前置き

機械学習の案件を実際にこなしてみて気づくことといえば、何よりも「データの前処理がものすごく大変」ということです。

実際に組んだスケジュール全体の中でも、結構なウエイトを占めます。地味な作業なのに。

特に自然言語処理の案件などでは多いかと思いますが。お客さんから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について書こうと思います。

大容量ファイルをさばくのにまさにうってつけ、って感じだったので。

あと、メモリ消費との関係なんかについても言及できたらと思います。

それでは。


『 Python 』Article List