Pandas跨列

时间:2017-08-30 15:53:22

标签: python pandas date interpolation

我有以下pd数据框。

import pandas as pd

df = pd.DataFrame([pd.datetime(2016,1,1), pd.datetime(2016,2,1)], columns = ['d1'])
df['d2'] = [pd.datetime(2016,1,5), pd.datetime(2016,2,10)]
df['d3'] = [pd.datetime(2016,1,10), pd.datetime(2016,2,20)]
df['v1'], df['v2'], df['v3'] = [1,10], [5, 100], [5, 100]
df['x1'] = [pd.datetime(2016,1,2), pd.datetime(2016,2,13)]


          d1         d2         d3  v1   v2   v3         x1
0 2016-01-01 2016-01-05 2016-01-10   1    5    5 2016-01-02
1 2016-02-01 2016-02-10 2016-02-20  10  100  100 2016-02-13

我想通过插值计算列x2

x: x1
independent = [d1, d2, d3]
dependent = [v1, v2, v3]

在上面的示例中,x2对于行0应为2,对于行1应为100。

我可以考虑使用df.iterrows(),slice [d1-d3]和[v1-v3],然后使用np.interp,但即使这样也有点复杂,因为我必须将日期转换为float。 (np.interp仅获取浮动列表)。

熊猫队应该有一个更清洁,更具矢量化的方法吗?

1 个答案:

答案 0 :(得分:1)

我不知道Pandas是否可以使用特定列作为IV和DV进行插值。但是,如果您将时间戳视为单个系列,则可以获得所需的内容。

通过稍微改组,我们可以重新格式化interpolate(method='time')的数据帧,然后将其恢复为原始格式。从示例数据框开始,添加一个空的x2

import numpy as np
df['x2'] = np.nan 

df
          d1         d2         d3  v1   v2   v3         x1  x2
0 2016-01-01 2016-01-05 2016-01-10   1    5    5 2016-01-02 NaN
1 2016-02-01 2016-02-10 2016-02-20  10  100  100 2016-02-13 NaN

现在从宽到长摆动,并缩减为两列,dvx获得dv的荣誉会员资格现在的俱乐部)。将实际列名保存为索引:

pairs = [('d1','v1'), ('d2','v2'), ('d3','v3'), ('x1','x2')]
df2 = pd.concat(
    (df[[d, v]].rename(index=lambda i: d,
                       columns=lambda x: 'd' if (x[0]=='d') | (x=='x1') else 'v') for d, v in pairs))

df2

            d      v
d1 2016-01-01    1.0
d1 2016-02-01   10.0
d2 2016-01-05    5.0
d2 2016-02-10  100.0
d3 2016-01-10    5.0
d3 2016-02-20  100.0
x1 2016-01-02    NaN
x1 2016-02-13    NaN

我们希望基于时间interpolate,因此我们将时间戳移动到索引中,按日期排序,然后执行插值:

df3 = (df2.reset_index()
          .set_index(pd.to_datetime(df2.d))
          .drop('d', 1)
          .sort_index()
          .interpolate(method="time")
          .sort_values('index')
      )

df3
           index      v
d                      
2016-01-01    d1    1.0
2016-02-01    d1   10.0
2016-01-05    d2    5.0
2016-02-10    d2  100.0
2016-01-10    d3    5.0
2016-02-20    d3  100.0
2016-01-02    x1    2.0
2016-02-13    x1  100.0

根据OP预期输出,插值是正确的。现在我们只需要将数据框恢复到原始形状。我们通过将索引设置回0 / 1,基于奇数/偶数行数,然后使用pivot()来执行此操作:

df4 = df3.reset_index().rename(index=lambda x: int(x%2)).pivot(columns='index')
df4.columns = df4.columns.droplevel(0)
iv, dv = zip(*pairs)
df4.columns = iv + dv

df4
          d1         d2         d3         x1    v1     v2     v3     x2
0 2016-01-01 2016-01-05 2016-01-10 2016-01-02   1.0    5.0    5.0    2.0
1 2016-02-01 2016-02-10 2016-02-20 2016-02-13  10.0  100.0  100.0  100.0

可能有一种更为隐蔽的方式来重塑中间的东西,但这里的关键直觉是,如果给出正确的格式,Pandas会使用时间作为参考来插入缺失值。

更新(每条评论)
如果插值必须按行进行,我们可以使用与上面类似的方法,apply()

def custom_interp(row, pairs):
    data = pd.concat(
        (pd.DataFrame(row[[d, v]]).rename(index=lambda x: 'd' if (x[0] == 'd') | (x == 'x1') else 'v',
                                          columns=lambda x: d) for d, v in pairs), 
        axis=1).T.reset_index()
    data = (data.set_index(pd.to_datetime(data.d))
                .drop('d', 1)
                .sort_index()
           )
    data.v = data.v.astype(float)
    data = data.interpolate(method='time').reset_index()
    data.index = np.repeat(row.name, len(data.index))
    data = data.pivot(columns='index')
    data.columns = data.columns.droplevel(0)
    return data.values[0]

df.apply(custom_interp, args=(pairs,), axis=1)

          d1         d2         d3         v1    v2     v3     x1     x2
0 2016-01-01 2016-01-05 2016-01-10 2016-01-02   1.0    5.0    5.0    2.0
1 2016-02-01 2016-02-10 2016-02-20 2016-02-13  10.0  100.0  100.0  100.0