如果找不到值,则在 Panda 中插入行

Inserting row in Panda if value can't be found

这是我的数据框: 数据框 1:

客户 C 将在 5 月之后退出,因此没有要计算平均值的行。我想:在一个月的时间里找出该 ID 的缺失值,然后添加一行零持有量,以便有一行将平均值放入:

dataframe2:

按 ID 分组并移动一个月以获取错误消息,如果有错误,请添加行。 我已经尝试过,但 gouping 和 shifting 最终导致下个月更进一步。

好的,这里有两种方法。首先,一种简单的方法,但是使用 O[n * m] 内存存储 n 个不同的日期和 m 个不同的 ID,因为它将 DataFrame 扩展为二维数组(日期 x ID)。然后,第二种方法稍微复杂一些,但只使用所需的内存(O[k],其中 k 是输出中的行数)。

可重现的设置

(OP注意:以后请提供一个作为问题的一部分)。

np.random.seed(0)
n = 10
t = pd.date_range('2021-01-01', '2021-07-01', freq='MS')
i = np.arange(len(t))
i = np.sort(np.r_[i, np.random.choice(i, n - len(i), True)])

df = pd.DataFrame({
    'Date': t[i],
    'ID': np.random.choice(list('ABC'), n),
    'Holding': np.random.randint(0, 10, n),
    
})

>>> df
        Date ID  Holding
0 2021-01-01  B        6
1 2021-01-01  B        7
2 2021-02-01  C        7
3 2021-03-01  A        8
4 2021-04-01  C        1
5 2021-05-01  A        5
6 2021-05-01  A        9
7 2021-06-01  A        8
8 2021-06-01  C        9
9 2021-07-01  B        4

方法一:简单易懂,但占用内存大

# make a tmp 2D DataFrame date x ID, make sure all months are there
z = df.pivot_table(index='Date', columns='ID', values='Holding', aggfunc=sum).resample('MS').sum()

# optional: append an extra month with zeros
z = z.reindex(z.index.union([z.last_valid_index() + pd.DateOffset(months=1)])).fillna(0)

# at this point:
>>> z
ID             A     B    C
Date                       
2021-01-01   0.0  13.0  0.0
2021-02-01   0.0   0.0  7.0
2021-03-01   8.0   0.0  0.0
2021-04-01   0.0   0.0  1.0
2021-05-01  14.0   0.0  0.0
2021-06-01   8.0   0.0  9.0
2021-07-01   0.0   4.0  0.0
2021-08-01   0.0   0.0  0.0

过去两个月的平均值(包括当前):

>>> z.rolling(2, min_periods=0).mean()
ID             A     B    C
Date                       
2021-01-01   0.0  13.0  0.0
2021-02-01   0.0   6.5  3.5
2021-03-01   4.0   0.0  3.5
2021-04-01   4.0   0.0  0.5
2021-05-01   7.0   0.0  0.5
2021-06-01  11.0   0.0  4.5
2021-07-01   4.0   2.0  4.5
2021-08-01   0.0   2.0  0.0

将它们全部放回一起,将平均值作为第二列:

out = z.stack().to_frame('Holding').assign(mo2avg=z.rolling(2, min_periods=0).mean().stack())

# optional: remove entries where both columns are 0
out = out.loc[(out != 0).any(1)]

# and now:
>>> out
               Holding  mo2avg
Date       ID                 
2021-01-01 B      13.0    13.0
2021-02-01 B       0.0     6.5
           C       7.0     3.5
2021-03-01 A       8.0     4.0
           C       0.0     3.5
2021-04-01 A       0.0     4.0
           C       1.0     0.5
2021-05-01 A      14.0     7.0
           C       0.0     0.5
2021-06-01 A       8.0    11.0
           C       9.0     4.5
2021-07-01 A       0.0     4.0
           B       4.0     2.0
           C       0.0     4.5
2021-08-01 B       0.0     2.0

方法 2:内存精简(永远不会扩展到完整日期 x ID)

# make a tmp DataFrame with dates grouped by month start, sum Holdings
# and add a month for each ID:
z = pd.concat([
    df,
    (df.groupby('ID')['Date'].last() + pd.DateOffset(months=1)).reset_index().assign(Holding=0),
]).set_index('Date').groupby('ID').resample('MS').sum()

>>> z
               Holding
ID Date               
A  2021-03-01        8
   2021-04-01        0
   2021-05-01       14
   2021-06-01        8
   2021-07-01        0
B  2021-01-01       13
   2021-02-01        0
   2021-03-01        0
   2021-04-01        0
   2021-05-01        0
   2021-06-01        0
   2021-07-01        4
   2021-08-01        0
C  2021-02-01        7
   2021-03-01        0
   2021-04-01        1
   2021-05-01        0
   2021-06-01        9
   2021-07-01        0

请注意每个 ID 的所有“缺失”月份是如何填充的,但仅在该 ID 的最后有效日期之后最多一个月。这是计算正确滚动方式所必需的。

现在,我们可以按 'ID' 分组,但保留 'Date' 索引,因此 RollingGroupBy 是正确的:

out = z.assign(mo2avg=z.reset_index('ID').groupby('ID')['Holding'].rolling(2, min_periods=0).mean())

# optional: drop rows where both Holding and avg are 0:
out = out.loc[(out != 0).any(1)]

# swap levels and sort index to make it comparable to the result
# of method 1:
out = out.swaplevel().sort_index()

# finally:
>>> out
               Holding  mo2avg
Date       ID                 
2021-01-01 B        13    13.0
2021-02-01 B         0     6.5
           C         7     7.0
2021-03-01 A         8     8.0
           C         0     3.5
2021-04-01 A         0     4.0
           C         1     0.5
2021-05-01 A        14     7.0
           C         0     0.5
2021-06-01 A         8    11.0
           C         9     4.5
2021-07-01 A         0     4.0
           B         4     2.0
           C         0     4.5
2021-08-01 B         0     2.0