问题
我有一个名为data.csv
的csv文件。在每一行上我有:
timestamp: int
account_id: int
data: float
字符串
例如:
timestamp,account_id,value
10,0,0.262
10,0,0.111
13,1,0.787
14,0,0.990
型
- 此文件按时间戳排序。
- 行数太大,无法在内存中存储所有行。
- 数量级:100 M行,帐户数:5 M
如何快速获取给定account_id的所有行?让account_id访问数据的最佳方法是什么?
Things I tried
生成样本:
N_ROW = 10**6
N_ACCOUNT = 10**5
# Generate data to split
with open('./data.csv', 'w') as csv_file:
csv_file.write('timestamp,account_id,value\n')
for timestamp in tqdm.tqdm(range(N_ROW), desc='writing csv file to split'):
account_id = random.randint(1,N_ACCOUNT)
data = random.random()
csv_file.write(f'{timestamp},{account_id},{data}\n')
# Clean result folder
if os.path.isdir('./result'):
shutil.rmtree('./result')
os.mkdir('./result')
型
方案一
编写一个脚本,为每个帐户创建一个文件,在原始csv上逐个读取行,在与帐户对应的文件上写入行(为每行打开和关闭一个文件)。
验证码:
# Split the data
p_bar = tqdm.tqdm(total=N_ROW, desc='splitting csv file')
with open('./data.csv') as data_file:
next(data_file) # skip header
for row in data_file:
account_id = row.split(',')[1]
account_file_path = f'result/{account_id}.csv'
file_opening_mode = 'a' if os.path.isfile(account_file_path) else 'w'
with open(account_file_path, file_opening_mode) as account_file:
account_file.write(row)
p_bar.update(1)
型
问题:
它是相当慢的(我认为这是低效的打开和关闭一个文件上的每一行).它需要大约4分钟的1 M行.即使它的工作,它会快吗?给定一个account_id我知道我应该读的文件的名称,但系统必须查找超过5 M文件找到它.我应该创建某种二叉树与文件夹的叶子是文件?
解决方案2(适用于小示例,不适用于大csv文件)
与解决方案1相同的想法,但不是为每行打开/关闭文件,而是将文件存储在字典中
验证码:
# A dict that will contain all files
account_file_dict = {}
# A function given an account id, returns the file to write in (create new file if do not exist)
def get_account_file(account_id):
file = account_file_dict.get(account_id, None)
if file is None:
file = open(f'./result/{account_id}.csv', 'w')
account_file_dict[account_id] = file
file.__enter__()
return file
# Split the data
p_bar = tqdm.tqdm(total=N_ROW, desc='splitting csv file')
with open('./data.csv') as data_file:
next(data_file) # skip header
for row in data_file:
account_id = row.split(',')[1]
account_file = get_account_file(account_id)
account_file.write(row)
p_bar.update(1)
型
问题:
我不确定它实际上更快。我必须同时打开5 M文件(每个帐户一个)。我得到一个错误OSError: [Errno 24] Too many open files: './result/33725.csv'
。
解决方案3(适用于小示例,不适用于大csv文件)
使用awk
命令,解决方案来自:split large csv text file based on column value
验证码:
生成文件后,运行:awk -F, 'NR==1 {h=$0; next} {f="./result/"$2".csv"} !($2 in p) {p[$2]; print h > f} {print >> f}' ./data.csv
问题:
我得到以下错误:input record number 28229, file ./data.csv source line number 1
(数字28229是一个例子,它通常在28 k左右失败)。我想这也是因为我打开了太多文件
3条答案
按热度按时间vc9ivgsu1#
@:
虽然不完全是
15 GB
,但我确实有一个7.6 GB
,有3列:--
148 mn
素数,它们的base-2 log
和它们的hex
字符串
|
型
|
型
gawk
的5个示例和big-integer
包gnu-GMP
,每个示例都具有指定的素数前导位子集,--它设法在 *2分6秒 * 内计算出这些素数的全精度平方,产生一个未排序的
13.2 GB
输出文件。如果它能很快地平方,那么仅仅按
account_id
分组应该是在公园里散步dldeef672#
1.看看https://docs.python.org/3/library/sqlite3.html你可以导入数据,创建所需的索引,然后正常运行查询。除了python本身之外,没有依赖关系。
1.如果你每次都必须查询原始数据,并且你只受到简单python的限制,那么你可以写一段代码来手动读取它并产生匹配的行,或者使用这样的助手:
字符串
但是,这不会比使用
csv.reader
阅读100 M行的csv文件快。noj0wjuj3#
使用DuckDB,完整的100 M行CSV文件(1.8GB)可以轻松地放入普通桌面计算机的内存中,并在几秒钟内运行任何SQL查询
以下是几个步骤:
字符串
对于演示,我使用以下命令生成数据
型
在内存中生成一个具有1亿行的表需要8秒
SUMMARIZE命令可以显示此表中某些统计信息
型
| 列名|柱式|min| Max|近似唯一|avg| STD| Q25| Q50| Q75|计数|空百分比|
| --|--|--|--|--|--|--|--|--|--|--|--|
| 时间戳|UINTEGER| 0 | 99999 | 99492 |49999.41741353| 28866.929417142343| 25033 | 50009 | 74979 | 100000000 |0.0%|
| 帐户ID| UINTEGER| 0 | 59999 | 59997 |30001.15393498| 17322.073797863097| 15009 | 29984 | 44990 | 100000000 |0.0%|
| 数据|浮子|0.0|一千点| 99244 |499.94853794041455| 288.66460489423315| 250.03180520538703| 500.3373948166276| 749.9874392136103| 100000000 |0.0%|
型
您可以导出完整的CSV文件或仅导出特定帐户的一部分
型
这是我电脑上的内存使用情况(不到2GB!)
| 数据库名|数据库大小|块大小|总区块数|旧块|自由块|壁尺寸|存储器使用|记忆极限|
| --|--|--|--|--|--|--|--|--|
| 存储器|0字节| 0 | 0 | 0 | 0 |0字节| 1.9GB | 26.8GB |