使用pandas,从一个DataFrame复制列到另一个DataFrame的最快方法是什么?

8

我有一个大型数据帧(超过一百万条记录),用于存储我的核心数据(类似于数据库),然后我有一个较小的数据帧(1到2000条记录),我将每个时间步长中的几列组合起来,这可能会有几千个时间步骤。两个数据帧都是通过id列以相同的方式进行索引的。

我正在使用的代码是:

df_large.loc[new_ids, core_cols] = df_small.loc[new_ids, core_cols]

这里的core_cols是我要复制的大约10个字段的列表,new_ids是小数据帧中的id。这段代码可以正常运行,但是它是我的代码中速度最慢的部分,比其他部分慢了三倍。我只是想知道是否有更快的方法将两个数据帧的数据合并在一起。

我尝试每次使用merge函数合并数据,但是这个过程时间太长,所以我创建了一个更大的DataFrame并进行更新来提高速度。

3个回答

14

使用 .loc 对齐帧进行设置并不 intrinsically 缓慢,虽然它确实要经过一些代码来 cover 很多 case,所以在紧密循环中使用可能不是理想的选择。FYI,此示例与第二个示例略有不同。

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: from pandas import DataFrame

In [4]: df = DataFrame(1.,index=list('abcdefghij'),columns=[0,1,2])

In [5]: df
Out[5]: 
   0  1  2
a  1  1  1
b  1  1  1
c  1  1  1
d  1  1  1
e  1  1  1
f  1  1  1
g  1  1  1
h  1  1  1
i  1  1  1
j  1  1  1

[10 rows x 3 columns]

In [6]: df2 = DataFrame(0,index=list('afg'),columns=[1,2])

In [7]: df2
Out[7]: 
   1  2
a  0  0
f  0  0
g  0  0

[3 rows x 2 columns]

In [8]: df.loc[df2.index,df2.columns] = df2

In [9]: df
Out[9]: 
   0  1  2
a  1  0  0
b  1  1  1
c  1  1  1
d  1  1  1
e  1  1  1
f  1  0  0
g  1  0  0
h  1  1  1
i  1  1  1
j  1  1  1

[10 rows x 3 columns]

这里提供一种替代方案,它可能符合您的数据模式,如果更新(您的小框架)几乎是独立的,则可以使用这种方法(如果您不是在更新大框架,然后挑选一个新的子框架进行更新等 - 如果这是您的模式,则使用.loc是正确的)。

不要更新大框架,而是使用来自大框架的列更新小框架,例如:

In [10]: df = DataFrame(1.,index=list('abcdefghij'),columns=[0,1,2])

In [11]: df2 = DataFrame(0,index=list('afg'),columns=[1,2])

In [12]: needed_columns = df.columns-df2.columns

In [13]: df2[needed_columns] = df.reindex(index=df2.index,columns=needed_columns)

In [14]: df2
Out[14]: 
   1  2  0
a  0  0  1
f  0  0  1
g  0  0  1

[3 rows x 3 columns]

In [15]: df3 = DataFrame(0,index=list('cji'),columns=[1,2])

In [16]: needed_columns = df.columns-df3.columns

In [17]: df3[needed_columns] = df.reindex(index=df3.index,columns=needed_columns)

In [18]: df3
Out[18]: 
   1  2  0
c  0  0  1
j  0  0  1
i  0  0  1

[3 rows x 3 columns]

当你需要时将所有内容合并在一起(在此期间它们被保存在列表中,或者请参见下面的评论,在创建这些子框架时可以将它们移动到外部存储器中,然后在连接步骤之前读回)。

In [19]: pd.concat([ df.reindex(index=df.index-df2.index-df3.index), df2, df3]).reindex_like(df)
Out[19]: 
   0  1  2
a  1  0  0
b  1  1  1
c  1  0  0
d  1  1  1
e  1  1  1
f  1  0  0
g  1  0  0
h  1  1  1
i  1  0  0
j  1  0  0

[10 rows x 3 columns]

这种模式的优美之处在于,它可以轻松扩展到使用实际的数据库(或者更好的是 HDFStore)来存储“数据库”,然后根据需要创建/更新子框架,完成后将其写入新的存储。

我经常使用这种模式,不过实际上是用面板 (Panels) 实现的。

  • 对数据的某个子集执行计算,并将每个子集单独写入一个文件
  • 最后将它们全部读入并连接(在内存中),然后写出一个巨大的新文件。连接步骤可以一次性在内存中完成,或者如果任务真正巨大,则可以迭代地完成。

我可以使用多进程来执行计算并将每个单独的面板写入一个文件,因为它们都是完全独立的。唯一有依赖关系的部分是连接。

这本质上是一种映射-归约模式。


谢谢Jeff提供的所有选项,我会尝试其中几个,看看能否找到最适合我的程序的选项。 - user1204369
只是为了提供额外的反馈,第一种解决方案是我的程序中最快的选项。将DataFrame连接起来然后重新索引的第二个选项使运行时间增加了约一半。 - user1204369

2
快速操作:将旧数据框中的列a和b复制到新数据框中。
df1 = df[['a', 'b']]

1
我经常需要在大型数据框之间复制。我正在使用实时市场数据的数据框,这可能不是pandas设计的目的,但这是我的经验。
在我的电脑上,使用`.at`复制单个数据点需要15微秒,数据框大小几乎没有影响。`loc`最小需要550微秒,并随着数据框变大而增加:从一个100000x2数据框复制单个点需要3100微秒。`.ix`似乎比`loc`略快。
对于单个数据点,`.at`非常快,并且不会受到数据框大小的影响,但它无法处理范围,因此需要循环,因此时间缩放是线性的。另一方面,`.loc`和`.ix`相对而言非常慢,但它们可以处理范围,而且比线性缩放更好。但是,与`.at`不同,它们与数据框大小相比会显着放慢。
因此,当我经常在大型数据框之间复制小范围时,我倾向于使用`for`循环的`.at`,否则我会使用带范围的`.ix`。
for new_id in new_ids:
    for core_col in core_cols:
        df_large.at[new_id, core_col] = df_small.at[new_id, core_col]

当然,为了做得更好,我会选择使用Jeff上面提供的方案,但有多个选项也很好。

.at 的注意事项:它不适用于范围,并且如果数据类型是日期时间(可能还有其他类型),则无法使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接