Pandas组内插值

31
我有一个数据框,其中包含以下信息:
    filename    val1    val2
t                   
1   file1.csv   5       10
2   file1.csv   NaN     NaN
3   file1.csv   15      20
6   file2.csv   NaN     NaN
7   file2.csv   10      20
8   file2.csv   12      15

我想根据索引在数据框中插值,但仅在每个文件组内
为了进行插值,通常会这样做:
df = df.interpolate(method="index")

"而且我要分组"
grouped = df.groupby("filename")

我希望插值后的数据框长这个样子:
    filename    val1    val2
t                   
1   file1.csv   5       10
2   file1.csv   10      15
3   file1.csv   15      20
6   file2.csv   NaN     NaN
7   file2.csv   10      20
8   file2.csv   12      15

在 t = 6 时,NaN 仍然存在,因为它们是文件2组中的第一项。我怀疑我需要使用“apply”,但还没有弄清楚具体如何操作...
grouped.apply(interp1d)
...
TypeError: __init__() takes at least 3 arguments (2 given)

任何帮助都将不胜感激。
3个回答

30
>>> df.groupby('filename').apply(lambda group: group.interpolate(method='index'))
    filename  val1  val2
t                       
1  file1.csv     5    10
2  file1.csv    10    15
3  file1.csv    15    20
6  file2.csv   NaN   NaN
7  file2.csv    10    20
8  file2.csv    12    15

2
这似乎在小数据框上运行良好,但在大数据框上需要很长时间(比仅使用.interpolate()要慢几个数量级)。您是否期望效率会差那么多?组数与行数相比较小(大约1000个组与10^7行)。 - R. W.

12

我也遇到了这个问题。你可以使用transform而不是apply,如果你有大约1000个组,这将使你的运行时间缩短超过25%:

import numpy as np
import pandas as pd

np.random.seed(500)
test_df = pd.DataFrame({
    'a': np.random.randint(low=0, high=1000, size=10000),
    'b': np.random.choice([1, 2, 4, 7, np.nan], size=10000, p=([0.2475]*4 + [0.01]))
})

测试:

%timeit test_df.groupby('a').transform(pd.DataFrame.interpolate)

输出:566毫秒±27.1毫秒每个循环(平均值±7次运行的标准差,每个循环1次)

%timeit test_df.groupby('a').apply(pd.DataFrame.interpolate)

输出:788 ms ± 10.4 ms每次循环(平均值±7次运行的标准偏差,1次循环)

%timeit test_df.groupby('a').apply(lambda group: group.interpolate())

输出:787 ms ± 17.9 ms每次循环(7次运行的平均值±标准差,每个循环1次)

%timeit test_df.interpolate()

输出:918 µs ± 16.9 µs每次循环(平均值±7个运行的标准偏差,每个1000个循环)

与在完整数据帧上完全向量化调用interpolate相比,您仍将看到运行时间显着增加,但我认为您在pandas中无法做得更好。


2
您使用 transform 的解决方案未能产生所需的输出。 - tommy.carstensen
也许这是因为距离原帖已经过去了大约4年,但我得到的结果与此相反。apply更快。 - Weevils

1
考虑到上述方法的长运行时间,我建议使用for循环和interpolate()函数,这不超过几行代码,但速度更快。
for i in range(len(df.filename.unique())):
      mask = df.loc[:,'filename']==df.filename.unique()[i]
      df[mask]=dfs[mask].interpolate(method='index')

我刚刚对groupby方法和for循环方法进行了比较,它们在一个有130个组的6000行DataFrame上似乎表现相似。不过,我承认我的两种插值方法都是使用线性方法运行的。 - emigre459

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