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です)

```python:
# 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で書けます。

```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モジュール使ってるときはこんな書き方で回避できます。

```python:
        # ヘッダー・インデックス部分をスキップして残りを読み込む方法
        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に書き込みを行いたい場合。

```python:
# 文字列をとにかく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モジュールを使って書いてみるとこんな感じ。

```python:
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について書こうと思います。


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


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


それでは。