首页 > 解决方案 > 在 pandas 中有效地在 250MB 数据帧上移位、连接和除法超过 90 次

问题描述

在尝试转换一个简单的数据集时,我遇到了 pandas 的性能和内存消耗问题。基本上,我试图将分组(按索引)数据集移动 90 次,然后除以移动列的值。

我的基本数据帧在内存中大约有 255MB(大约 11M 行和 5 列),并且使用 parquet 加载并具有多索引。使用我尝试过的方法移动和连接这么多数据,导致荒谬的内存消耗(我假设这是因为 pandas 在进程中管理内存和数据帧副本的方式)。

即使我的机器不受限制,操作本身也需要很长时间,以至于我认为它不切实际。我期望并假设这可以在几分钟内完成。我错了吗?

更具体地说,这是我要执行的操作:

  1. 对于数据框中的每一列,创建一个包含 X 天前移位数据的新列for X in range(1, 90)

  2. 将每列的值除以前一列移位。(所以columnA.shift(90)除以columnA.shift(89);

  3. 将所有内容连接到一个数据框中。

我发现纯粹用语言来解释它有点困难,所以这里是一个数据操作的例子:

这是原始数据集(示例只有 2 列):

data = pd.DataFrame(
    [[20, 43],
    [40, 52],
    [30, 34],
    [60, 52],
    [44, 66],
    [55, 72],
    [34, 34],
    [51, 17]],
    columns=['a', 'b'])

换班后:

值除法后:

以下是我尝试过的一些代码片段。

连接循环中的所有移位列(这没有divide操作)。这实际上是错误的,因为我忘了在groupby()这里执行:

for w in range(1, 91):
    data = pd.concat([
        data,
        (data[['c1', 'c2', 'c3', 'c4', 'c5']].shift(w).rename(
            columns={
                'c1': f'c1_{w}',
                'c2': f'c2_{w}',
                'c3': f'c3_{w}',
                'c4': f'c4_{w}',
                'c5': f'c5_{w}'
            }
        ))], axis=1)

使用groupby().apply()

def _myfunc(x):
    for w in range (1, 4):
        return (x.shift(w) / x.shift(w - 1)).rename({
            'c1': f'c1_{w}',
            'c2': f'c2_{w}',
            'c3': f'c3_{w}',
            'c4': f'c4_{w}',
            'c5': f'c5_{w}'
            })
        
data.groupby("mylevel")[['c1', 'c2', 'c3', 'c4', 'c5']].apply(_myfunc)

很高兴在这里获得帮助。

标签: pythonpandas

解决方案


这显示了我在评论中提出的问题:

df = pd.DataFrame(np.random.randint(0,100, size=(100, 1)), columns=('a',))
shift_1 = df['a'].shift(1) / df['a']
assert all((df['a'].shift(90) / df['a'].shift(89)).dropna() == shift_1.shift(89).dropna())

dropna() 就在那里,因为np.nan != np.nan

那么,当您可以在查找之前进行转移时,为什么要保留所有副本?


推荐阅读