为什么DataFrame的连接速度会呈指数级增长?

时间:2016-04-08 00:34:49

标签: python performance pandas concatenation processing-efficiency

我有一个处理DataFrame的函数,主要是将数据处理到存储桶中,使用pd.get_dummies(df[col])在特定列中创建特征的二进制矩阵。

为了避免一次使用此函数处理我的所有数据(内存不足并导致iPython崩溃),我使用以下方法将大型DataFrame分解为块:

chunks = (len(df) / 10000) + 1
df_list = np.array_split(df, chunks)

pd.get_dummies(df)会根据df[col]的内容自动创建新列,这些列可能会因df中的每个df_list而有所不同。

处理完毕后,我使用以下方法将DataFrame连接在一起:

for i, df_chunk in enumerate(df_list):
    print "chunk", i
    [x, y] = preprocess_data(df_chunk)
    super_x = pd.concat([super_x, x], axis=0)
    super_y = pd.concat([super_y, y], axis=0)
    print datetime.datetime.utcnow()

第一个块的处理时间是完全可以接受的,然而,它每个块增长!这与preprocess_data(df_chunk)没有关系,因为没有理由增加它。由于调用pd.concat()

,是否会增加时间

请参阅以下日志:

chunks 6
chunk 0
2016-04-08 00:22:17.728849
chunk 1
2016-04-08 00:22:42.387693 
chunk 2
2016-04-08 00:23:43.124381
chunk 3
2016-04-08 00:25:30.249369
chunk 4
2016-04-08 00:28:11.922305
chunk 5
2016-04-08 00:32:00.357365

是否有解决方法来加快速度?我有2900个块要处理,所以任何帮助都表示赞赏!

打开Python中的任何其他建议!

2 个答案:

答案 0 :(得分:28)

永远不要在for循环中调用DataFrame.appendpd.concat。它导致二次复制。

pd.concat会返回一个新的DataFrame。必须为新的空间分配空间 DataFrame和旧DataFrame中的数据必须复制到新的DataFrame中 数据帧。考虑for-loop中此行所需的复制量(假设每个x的大小为1):

super_x = pd.concat([super_x, x], axis=0)

| iteration | size of old super_x | size of x | copying required |
|         0 |                   0 |         1 |                1 |
|         1 |                   1 |         1 |                2 |
|         2 |                   2 |         1 |                3 |
|       ... |                     |           |                  |
|       N-1 |                 N-1 |         1 |                N |

1 + 2 + 3 + ... + N = N(N-1)/2。因此需要O(N**2)个副本 完成循环。

现在考虑

super_x = []
for i, df_chunk in enumerate(df_list):
    [x, y] = preprocess_data(df_chunk)
    super_x.append(x)
super_x = pd.concat(super_x, axis=0)

附加到列表是O(1)操作,不需要复制。现在 循环完成后,只有一次调用pd.concat。这个电话 由于pd.concat包含super_xN需要制作N份副本 大小为1的DataFrame。因此,当以这种方式构造时,super_x需要O(N) 副本。

答案 1 :(得分:7)

每次连接时,都会返回数据的副本。

您希望保留一个列表,然后将所有内容连接起来作为最后一步。

df_x = []
df_y = []
for i, df_chunk in enumerate(df_list):
    print "chunk", i
    [x, y] = preprocess_data(df_chunk)
    df_x.append(x)
    df_y.append(y)

super_x = pd.concat(df_x, axis=0)
del df_x  # Free-up memory.
super_y = pd.concat(df_y, axis=0)
del df_y  # Free-up memory.