大熊猫附加性能concat / append使用"更大" DataFrames

时间:2015-08-06 16:06:42

标签: python pandas

问题:我的数据存储在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')

3 个答案:

答案 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)