【问题标题】:Writing to slice of Pandas multindex dataframe is slow写入 Pandas 多索引数据帧的切片很慢
【发布时间】:2021-01-30 14:42:04
【问题描述】:

我有一个大的 for 循环来计算许多变量,最终我想将这些变量存储在 Pandas 多索引数据框中。循环的每一步,我都需要写入数据帧的一部分。数据帧有两个行索引和许多列。每个操作都需要写入一个切片,定义为:所有顶部索引、一个特定的第二个索引和一个特定的列。下面是一个操作示例。

import numpy as np
import pandas as pd
n_paths = 1000
pathgrid=np.arange(1, n_paths + 1)
dt = 0.25
T = 40
timegrid = np.arange(0, T + dt, dt)
multiindex = pd.MultiIndex.from_product([pathgrid, timegrid], names=['Path', 'Timestep'])
df=pd.DataFrame(index=multiindex,columns=['Values'])

input = np.random.random_sample(n_paths)
i=0
%timeit df.loc[(pathgrid,i),'Values'] = input

868 ms ± 8.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我发现这非常慢,这使得我的总运行时间非常慢,因为该操作需要执行数百次。有没有更快的方法来使用 Pandas 做到这一点?我知道的唯一选择是将值存储在单独的 Numpy 数组中,并在循环后将 Numpy 数组写入数据帧。但这意味着我必须创建大量这些临时数组

【问题讨论】:

  • 我觉得上面不是原代码,所以不能给推荐。例如,我不确定数据帧是否写入文件。但是,您提到“数据帧有两个行索引和许多列”,那么我们应该期望它需要更多的内存和时间来操作。建议你在内存更大的电脑上测试你的代码。
  • 这不是内存问题,我有足够的可用内存用于此操作

标签: python pandas dataframe


【解决方案1】:
df.loc[(pathgrid,i),'Values'] = input

input 中的各个值分配给Timestep==0.0len(input)-通过的行。因此它很慢。

我建议使用函数一次性赋值:

import numpy as np
import pandas as pd


def assignValue(df, timestep, values):
    for path, new_df in df.groupby(level=0):
        new_df.loc[(path, timestep)] = values[path-1]


n_paths = 1000
pathgrid=np.arange(1, n_paths + 1)
dt = 0.25
T = 40

timegrid = np.arange(0, T + dt, dt)

multiindex = pd.MultiIndex.from_product(
    [pathgrid, timegrid], names=['Path', 'Timestep'])

df = pd.DataFrame(index=multiindex, columns=['Values'])

input = np.random.random_sample(n_paths)

i = 0
%timeit assignValue(df, 0.0, input)

在我的电脑上,新代码需要 315 毫秒,但原始代码需要 1.49 秒。

更新

原始代码不会更新原始数据框对象。我使用 for 循环和 .loc 来修改行,它更快。

import numpy as np
import pandas as pd


n_paths = 1000
dt = 0.25
T = 40

pathgrid=np.arange(1, n_paths + 1)
timegrid = np.arange(0, T + dt, dt)

multiindex = pd.MultiIndex.from_product(
    [pathgrid, timegrid],
    names=['Path', 'Timestep']
)

df = pd.DataFrame(index=multiindex, columns=['Values'])

inputs = np.random.random_sample(n_paths)
%timeit for i in range(1, n_paths+1): df.loc[(i, 0.0)] = inputs[i-1]

新代码现在需要 80.1 毫秒。

【讨论】:

  • 这是一个不错的解决方案,但运行时间仍然慢了两个,以满足我的需要。我将求助于使用临时变量并在我的 for 循环之后构建数据框
  • 查看原帖的变化。
  • 哇,速度更快,令人印象深刻。这可能有效
  • 如果我想将二维数组作为新列加载到这个多索引数据帧中怎么办?假设我的数据是 input = np.random.rand(len(pathgrid),len(timegrid)) 并且我想像 df.loc[(pathgrid,timegrid),'Values'] = input 一样加载它?这对我不起作用,虽然等等我认为只是做 df['Values'] = input.ravel() 工作并且非常快!
  • 真的吗?使用for循环分配比按块分配更快?这是否意味着熊猫做了一些愚蠢的事情?
猜你喜欢
  • 2019-07-22
  • 2022-11-03
  • 2018-06-10
  • 2017-01-16
  • 2017-08-11
  • 1970-01-01
  • 2016-03-23
  • 1970-01-01
  • 2015-03-16
相关资源
最近更新 更多