Python Pandas append performance concat/append使用;“较大的”;数据帧

Python Pandas append performance concat/append使用;“较大的”;数据帧,python,pandas,Python,Pandas,问题是:我的数据存储在csv文件中,包含以下列data/id/value。我有15个文件,每个文件包含大约10-20mio行。每个csv文件都覆盖一个不同的时间段,因此时间索引不重叠,但列是重叠的(新ID不时输入,旧ID消失)。我最初做的是在没有pivot调用的情况下运行脚本,但后来我在本地计算机上遇到了内存问题(只有8GB)。由于每个文件中都有大量冗余,pivot起初看起来是一个不错的解决方案(大约少了2/3的数据),但现在性能开始发挥作用。如果我运行以下脚本,concat函数将“永远”运行(

问题是:我的数据存储在csv文件中,包含以下列data/id/value。我有15个文件,每个文件包含大约10-20mio行。每个csv文件都覆盖一个不同的时间段,因此时间索引不重叠,但列是重叠的(新ID不时输入,旧ID消失)。我最初做的是在没有pivot调用的情况下运行脚本,但后来我在本地计算机上遇到了内存问题(只有8GB)。由于每个文件中都有大量冗余,pivot起初看起来是一个不错的解决方案(大约少了2/3的数据),但现在性能开始发挥作用。如果我运行以下脚本,concat函数将“永远”运行(我总是在一段时间后手动中断(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-20mio行和三列,在应用pivot后,这将减少到大约2000行,但会导致10000列

我可以通过简单地将整个id集拆分为子集来解决内存问题,并基于每个子集运行所需的计算,因为它们对每个id都是独立的。我知道这会使我重新加载相同的文件n次,其中n是使用的子集数,但这仍然是合理的快速。我仍然想知道为什么append没有执行

编辑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')

正如@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)

使用您的可复制示例代码,我确实可以确认只有两个数据帧的
concat
需要很长时间。但是,如果先将它们对齐(使列名相等),则concatting非常快:

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
之所以较慢,是因为它做得更多。例如,当列名不相等时,它会检查每一列的数据类型是否必须向上转换或不保存NaN值(通过对齐列名引入)。通过调整你自己,你跳过了这个。但是在这种情况下,如果您确定所有的数据类型都相同,那么这就没有问题了。

速度如此之慢也让我感到惊讶,但我将提出一个问题。

总结,三个关键性能驱动因素取决于设置:

1) 连接两个数据帧时,确保数据类型相同

2) 如果可能,请使用基于整数的列名


3) 使用基于字符串的列时,请确保在调用concat之前使用align方法,如joris所建议的那样。如果要在循环中追加或浓缩(并且循环是不可避免的),则至少应将
数据\u tmp
追加到列表中,然后在循环后一次性将它们全部浓缩这不会对性能产生太大的影响在这种情况下,我认为问题会出现,因为数据帧的大小(包括列和行维度),如果它由于内存而能够运行的话(因为它可以防止复制增长的数据帧15次)。但我并没有完全明白这一点。你说你有15个乘以10-20mio的行和10000-20000个列,这怎么会适合8GB内存呢?(一个快速的计算告诉我至少在TBs中是这样)为了弄清楚,原始csv文件有大约10mio行和三列。然后,Pivot通过在行和列索引中唯一地存储日期/id信息来减少冗余。然而,它最终有大约10000-20000列。当我只附加两个较大的数据透视表时,问题实际上已经出现了。如果我在一个列表中收集这两个数据帧(如建议的那样),则先对它们进行透视,然后仅对其进行透视,速度相当快(由于内存限制,无法对完整的文件列表执行此操作),但如果我先对每个表进行透视,然后再对其进行append/concat,则它将以非线性方式运行。谢谢。我想到了两件事:首先,当你用一个整数索引替换id\u列表时(只需添加
id\u list=range(0,num\u id)
与模拟的ID不同,它对性能有很大的影响。其次,我意识到我在原始数据中也有一些错误代码,因此数据类型为object类型,清理数据并仅使用浮点数,性能也会提高一个更大的因数。您建议的对齐也会提高性能,但只有在列或strin类型为当你使用整型列的时候,g就不再那么多了。也许有人改变了这个发现,在Pandash中可能值得改进。你有机会看看我的评论吗?