为啥 Pandas 串联 (pandas.concat) 的内存效率如此之低?
Posted
技术标签:
【中文标题】为啥 Pandas 串联 (pandas.concat) 的内存效率如此之低?【英文标题】:Why is Pandas Concatenation (pandas.concat) so Memory Inefficient?为什么 Pandas 串联 (pandas.concat) 的内存效率如此之低? 【发布时间】:2015-06-30 15:56:21 【问题描述】:我尝试将大约 30 GB 的数据(在大约 900 个数据帧的列表中)连接在一起。我正在使用的机器是一个功能适中的 Linux Box,内存约为 256 GB。但是,当我尝试连接我的文件时,我很快就用完了可用的内存。我已经尝试了各种解决方法来解决这个问题(用 for 循环等小批量连接),但我仍然无法将它们连接起来。两个问题浮现在脑海:
有没有其他人处理过这个问题并找到了有效的解决方法?我不能使用直接追加,因为我需要 pd.concat()
中的 join='outer'
参数的“列合并”(因为缺少更好的词)功能。
为什么 Pandas 连接(我知道它只是调用 numpy.concatenate
)对内存的使用效率如此之低?
我还应该注意,我认为问题不是列的爆炸,因为将 100 个数据帧连接在一起会产生大约 3000 个列,而基本数据帧大约有 1000 个。
编辑:
我正在处理的数据是我的 900 个数据帧中的每一个数据帧大约 1000 列宽和大约 50,000 行深的财务数据。从左到右的数据类型有:
-
字符串格式的日期,
string
np.float
int
... 以此类推。我将列名与外部连接连接起来,这意味着df2
中不在df1
中的任何列都不会被丢弃,而是被分流到一边。
示例:
#example code
data=pd.concat(datalist4, join="outer", axis=0, ignore_index=True)
#two example dataframes (about 90% of the column names should be in common
#between the two dataframes, the unnamed columns, etc are not a significant
#number of the columns)
print datalist4[0].head()
800_1 800_2 800_3 800_4 900_1 900_2 0 2014-08-06 09:00:00 BEST_BID 1117.1 103 2014-08-06 09:00:00 BEST_BID
1 2014-08-06 09:00:00 BEST_ASK 1120.0 103 2014-08-06 09:00:00 BEST_ASK
2 2014-08-06 09:00:00 BEST_BID 1106.9 11 2014-08-06 09:00:00 BEST_BID
3 2014-08-06 09:00:00 BEST_ASK 1125.8 62 2014-08-06 09:00:00 BEST_ASK
4 2014-08-06 09:00:00 BEST_BID 1117.1 103 2014-08-06 09:00:00 BEST_BID
900_3 900_4 1000_1 1000_2 ... 2400_4 0 1017.2 103 2014-08-06 09:00:00 BEST_BID ... NaN
1 1020.1 103 2014-08-06 09:00:00 BEST_ASK ... NaN
2 1004.3 11 2014-08-06 09:00:00 BEST_BID ... NaN
3 1022.9 11 2014-08-06 09:00:00 BEST_ASK ... NaN
4 1006.7 10 2014-08-06 09:00:00 BEST_BID ... NaN
_1 _2 _3 _4 _1.1 _2.1 _3.1 _4.1 0 #N/A Invalid Security NaN NaN NaN #N/A Invalid Security NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN
dater
0 2014.8.6
1 2014.8.6
2 2014.8.6
3 2014.8.6
4 2014.8.6
[5 rows x 777 columns]
print datalist4[1].head()
150_1 150_2 150_3 150_4 200_1 200_2 0 2013-12-04 09:00:00 BEST_BID 1639.6 30 2013-12-04 09:00:00 BEST_ASK
1 2013-12-04 09:00:00 BEST_ASK 1641.8 133 2013-12-04 09:00:08 BEST_BID
2 2013-12-04 09:00:01 BEST_BID 1639.5 30 2013-12-04 09:00:08 BEST_ASK
3 2013-12-04 09:00:05 BEST_BID 1639.4 30 2013-12-04 09:00:08 BEST_ASK
4 2013-12-04 09:00:08 BEST_BID 1639.3 133 2013-12-04 09:00:08 BEST_BID
200_3 200_4 250_1 250_2 ... 2500_1 0 1591.9 133 2013-12-04 09:00:00 BEST_BID ... 2013-12-04 10:29:41
1 1589.4 30 2013-12-04 09:00:00 BEST_ASK ... 2013-12-04 11:59:22
2 1591.6 103 2013-12-04 09:00:01 BEST_BID ... 2013-12-04 11:59:23
3 1591.6 133 2013-12-04 09:00:04 BEST_BID ... 2013-12-04 11:59:26
4 1589.4 133 2013-12-04 09:00:07 BEST_BID ... 2013-12-04 11:59:29
2500_2 2500_3 2500_4 Unnamed: 844_1 Unnamed: 844_2 0 BEST_ASK 0.35 50 #N/A Invalid Security NaN
1 BEST_ASK 0.35 11 NaN NaN
2 BEST_ASK 0.40 11 NaN NaN
3 BEST_ASK 0.45 11 NaN NaN
4 BEST_ASK 0.50 21 NaN NaN
Unnamed: 844_3 Unnamed: 844_4 Unnamed: 848_1 dater
0 NaN NaN #N/A Invalid Security 2013.12.4
1 NaN NaN NaN 2013.12.4
2 NaN NaN NaN 2013.12.4
3 NaN NaN NaN 2013.12.4
4 NaN NaN NaN 2013.12.4
[5 rows x 850 columns]
【问题讨论】:
您能否提供更多信息:行数、每个数据帧的列数。每个字段的类型是什么。你正在加入什么。 @sfortney 如果您可以添加一个小的、完整的、可运行的示例来展示您的代码是如何工作的,那将会有所帮助。然后我们都会在同一个页面上。您可以手动编码或随机生成三个或四个小的 DataFrame,并以代码的形式准确显示您是如何连接它们的。有关类似示例,请参阅此最近的问题:***.com/q/29629821/553404 一般来说,顺序连接子数组是生成单个大数组的慢速方法。连接a
和b
涉及分配一个新的输出数组,其大小为a
和b
的总和。随着您的输出数组变得越来越长,分配每个新数组的成本也越来越大。最好预先分配一个与整个输出数组大小相同的数组,然后在执行过程中填充行。
是的 - 请参阅 StringBuilder 类的类似案例yoda.arachsys.com/csharp/stringbuilder.html
另一个 pandas 特有的点 - 你肯定希望将 copy=False
传递给 pd.concat()
以避免生成不必要的副本
【参考方案1】:
在将大量 DataFrame 连接到“不断增长的”DataFrame 时,我遇到了性能问题。我的解决方法是将所有子 DataFrames 附加到一个列表中,然后在子 DataFrames 的处理完成后连接 DataFrames 列表。
【讨论】:
这实际上是我目前的解决方法。它似乎工作正常,但我想知道是否有更好的方法。谢谢! 这将我的运行时间缩短了一半。谢谢! 这对我根本不起作用。当你有一个数据框列表并且你想连接行(将行添加到具有相同列的数据框)时,你必须做 pd.concat(my_list, axis=1) 吗? @Konradaxis=0
将它们堆叠在一起。
这个想法对我不起作用。我有将近 600 万行,我将它们分成 1,000 行块,根据这个:deo.im/2016/09/22/Load-data-from-mongodb-to-Pandas-DataFrame。它运行良好,但是当我到达连接点时,它只会卡住我的电脑。有什么想法吗?【参考方案2】:
看起来您正在尝试按行连接,即使您的文字表明您是按列连接的。指定axis=1
。
其他需要考虑的点:
copy=False
标志根本没有帮助;这仅在您不连接相同 dtype 的块时才重要(您表示您是)。
pd.concat
确实在后台使用np.concatenate
。如果你认为你可以做得更好,那就去吧。
def make_frames(n=100, rows=100, cols=100):
return [ pd.DataFrame(np.random.randn(rows,cols),columns=np.random.choice(110,100,replace=False)) for i in xrange(n) ]
In [28]: l = make_frames(rows=10000)
In [29]: l[0].head()
Out[29]:
60 75 101 103 87 29 10 106 71 26 30 83 2 28 99 85 88 62 58 18 42 1 105 25 34 ... 102 27 22 \
0 -0.854117 -0.007549 -0.510359 -0.993757 0.877635 -0.303199 -1.488548 1.179360 0.578095 0.807792 0.169930 -1.781403 0.204696 -0.515057 -0.954246 1.106073 0.666516 -1.146988 1.335709 0.362838 -0.675379 1.483469 0.670385 -0.483312 -0.703795 ... 1.322645 -1.942183 1.053502
1 2.057542 0.860946 -0.037665 -0.347265 0.152562 -0.859537 1.431045 1.306419 0.623013 1.192325 0.909597 1.710507 1.319330 -0.402874 1.749581 1.223489 0.036354 0.140255 0.844330 -0.091447 -0.347245 0.259055 1.187882 -0.216858 -1.421336 ... 1.122068 0.887538 0.205854
2 -0.077974 0.947503 0.688666 0.288104 -1.275329 -0.840847 -2.014090 -1.318507 -0.889416 -0.098005 0.055492 0.847597 -1.289428 -0.910093 0.201312 -1.699879 0.103062 -1.041608 0.379171 -1.089937 0.894626 -1.500215 -0.501182 0.042078 -0.840789 ... 0.539192 0.193256 0.196138
3 0.291993 1.138577 1.061509 0.856553 1.118931 0.725806 -0.689776 1.337957 -1.009835 -0.976506 -0.392317 0.295876 0.092240 0.418201 0.473585 0.013809 -1.169947 0.424797 0.019051 -0.526189 0.066991 -0.268750 1.277004 -0.736560 -0.314987 ... 0.272045 -0.333272 0.573267
4 -2.073985 -0.016950 -1.712770 0.286212 -0.159693 -0.495864 1.286450 -1.168880 1.031456 -3.080568 1.443880 -0.604405 0.406383 -0.162986 1.077255 1.160726 0.943949 -1.517681 -1.049972 1.208850 -0.859617 -0.145358 -0.638898 0.248012 -2.985845 ... -0.699697 0.051352 0.575304
69 76 91 45 14 37 0 81 38 72 107 11 5 73 70 8 90 94 53 3 55 12
0 -0.972965 -0.298674 1.283482 2.344092 -0.597735 -0.407978 0.971726 -0.935620 0.236889 -0.957096 -2.366399 -0.943760 0.293325 -0.240385 -0.392554 -0.887556 0.261402 -2.050122 -1.776865 -1.513899 -0.953916 0.630495
1 -1.471033 0.269830 -0.744507 -0.982779 0.624527 -1.782704 1.197262 -0.297730 1.122939 -1.039226 0.171351 -0.828985 0.698245 0.563430 0.718177 0.682369 1.415918 0.049931 0.648000 1.785455 -0.190021 -1.329753
2 -1.942792 0.560981 -0.353782 -1.637407 -1.495131 -0.593041 -1.617116 -0.910257 -0.506877 0.178378 -0.623986 0.302544 0.279309 -0.266409 0.780306 0.986510 -1.549847 0.063632 -0.480434 1.393221 -1.237682 1.577320
3 0.468151 -1.002872 -0.147329 -0.420609 0.183696 0.527632 0.018911 -2.059989 1.642613 -0.428345 1.350693 -1.323321 -0.247263 0.331525 -2.036862 -2.593575 0.362101 -0.184095 0.419231 -0.633878 0.097499 -0.026044
4 -0.581330 -0.848421 -0.682027 -1.260004 -0.357354 -0.304743 0.409537 -1.189925 -0.609352 -0.610345 -0.798009 0.219822 -0.681764 1.872736 1.738017 0.439148 1.012881 -0.934613 -1.007427 -0.390359 0.329949 0.486906
[5 rows x 100 columns]
Concat,注意使用axis=1
,因为这是按列连接。
In [31]: df = pd.concat(l,axis=1,ignore_index=True)
In [32]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 10000 entries, 0 to 9999
Columns: 10000 entries, 0 to 9999
dtypes: float64(10000)
memory usage: 763.0 MB
时间
In [33]: %timeit pd.concat(l,axis=1,ignore_index=True)
1 loops, best of 3: 1.15 s per loop
In [34]: %memit pd.concat(l,axis=1,ignore_index=True)
peak memory: 2390.25 MiB, increment: 651.28 MiB
【讨论】:
其实axis=0才是正确的选择。在上面的 cmets 中,我发布了一个演示所需输出的链接。我还对这两个选项进行了一些测试,以确保 axis=0 是正确的选择。抱歉,如果不清楚。 为了有效利用内存,您可以对所有 dtype 对象使用类别类型。 @rajeshcis 不完全正确。如果其中少于 50% 是重复的,我不会将它们转换为分类以上是关于为啥 Pandas 串联 (pandas.concat) 的内存效率如此之低?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我使用 modin.pandas 比使用 Pandas 需要更长的时间 [ray]