熊猫-使用daterange展开数据框

时间:2018-09-06 13:48:56

标签: python pandas dataframe

我有以下数据框:

 name    from       amount   days
 A       7/31/18    200      1
 B       7/31/18    300      1
 C       7/30/18    200      1
 D       7/27/18    100      3
 ......
 G       7/17/18    50       1
 H       7/13/18    150      4

我想将其扩展为天数不等于1的地方:

 name    from       amount   days
 A       7/31/18    200      1
 B       7/31/18    300      1
 C       7/30/18    200      1
 D       7/29/18    100      3
 D       7/28/18    100      3
 D       7/27/18    100      3
 ......
 G       7/17/18    50       1
 H       7/16/18    150      4
 H       7/15/18    150      4
 H       7/14/18    150      4
 H       7/13/18    150      4

如果可能的话,我还想添加一列以区分原始数据和扩展数据(因为最终我将需要过滤一些日期):

 name    from       amount   days   original
 A       7/31/18    200      1      1
 B       7/31/18    300      1      1
 C       7/30/18    200      1      1
 D       7/29/18    100      3      0
 D       7/28/18    100      3      0
 D       7/27/18    100      3      1
 ......
 G       7/17/18    50       1      1
 H       7/16/18    150      4      0
 H       7/15/18    150      4      0
 H       7/14/18    150      4      0
 H       7/13/18    150      4      1

编辑:要澄清扩展:天将告诉您需要扩展到多少行。或者,您可以使用原始值上方的日期作为边界(条目7/27(天数= 3)将在上面的值(日期为7/30处停止)。数据具有约束条件以确保其永不重叠)。

2 个答案:

答案 0 :(得分:3)

理解力

df['from'] = pd.to_datetime(df['from'])

pd.DataFrame([
    (n, f, a, d, int(f == F))
    for n, F, a, d in zip(*map(df.get, df))
    for f in pd.date_range(F, periods=d)[::-1]
], columns=[*df.columns] + ['original'])

   name       from  amount  days  original
0     A 2018-07-31     200     1         1
1     B 2018-07-31     300     1         1
2     C 2018-07-30     200     1         1
3     D 2018-07-29     100     3         0
4     D 2018-07-28     100     3         0
5     D 2018-07-27     100     3         1
6     G 2018-07-17      50     1         1
7     H 2018-07-16     150     4         0
8     H 2018-07-15     150     4         0
9     H 2018-07-14     150     4         0
10    H 2018-07-13     150     4         1

助手功能

我将答案编辑为使用duplicated而不是cum_count。我是从@Wen's post

得到这个想法的
def f(x):
  return pd.date_range(
      pd.to_datetime(x).min(),
      periods=len(x)
  ).sort_values(ascending=False)

def g(d):
  return d.groupby('name')['from'].transform(f)

def h(d):
  return 1 - d.name.duplicated(keep='last')

df.loc[df.index.repeat(df.days)].assign(**{'from': g, 'original': h})

  name       from  amount  days  original
0    A 2018-07-31     200     1         1
1    B 2018-07-31     300     1         1
2    C 2018-07-30     200     1         1
3    D 2018-07-29     100     3         0
3    D 2018-07-28     100     3         0
3    D 2018-07-27     100     3         1
4    G 2018-07-17      50     1         1
5    H 2018-07-16     150     4         0
5    H 2018-07-15     150     4         0
5    H 2018-07-14     150     4         0
5    H 2018-07-13     150     4         1

答案 1 :(得分:3)

大约分两步创建数据框(reindex)并调整值(duplicated

newdf=df.reindex(df.index.repeat(df.days)) # create the df using reindex
adddate=pd.Series(np.concatenate(df.days.apply(np.arange).values),index=newdf.index)# create the timedelta to add 
newdf['from']=pd.to_datetime(newdf['from'])+pd.to_timedelta(adddate,unit='d')# assign the value 
newdf['original']=(~newdf.index.duplicated()).astype(int)
newdf
Out[240]: 
  name       from  amount  days  original
0    A 2018-07-31     200     1         1
1    B 2018-07-31     300     1         1
2    C 2018-07-30     200     1         1
3    D 2018-07-27     100     3         1
3    D 2018-07-28     100     3         0
3    D 2018-07-29     100     3         0