Fakerでデカいデータを作りたい

  • 顧客1億人
  • 商品1万件
  • 顧客がそれぞれ数個の商品を購入

というシナリオのダミーデータが必要になって、あまり本番ぽくなくて良いならPostgreSQLでgenerate_series()したりするんだけど、グラフデータとして使おうとすると、propertiesにそれなりのデータが入っていて欲しい。

でも、1億回のループを書いちゃうとくっそ遅い。下記コードのループを真面目に1億回にすると、顧客データの生成だけで5時間ぐらいかかる計算になったので、10万件生成してidだけ変える、というアルゴリズムにした。まあ、同じ顧客がアカウントを忘れて1,000回ほど再登録したと思えばw

購入履歴は元は顧客はそれぞれ1個の商品を購入するというシナリオだったのだけど、ちょっとそれっぽくならないので、ランダム数の商品を購入するようにした。

なおちょっとしたことだけれど、append()をループの中でやたらと使うとこのぐらいの規模感では影響が無視出来ないし、ifみたいな条件分岐は絶対禁止。

また、csv.writerのquotingは指定した方が速い。無い場合、csv.writerは値の中にデリミタが含まれていないかなどのチェックをすることになる。quoting=csv.QUOTE_NONEが最速だけれど、QUOTE_ALLだと有無を言わさずquoteするので、これも速い。

#!/usr/bin/env python3.11
# -*- coding: utf-8 -*-

import csv
from faker import Faker
import random
import time

fake = Faker()

# create customers
header = ['id', 'name', 'address', 'email', 'phone']
row_cnt  = 100000000
item_cnt = 100000
names = [fake.name() for i in range(item_cnt)]
addresses = [fake.address().replace('\n',' ') for i in range(item_cnt)]
emails = [fake.email() for i in range(item_cnt)]
phones = [fake.phone_number() for i in range(item_cnt)]
customers = []
for i in range(row_cnt):
    id = f"{i + 1:09}"
    rec_no = i % item_cnt
    customers.append([id, names[rec_no], addresses[rec_no], emails[rec_no], phones[rec_no]])

with open('customers.csv', 'w') as f:
    writer = csv.writer(f, quoting=csv.QUOTE_ALL)
    writer.writerow(header)
    writer.writerows(customers)
f.close()

# create products
header = ['id', 'name', 'SKU', 'price', 'specifications']
row_cnt = 10000
products = []
for i in range(row_cnt):
    id = f"{i + 1:05}"
    name = fake.catch_phrase()
    SKU = fake.ean13()
    price = round(fake.random_number(digits=5, fix_len=True) / 100, 2)
    specifications = f"Color: {fake.color_name()}; Size: {fake.random_element(elements=('S', 'M', 'L', 'XL'))}; Weight: {fake.random_int(min=100, max=1000)}g"
    products.append([id, name, SKU, price, specifications])

with open('products.csv', 'w') as f:
    writer = csv.writer(f, quoting=csv.QUOTE_ALL)
    writer.writerow(header)
    writer.writerows(products)
f.close()

# create boughts (edges)
header = ['start_id', 'start_vertex_type', 'end_id', 'end_vertex_type']
row_cnt = len(customers)
boughts = []
for i in range(row_cnt):
    start_id = customers[i][0]
    start_vertex_type = 'Customer'
    [boughts.append([start_id, start_vertex_type, fake.random_int(min=1, max=len(products)), 'Product'])  for _ in range(random.randrange(1, 5))]

with open('boughts.csv', 'w') as f:
    writer = csv.writer(f, quoting=csv.QUOTE_ALL)
    writer.writerow(header)
    writer.writerows(boughts)
f.close()