使用 Python 编写大型 CSV 的最快方法
Fastest way to write large CSV with Python
我想在 csv 文件中写入一些随机样本数据,直到它达到 1GB 大。以下代码有效:
import numpy as np
import uuid
import csv
import os
outfile = 'data.csv'
outsize = 1024 # MB
with open(outfile, 'ab') as csvfile:
wtr = csv.writer(csvfile)
while (os.path.getsize(outfile)//1024**2) < outsize:
wtr.writerow(['%s,%.6f,%.6f,%i' % (uuid.uuid4(), np.random.random()*50, np.random.random()*50, np.random.randint(1000))])
如何更快地获得它?
删除所有不必要的东西,因此它应该更快更容易理解:
import random
import uuid
outfile = 'data.csv'
outsize = 1024 * 1024 * 1024 # 1GB
with open(outfile, 'ab') as csvfile:
size = 0
while size < outsize:
txt = '%s,%.6f,%.6f,%i\n' % (uuid.uuid4(), random.random()*50, random.random()*50, random.randrange(1000))
size += len(txt)
csvfile.write(txt)
问题似乎主要与 IO 相关。您可以通过以更大的块写入文件而不是一次写入一行来稍微改进 I/O:
import numpy as np
import uuid
import os
outfile = 'data-alt.csv'
outsize = 10 # MB
chunksize = 1000
with open(outfile, 'ab') as csvfile:
while (os.path.getsize(outfile)//1024**2) < outsize:
data = [[uuid.uuid4() for i in range(chunksize)],
np.random.random(chunksize)*50,
np.random.random(chunksize)*50,
np.random.randint(1000, size=(chunksize,))]
csvfile.writelines(['%s,%.6f,%.6f,%i\n' % row for row in zip(*data)])
您可以试验 chunksize(每个块写入的行数)以查看哪种方式最适合您的机器。
这是一个基准测试,将上面的代码与您的原始代码进行比较,outsize
设置为 10 MB:
% time original.py
real 0m5.379s
user 0m4.839s
sys 0m0.538s
% time write_in_chunks.py
real 0m4.205s
user 0m3.850s
sys 0m0.351s
所以这比原始代码快了大约 25%。
PS。我尝试用对所需总行数的估计来替换对 os.path.getsize
的调用。不幸的是,它并没有提高速度。由于表示最终 int 所需的字节数各不相同,因此估计也不准确——也就是说,它不能完美地复制原始代码的行为。所以我把 os.path.getsize
留在原地。
这是基于 unutbu 上述回答的更新:
大部分时间花在生成随机数和检查文件大小上。
如果您提前生成行,您可以评估原始磁盘 io 性能:
import time
from pathlib import Path
import numpy as np
import uuid
outfile = Path('data-alt.csv')
chunksize = 1_800_000
data = [
[uuid.uuid4() for i in range(chunksize)],
np.random.random(chunksize) * 50,
np.random.random(chunksize) * 50,
np.random.randint(1000, size=(chunksize,))
]
rows = ['%s,%.6f,%.6f,%i\n' % row for row in zip(*data)]
t0 = time.time()
with open(outfile, 'a') as csvfile:
csvfile.writelines(rows)
tdelta = time.time() - t0
print(tdelta)
在我的标准 860 evo ssd(不是 nvme)上,我得到 1_800_000 行 1.43 秒,所以这是 1,258,741 rows/sec(在我看来还算不错)
我想在 csv 文件中写入一些随机样本数据,直到它达到 1GB 大。以下代码有效:
import numpy as np
import uuid
import csv
import os
outfile = 'data.csv'
outsize = 1024 # MB
with open(outfile, 'ab') as csvfile:
wtr = csv.writer(csvfile)
while (os.path.getsize(outfile)//1024**2) < outsize:
wtr.writerow(['%s,%.6f,%.6f,%i' % (uuid.uuid4(), np.random.random()*50, np.random.random()*50, np.random.randint(1000))])
如何更快地获得它?
删除所有不必要的东西,因此它应该更快更容易理解:
import random
import uuid
outfile = 'data.csv'
outsize = 1024 * 1024 * 1024 # 1GB
with open(outfile, 'ab') as csvfile:
size = 0
while size < outsize:
txt = '%s,%.6f,%.6f,%i\n' % (uuid.uuid4(), random.random()*50, random.random()*50, random.randrange(1000))
size += len(txt)
csvfile.write(txt)
问题似乎主要与 IO 相关。您可以通过以更大的块写入文件而不是一次写入一行来稍微改进 I/O:
import numpy as np
import uuid
import os
outfile = 'data-alt.csv'
outsize = 10 # MB
chunksize = 1000
with open(outfile, 'ab') as csvfile:
while (os.path.getsize(outfile)//1024**2) < outsize:
data = [[uuid.uuid4() for i in range(chunksize)],
np.random.random(chunksize)*50,
np.random.random(chunksize)*50,
np.random.randint(1000, size=(chunksize,))]
csvfile.writelines(['%s,%.6f,%.6f,%i\n' % row for row in zip(*data)])
您可以试验 chunksize(每个块写入的行数)以查看哪种方式最适合您的机器。
这是一个基准测试,将上面的代码与您的原始代码进行比较,outsize
设置为 10 MB:
% time original.py
real 0m5.379s
user 0m4.839s
sys 0m0.538s
% time write_in_chunks.py
real 0m4.205s
user 0m3.850s
sys 0m0.351s
所以这比原始代码快了大约 25%。
PS。我尝试用对所需总行数的估计来替换对 os.path.getsize
的调用。不幸的是,它并没有提高速度。由于表示最终 int 所需的字节数各不相同,因此估计也不准确——也就是说,它不能完美地复制原始代码的行为。所以我把 os.path.getsize
留在原地。
这是基于 unutbu 上述回答的更新:
大部分时间花在生成随机数和检查文件大小上。
如果您提前生成行,您可以评估原始磁盘 io 性能:
import time
from pathlib import Path
import numpy as np
import uuid
outfile = Path('data-alt.csv')
chunksize = 1_800_000
data = [
[uuid.uuid4() for i in range(chunksize)],
np.random.random(chunksize) * 50,
np.random.random(chunksize) * 50,
np.random.randint(1000, size=(chunksize,))
]
rows = ['%s,%.6f,%.6f,%i\n' % row for row in zip(*data)]
t0 = time.time()
with open(outfile, 'a') as csvfile:
csvfile.writelines(rows)
tdelta = time.time() - t0
print(tdelta)
在我的标准 860 evo ssd(不是 nvme)上,我得到 1_800_000 行 1.43 秒,所以这是 1,258,741 rows/sec(在我看来还算不错)