2017-08-30 108 views
2

我有以下pd数据帧。Pandas在列间插入日期

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 

我想通过内插

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

在上面的例子来计算列X2,X2应该是2行0,100为第1行

我能想到使用df.iterrows(),slice [d1-d3]和[v1-v3],然后使用np.interp,但即使这样会变得有点复杂,因为我必须将日期转换为float。 (np.interp只接受浮动列表)。

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

回答

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 

有可能重塑在中间的东西隐蔽的方式,但关键的直觉如果给出正确的格式,熊猫将使用时间作为参考插入缺失值。

UPDATE(每评论)
如果内插必须每行发生,我们可以使用一个类似的方法如上述,与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 
+0

感谢。就像这个想法一样,但是插值需要按行进行。您的示例df2将d行和v行结合在一起。 –

+0

在您提供的示例数据中,每行中的日期形成了不跨行的时段。您的数据中是否会出现这种重叠情况? –

+0

是的,对不起,我的例子很差。有重叠。更好的例子是在第1行中将所有'Feb'替换为'Jan'。 –