问题:我的数据存储在csv文件中,其中包含以下列data / id / value。我有15个文件,每个文件包含大约10-20十亿行。每个csv文件都包含一个不同的时间段,因此时间索引不重叠,但列是(新的ID不时输入,旧的消失)。我最初做的是在没有透视调用的情况下运行脚本,但后来我在本地机器上遇到内存问题(仅8GB)。由于每个文件中都有很多冗余,因此最初的枢轴似乎是一个很好的出路(大约减少2/3的数据)但现在性能开始了。如果我运行以下脚本,concat函数将运行"永远&#34 ; (我总是在一段时间后手动中断(2h>))。 Concat / append似乎在大小方面有限制(我有大约10000-20000列),或者我在这里想念一些东西?有什么建议吗?
import pandas as pd
path = 'D:\\'
data = pd.DataFrame()
#loop through list of raw file names
for file in raw_files:
data_tmp = pd.read_csv(path + file, engine='c',
compression='gzip',
low_memory=False,
usecols=['date', 'Value', 'ID'])
data_tmp = data_tmp.pivot(index='date', columns='ID',
values='Value')
data = pd.concat([data,data_tmp])
del data_tmp
编辑I:为了澄清,每个csv文件大约有10-20个行和3个列,在应用数据后,这会减少到大约2000行,但会导致10000列。
我可以通过简单地将整组id分成子集并根据每个子集运行所需的计算来解决内存问题,因为它们对于每个id是独立的。我知道它让我重新加载相同的文件n次,其中n是使用的子集数量,但这仍然是合理的快速。我仍然想知道为什么追加没有表现。
编辑II:我尝试使用模拟重建文件结构,该模拟尽可能接近实际的数据结构。我希望很明显,我没有花费太多时间来最小化模拟时间,但它在我的机器上运行速度合理。
import string
import random
import pandas as pd
import numpy as np
import math
# Settings :-------------------------------
num_ids = 20000
start_ids = 4000
num_files = 10
id_interval = int((num_ids-start_ids)/num_files)
len_ids = 9
start_date = '1960-01-01'
end_date = '2014-12-31'
run_to_file = 2
# ------------------------------------------
# Simulation column IDs
id_list = []
# ensure unique elements are of size >num_ids
for x in range(num_ids + round(num_ids*0.1)):
id_list.append(''.join(
random.choice(string.ascii_uppercase + string.digits) for _
in range(len_ids)))
id_list = set(id_list)
id_list = list(id_list)[:num_ids]
time_index = pd.bdate_range(start_date,end_date,freq='D')
chunk_size = math.ceil(len(time_index)/num_files)
data = []
# Simulate files
for file in range(0, run_to_file):
tmp_time = time_index[file * chunk_size:(file + 1) * chunk_size]
# TODO not all cases cover, make sure ints are obtained
tmp_ids = id_list[file * id_interval:
start_ids + (file + 1) * id_interval]
tmp_data = pd.DataFrame(np.random.standard_normal(
(len(tmp_time), len(tmp_ids))), index=tmp_time,
columns=tmp_ids)
tmp_file = tmp_data.stack().sortlevel(1).reset_index()
# final simulated data structure of the parsed csv file
tmp_file = tmp_file.rename(columns={'level_0': 'Date', 'level_1':
'ID', 0: 'Value'})
# comment/uncomment if pivot takes place on aggregate level or not
tmp_file = tmp_file.pivot(index='Date', columns='ID',
values='Value')
data.append(tmp_file)
data = pd.concat(data)
# comment/uncomment if pivot takes place on aggregate level or not
# data = data.pivot(index='Date', columns='ID', values='Value')
答案 0 :(得分:12)
使用可重现的示例代码,我确实可以确认只有两个数据帧的concat
需要很长时间。但是,如果您首先对齐它们(使列名相等),那么连接速度非常快:
In [94]: df1, df2 = data[0], data[1]
In [95]: %timeit pd.concat([df1, df2])
1 loops, best of 3: 18min 8s per loop
In [99]: %%timeit
....: df1b, df2b = df1.align(df2, axis=1)
....: pd.concat([df1b, df2b])
....:
1 loops, best of 3: 686 ms per loop
两种方法的结果是相同的 对齐方式相当于:
common_columns = df1.columns.union(df2.columns)
df1b = df1.reindex(columns=common_columns)
df2b = df2.reindex(columns=common_columns)
因此,当处理完整的数据帧列表时,这可能是更容易使用的方法。
pd.concat
较慢的原因是因为它做得更多。例如。当列名不相等时,它会检查每个列是否必须上升dtype或不保持NaN值(通过对齐列名引入)。通过调整自己,你可以跳过这个。但是在这种情况下,你确定拥有所有相同的dtype,这没有问题
那么这么多慢得让我感到惊讶,但我会提出一个问题。
答案 1 :(得分:2)
总结,取决于设置的三个关键性能驱动因素:
1)确保连接两个数据帧时数据类型相同
2)尽可能使用基于整数的列名
3)使用基于字符串的列时,请确保在按照joris的建议调用concat之前使用align方法
答案 2 :(得分:1)
正如@joris所提到的,您应该将所有数据透视表附加到列表中,然后将它们全部连接起来。以下是对代码的建议修改:
dfs = []
for file in raw_files:
data_tmp = pd.read_csv(path + file, engine='c',
compression='gzip',
low_memory=False,
usecols=['date', 'Value', 'ID'])
data_tmp = data_tmp.pivot(index='date', columns='ID',
values='Value')
dfs.append(data_tmp)
del data_tmp
data = pd.concat(dfs)