PandasやSQLiteで大量のデータを読み込む際の速度比較と実行によるメモリ使用率を調べてみました。検証パターンは、下記の3パターンです。
1.sqlite3のfetchall
でDBの読み込み
2.SQLiteのテーブルをpandasのread_sql_query
でDBの読み込み
3.pandasのread_csv
でCSVファイルの読み込み
目次
環境
Python 3.11.9
pandas 2.2.3
sqlite3 3.45.1
memory-profiler 0.61.0
Faker 33.1.0
検証用データ作成
カラム数 10 × 行数(10万・100万・500万)をSQLiteとCSVファイルで作成し検証しました。
import sqlite3
import csv
import random
import time
from datetime import datetime, timedelta
from faker import Faker
# 日本語のFakerインスタンスを作成
fake = Faker("ja_JP")
conn = sqlite3.connect("test.db")
cur = conn.cursor()
# 既存のテーブルを削除(存在する場合)
cur.execute("DROP TABLE IF EXISTS sample_data")
# 新しいテーブルを作成
cur.execute(
"""
CREATE TABLE sample_data (
col1 INTEGER PRIMARY KEY,
col2 TEXT,
col3 INTEGER,
col4 REAL,
col5 TEXT,
col6 TEXT,
col7 INTEGER,
col8 TEXT,
col9 REAL,
col10 TEXT
)
"""
)
with open("sample_data.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow([f"col{i}" for i in range(1, 11)]) # CSVに列名を生成
end_date = datetime.now()
start_date = end_date - timedelta(days=365)
for i in range(1, 5000001):
row_data = [
i, # ID
fake.name(), # 名前
random.randint(1000, 1000000), # 数値1
random.uniform(0.1, 100.0), # 浮動小数点
fake.date_between(start_date=start_date, end_date=end_date).strftime("%Y-%m-%d"), # 日付
random.choice(["A", "B", "C", "D", "E"]), # カテゴリ
random.randint(1, 100), # 数値2
fake.email(), # メールアドレス
random.random(), # 0-1の乱数
random.choice(["active", "inactive", "pending"]), # ステータス
]
writer.writerow(row_data) # CSVにデータ書き込み
# SQLiteへの挿入処理
sql = "INSERT INTO sample_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
cur.execute(sql, row_data)
検証用のデータ作成にはFakerライブラリを使用しました。
各パターン 検証コード
■ sqlite3 fetchall
import sqlite3
import time
from memory_profiler import profile
@profile
def fetch_data():
start = time.time()
with sqlite3.connect("test.db") as conn:
cur = conn.cursor()
sql = "SELECT * FROM sample_data"
cur.execute(sql)
cur.fetchall()
end_time = time.time()
elapsed_time = end_time - start
print(f"実行時間: {elapsed_time/60:.2f}分 ({elapsed_time:.2f}秒)")
■ pandas read_sql_query
import pandas
import sqlite3
import time
from memory_profiler import profile
@profile
def pandas_sqlite_db_read():
with sqlite3.connect("test.db") as conn:
start = time.time()
sql = "select * from sample_data"
df = pandas.read_sql_query(sql, conn)
end_time = time.time()
elapsed_time = end_time - start
print(f"実行時間:{elapsed_time/60}分({elapsed_time:.2f}秒)")
■ pandas read_csv
import pandas
import time
from memory_profiler import profile
READ_PATH = R"large_data.csv"
@profile
def pandas_csv_read():
start = time.time()
df = pandas.read_csv(READ_PATH)
end = time.time()
elapsed_time = end - start
print(f"実行時間:{elapsed_time/60}分({elapsed_time:.2f}秒)")
memory_profiler
を使用してプログラムのメモリ使用状況を確認することが可能です。
検証結果
10万件読み込み
■ sqlite3.fetchall
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 0.44秒 | 29.9 MiB | 5.6 MiB |
2回目 | 0.31秒 | 30.8 MiB | 6.5 MiB |
3回目 | 0.30秒 | 29.5 MiB | 5.0 MiB |
■ pandas.read_sql_query
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 0.46秒 | 131.9 MiB | 64.4 MiB |
2回目 | 0.47秒 | 131.8 MiB | 64.6 MiB |
3回目 | 0.47秒 | 131.9 MiB | 64.5 MiB |
■ pandas.read_csv
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 0.26秒 | 79.8 MiB | 12.2 MiB |
2回目 | 0.24秒 | 79.6 MiB | 12.3 MiB |
3回目 | 0.25秒 | 79.7 MiB | 12.4 MiB |
100万件読み込み
■ sqlite3.fetchall
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 3.42秒 | 23.8 MiB | 0.4 MiB |
2回目 | 3.32秒 | 24.5 MiB | 0.4 MiB |
3回目 | 3.30秒 | 24.3 MiB | 0.4 MiB |
■ pandas.read_sql_query
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 4.62秒 | 593.6 MiB | 526.2 MiB |
2回目 | 4.60秒 | 686.3 MiB | 619.1 MiB |
3回目 | 4.08秒 | 686.8 MiB | 619.4 MiB |
■ pandas.read_csv
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 2.13秒 | 181.5 MiB | 114.2 MiB |
2回目 | 2.01秒 | 179.5 MiB | 112.1 MiB |
3回目 | 1.88秒 | 180.3 MiB | 113.0 MiB |
500万件読み込み
■ sqlite3.fetchall
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 17.11秒 | 24.4 MiB | 0.4 MiB |
2回目 | 17.59秒 | 24.3 MiB | 0.4 MiB |
3回目 | 17.27秒 | 24.4 MiB | 0.4 MiB |
■ pandas.read_sql_query
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 51.16秒 | 2915.4 MiB | 2847.9 MiB |
2回目 | 39.05秒 | 3085.6 MiB | 3018.3 MiB |
3回目 | 40.58秒 | 2894.4 MiB | 2827.0 MiB |
■ pandas.read_csv
回数 | 実行時間 | 読み込み行でのメモリ使用量 | 読み込み行での増加量 |
---|---|---|---|
1回目 | 12.57秒 | 338.0 MiB | 270.6 MiB |
2回目 | 10.86秒 | 622.5 MiB | 555.3 MiB |
3回目 | 10.30秒 | 623.0 MiB | 555.4 MiB |
まとめ
■ sqlite3.fetchall
- 速度: 比較的速いが、大量データ(500万件)では実行時間が増加。
- メモリ使用量: 他の方法と比較してメモリ使用量の増加が非常に少なく、効率的。
■ pandas.read_sql_query
- 速度: 全てのパターンで遅い。特に大量データの場合、実行時間が顕著に増加。
- メモリ使用量: データ量に対して大幅に増加。大量データではメモリ不足になる可能性がある。
■ pandas.read_csv
- 速度: 最速(10万件~500万件で安定)。
- メモリ使用量: pandas.read_sql_queryよりも効率的だが、sqlite3.fetchallよりは高い。