LoginSignup
34
58

More than 5 years have passed since last update.

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

Posted at

前置き

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

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

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

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

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

それでは。

34
58
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
58