Pythonで容量の大きいCSVファイルを指定したサイズの複数ファイルに分割する

2023.08.02

データアナリティクス事業本部のueharaです。

今回は、Pythonで容量の大きいCSVファイルを複数ファイルに分割してみたいと思います。

要件とテストデータ

今回、分割の要件として以下を設定したいと思います。

  • 1ファイルの上限を5MBとしてファイルの分割をする
  • 1行の途中で分割するのはNG

また、テストデータはこちらのサイトを利用して、100万件(約59.1MB)のダミーデータを作成しました。

data.csv

1,モチダ,51,090-3329-6255,1972/6/1,持田 貞
2,キリハラ,34,090-3592-4301,1989/2/6,桐原 貴美
3,イズミダ,32,080-8445-1850,1990/10/28,泉田 喜久治
(中略)
999998,ヒグチ,16,090-4806-7546,2007/5/8,樋口 利明
999999,タテヤマ,29,080-4520-0946,1993/11/25,立山 正巳
1000000,ニシムラ,4,080-8908-6323,2019/2/1,西村 裕美子

今回はこちらのデータを分割したいと思います。

対応方針

まず簡単に思いつくのは、pandasのread_csv関数のchunksizeを指定し、DataFrameに読み込む行数を絞った後にto_csv関数でcsvを順次出力していく方法です。

ただし、この方法だとあくまで行数ベースでの分割になるので、確実に5MBを超えないような行数を予め検討する必要があります。

また、データ量の分散が高いカラムがある場合、ある程度バッファを持たせる必要もあるので、そのようなケースだと平均的に出力されるデータはおおよそ4MB〜4.5MB程度になるのではないかと思います。

したがって、ここでは元データの1行ごとのデータサイズを見て、出力先のファイルが5MBを超えるようなら次のファイルに出力を行うという方針で対応したいと思います。

やってみた

data.csvと同じディレクトリにcsv_splitter.pyを作成します。

全体のコードは以下の通りです。

csv_splitter.py

import os


file_number = 0
limit = 5000000  # size limit (5 MB)
input_filename = 'data.csv'
output_dir = 'output'
output_filename_template = os.path.join(output_dir, 'output_{}.csv')

if not os.path.exists(output_dir):
    os.makedirs(output_dir)

with open(input_filename, 'r') as input_file:
    current_out_file = open(output_filename_template.format(file_number), 'a')
    current_size = 0
    for line in input_file:
        # 文字列のバイトサイズを確認
        line_size = len(line.encode('utf-8'))
        if current_size + line_size > limit:
            current_out_file.close()
            file_number += 1
            current_size = 0
            current_out_file = open(output_filename_template.format(file_number), 'a')
        current_out_file.write(line)
        current_size += line_size
    current_out_file.close()

実行結果の確認

上記Pythonスクリプトを実行すると、outputフォルダにファイルが5MB毎に出力されます。

念のため、いくつかデータの中身を確認してみます。

# 1つめのファイルの先頭3行を確認
$ head -n 3 output_0.csv  
1,モチダ,51,090-3329-6255,1972/6/1,持田 貞
2,キリハラ,34,090-3592-4301,1989/2/6,桐原 貴美
3,イズミダ,32,080-8445-1850,1990/10/28,泉田 喜久治

# 1つめのファイルの末尾3行を確認
$ tail -n 3 output_0.csv
86116,タウチ,56,080-8935-3100,1967/5/24,田内 信二
86117,シンタク,34,090-7557-1020,1989/2/12,新宅 愛菜
86118,オグリ,38,080-1645-6856,1985/6/14,小栗 正利

# 2つめのファイルの先頭3行を確認
$ head -n 3 output_1.csv
86119,カサイ,36,080-6867-5502,1987/3/22,笠井 風花
86120,ウチウミ,17,080-5301-7489,2006/3/19,内海 新平
86121,シオザワ,41,090-5030-8597,1982/3/22,塩沢 奈保子

# 最後のファイルの末尾3行を確認
$ tail -n 3 output_11.csv
999998,ヒグチ,16,090-4806-7546,2007/5/8,樋口 利明
999999,タテヤマ,29,080-4520-0946,1993/11/25,立山 正巳
1000000,ニシムラ,4,080-8908-6323,2019/2/1,西村 裕美子

行の途中でデータが切れておらず、つなぎ目のデータの連続性も問題ないことが分かります。

また、仮に元のCSVファイルにヘッダがあり、複数分割するファイルに対してもそれぞれヘッダを入れたいケースについても、本スクリプトを微修正することで対応可能です。

最後に

今回は、Pythonで容量の大きいCSVファイルを複数ファイルに分割してみました。

参考になりましたら幸いです。

参考文献