Python 熊猫:通过在现有列之间线性插值来创建新列

Python 熊猫:通过在现有列之间线性插值来创建新列,python,pandas,Python,Pandas,假设我有一个数据框,其中包含关于山上不同海拔高度的温度数据,每个数据框每天同时采样一次。每个探测器的高度是固定的(即它们每天保持不变),并且是已知的。每一行代表一个不同的时间戳,我有一个单独的列来记录每个探针观察到的温度。我还有一个列(targ_alt),其中包含每行的“感兴趣的高度” 我的目标是添加一个名为intreped_-temp的新列,该列包含每行的targ_-alt温度,通过在已知高度处的探头温度之间进行线性插值。最好的方法是什么 以下是一些设置代码,以便我们可以查看相同的上下文: i

假设我有一个数据框,其中包含关于山上不同海拔高度的温度数据,每个数据框每天同时采样一次。每个探测器的高度是固定的(即它们每天保持不变),并且是已知的。每一行代表一个不同的时间戳,我有一个单独的列来记录每个探针观察到的温度。我还有一个列(
targ_alt
),其中包含每行的“感兴趣的高度”

我的目标是添加一个名为
intreped_-temp
的新列,该列包含每行的
targ_-alt
温度,通过在已知高度处的探头温度之间进行线性插值。最好的方法是什么

以下是一些设置代码,以便我们可以查看相同的上下文:

import pandas as pd
import numpy as np

np.random.seed(1)

n = 10
probe_alts = {'base': 1000, 'mid': 2000, 'peak': 3500}
# let's make the temperatures decrease at higher altitudes...just for style
temp_readings = {k: np.random.randn(n) + 15 - v/300 for k, v in probe_alts.items()}
df = pd.DataFrame(temp_readings)

targ_alt = 2000 + (500 * np.random.randn(n))
df['targ_alt'] = targ_alt
因此,
df
看起来像这样:

        base        mid      peak     targ_alt
0  13.624345  10.462108  2.899381  1654.169624
1  11.388244   6.939859  5.144724  1801.623237
2  11.471828   8.677583  4.901591  1656.413650
3  10.927031   8.615946  4.502494  1577.397179
4  12.865408  10.133769  4.900856  1664.376935
5   9.698461   7.900109  3.316272  1993.667701
6  13.744812   8.827572  3.877110  1441.344826
7  11.238793   8.122142  3.064231  2117.207849
8  12.319039   9.042214  3.732112  2829.901089
9  11.750630   9.582815  4.530355  2371.022080

嗯。事实上,我有点惊讶这是否奏效,但这是一个起点。有谁能建议一种方法,在使用
map
之前,我不需要使用
zip
ping将
np.interp
的所有输入压缩到一列?(请参见下面的编辑。这正是
数据帧.apply
所做的…
)也就是说,是否有一个Pandas函数可以像
map
那样处理序列,但将整行数据帧作为其输入(但不涉及
groupby

下面是代码,从问题结束的地方开始:

df['rolled'] = zip(df['targ_alt'], zip(df['base'], df['mid'], df['peak']))
%timeit df['interped_temp'] = df['rolled'].map(lambda x: np.interp(x[0], probe_alts.values(), x[1]))
del df['rolled']
这将根据需要返回:

        base        mid      peak     targ_alt  interped_temp
0  13.624345  10.462108  2.899381  1654.169624      11.555706
1  11.388244   6.939859  5.144724  1801.623237       7.822315
2  11.471828   8.677583  4.901591  1656.413650       9.637647
3  10.927031   8.615946  4.502494  1577.397179       9.592617
4  12.865408  10.133769  4.900856  1664.376935      11.050570
5   9.698461   7.900109  3.316272  1993.667701       7.911496
6  13.744812   8.827572  3.877110  1441.344826      11.574613
7  11.238793   8.122142  3.064231  2117.207849       7.726924
8  12.319039   9.042214  3.732112  2829.901089       6.104308
9  11.750630   9.582815  4.530355  2371.022080       8.333099
对于
n=10
%timeit
给出182us/loop。对于
n=1e6
%timeit
给出4.51s/loop。很想看到其他的方法


@DSM做了一个很好的注释,
probe_alts.values()
可以以任何顺序返回。这里有一个更清晰的代码,它遵循了上面的精神:

probes = ['base', 'mid', 'peak']
df['rolled'] = zip(df['targ_alt'], zip(*[df[p] for p in probes]))
df['interped_temp'] = df['rolled'].map(lambda x: np.interp(x[0], tuple(probe_alts[p] for p in probes), x[1]))
del df['rolled']
在我看来,这是一个更干净的方法,它只使用了
数据帧

probes = ['base', 'mid', 'peak']
def cust_interp(row):
    return np.interp(row['targ_alt'], tuple(probe_alts[p] for p in probes), row[probes])
df['interped_temp'] = df.apply(cust_interp, axis=1)

在我上面给出的示例中,我希望在每一行中插入不同的x坐标。好的如果你不想…如果你想在每一行内对同一个x坐标进行插值,使用SciPy可以节省大量的时间。见下例:

import numpy as np
import pandas as pd
from scipy.interpolate import interp1d

np.random.seed(1)
n = 10e4

df = pd.DataFrame({'a': np.random.randn(n), 
                   'b': 10 + np.random.randn(n), 
                   'c': 30 + np.random.randn(n)})

xs = [-10, 0, 10]
cvs = df.columns.values

现在考虑3种不同的方法来钉住一列,它将在给定列之间插入5×:

的x坐标。
%timeit df['n1'] = df.apply(lambda row: np.interp(5, xs, row[cvs]), axis=1)
%timeit df['n2'] = df.apply(lambda row: np.interp(5, xs, tuple([row[j] for j in cvs])), axis=1)
%timeit df['n3'] = interp1d(xs, df[cvs])(5)
以下是n=1e2的结果:

100 loops, best of 3: 13.2 ms per loop
1000 loops, best of 3: 1.24 ms per loop
1000 loops, best of 3: 488 µs per loop
对于n=1e4:

1 loops, best of 3: 1.33 s per loop
10 loops, best of 3: 109 ms per loop
1000 loops, best of 3: 798 µs per loop
对于n=1e6:

# first one is too slow to wait for
1 loops, best of 3: 10.9 s per loop
10 loops, best of 3: 58.3 ms per loop

一个后续问题:是否有一种快速的方法来修改此代码,以便它可以通过线性外推处理训练数据最小-最大范围之外的x个输入

一种方法可能是转置数据帧,在要插值的行之间插入一行NaN(缺失值),插值(填充值),然后转置回:

df.transpose()
df.loc['intreped_temp'] = np.nan

# row needs to reside in between data columns for interpolating to work
# may require further index manipulation 
df.sort_index() 

df.interpolate()
df.transpose()

此策略源自此处指南中的信息:

警告:
probe\u alts.values()
可以以任何顺序返回。@DSM,您关于
probe\u alts.values()
的任意顺序的观点是一个很好的答案。只要更清楚地考虑原始问题,您当然可以使用
DataFrame。在这里应用
以获得良好效果。在0.13中,他们是一个关于插值的全新部分,请参见:@Jeff,感谢您为0.13中的新功能提供了指针。它们似乎是针对针对从行索引派生的x值进行插值的情况而设计的。您知道0.13是否包含对列索引进行插值的功能?无论哪种方式,我认为我的上述构造对于更定制的插值技术都是一个不错的参考。