对于Pandas DataFrame,我试图通过滚动均值进行分组,并在每行中指定相对于日期时间索引的可变窗口大小。
对于以下每周df
数据:
| week_start_date | material | location | quantity | window_size |
|-----------------|----------|----------|----------|-------------|
| 2019-01-28 | C | A | 870 | 1 |
| 2019-02-04 | C | A | 920 | 3 |
| 2019-02-18 | C | A | 120 | 1 |
| 2019-02-25 | C | A | 120 | 2 |
| 2019-03-04 | C | A | 120 | 1 |
| 2018-12-31 | D | A | 1200 | 8 |
| 2019-01-21 | D | A | 720 | 8 |
| 2019-01-28 | D | A | 480 | 8 |
| 2019-02-04 | D | A | 600 | 8 |
| 2019-02-11 | D | A | 720 | 8 |
| 2019-02-18 | D | A | 80 | 8 |
| 2019-02-25 | D | A | 600 | 8 |
| 2019-03-04 | D | A | 1200 | 8 |
| 2019-01-14 | E | B | 150 | 1 |
| 2019-01-28 | E | B | 1416 | 1 |
| 2019-02-04 | F | B | 1164 | 1 |
| 2019-01-28 | G | B | 11520 | 8 |
该窗口需要相对于week_start_date
中设置的实际日期,而不是像整数索引一样对待。
它需要按material
和location
分组。
滚动平均值用于quantity
列。
窗口大小需要根据window_size
列中的值进行更改。该值随时间变化-它表示需要汇总数量的时间倒退的周数。
当某行不可用时,平均值应假定该值为0,即:
当没有星期日期的行时
mean(null, null, null, 1000) = 1000
但实际上应该:
平均值(0,0,0,1000)= 250
但是-仅在对首次观测值进行测量后才适用。
我可以使用以下方法获得为期8周(56天)的静态窗口:
df.set_index('week_start_date').groupby(['material', 'location'])['quantity'].rolling('56D', min_periods=1).mean()
我已经尝试使用expanding,但没有成功。
如何相对于读取的每一行设置窗口大小?
# Example Data
df = pd.DataFrame({'week_start_date': ['2019-01-28','2019-02-04','2019-02-18','2019-02-25','2019-03-04','2018-12-31','2019-01-21','2019-01-28','2019-02-04','2019-02-11','2019-02-18','2019-02-25','2019-03-04','2019-01-14','2019-01-28','2019-02-04','2019-01-28'],
'material': ['C','C','C','C','C','D','D','D','D','D','D','D','D','E','E','F','G'],
'location': ['A','A','A','A','A','A','A','A','A','A','A','A','A','B','B','B','B'],
'quantity': ['870','920','120','120','120','1200','720','480','600','720','80','600','1200','150','1416','1164','11520'],
'min_of_pdt_or_8_weeks': ['1','3','1','2','1','8','8','8','8','8','8','8','8','1','3','1','8']})
# Fix formats
df['week_start_date'] = pd.to_datetime(df['week_start_date'])
df['actual_week_qty'] = df['quantity'].astype(float)
| material | location | week_start_date | quantity |
| C | A | 2019-01-28 | 870 |
| C | A | 2019-04-02 | 306.6667 |
| C | A | 2019-02-18 | 520 |
| C | A | 2019-02-25 | 386.6667 |
| D | A | 2018-12-31 | 1200 |
| D | A | 2019-01-21 | 960 |
| D | A | 2019-01-28 | 800 |
| D | A | 2019-04-02 | 600 |
| D | A | 2019-11-02 | 720 |
| D | A | 2019-02-18 | 400 |
| D | A | 2019-02-25 | 466.6667 |
| D | A | 2019-04-03 | 650 |
| E | B | 2019-01-14 | 150 |
| E | B | 2019-01-28 | 783 |
| F | B | 2019-04-02 | 1164 |
| G | B | 2019-01-28 | 11520 |
答案 0 :(得分:0)
您可能会这样做的天真的方法是进行8次计算(假设这是有界的!)并合并结果:
In [11]: d = {w: df.set_index('week_start_date')
.groupby(['material', 'location'])['quantity']
.rolling(f'{7*w}D', min_periods=1)
.mean()
.reset_index(name="mean")
.assign(window_size=w)
for w in range(1, 9)}
然后,您可以将这些DataFrame合并在一起并与原始DataFrame合并,因为我们在左右两侧都有window_size列,该列位于其内部。
In [12]: pd.concat(d.values()).merge(df, how="inner")
Out[12]:
material location week_start_date mean window_size quantity
0 C A 2019-01-28 870.000000 1 870.0
1 C A 2019-02-18 520.000000 1 120.0
2 C A 2019-04-03 320.000000 1 120.0
3 E B 2019-01-14 150.000000 1 150.0
4 F B 2019-04-02 1164.000000 1 1164.0
5 C A 2019-02-25 386.666667 2 120.0
6 C A 2019-04-02 920.000000 3 920.0
7 E B 2019-01-28 783.000000 3 1416.0
8 D A 2018-12-31 1200.000000 8 1200.0
9 D A 2019-01-21 960.000000 8 720.0
10 D A 2019-01-28 800.000000 8 480.0
11 D A 2019-04-02 600.000000 8 600.0
12 D A 2019-11-02 720.000000 8 720.0
13 D A 2019-02-18 400.000000 8 80.0
14 D A 2019-02-25 466.666667 8 600.0
15 D A 2019-04-03 650.000000 8 1200.0
16 G B 2019-01-28 11520.000000 8 11520.0
注意:假设您已将window_size的fillna设置为8:
df.window_size = df.window_size.replace('NaN', 8).astype(int) # in your example
此外,您想确保将格式传递给to_datetime以确保不会出现歧义,大熊猫也许在这里可以做得很好……但是我不会不能依赖它(显式使用format='%d/%m/%Y
)。您希望在读完日期后就摆脱奇怪的日期格式,也可以将其传递给read_csv(dayfirst = True)和朋友。
我并不完全相信这是您想要的,因为您输入的df和预期值之间存在差异(例如,预期值中没有G B)。
无论如何,我怀疑只有一个拍摄方法可以做到这一点,但是这取决于周/物料/位置的稀疏性(如果密密麻麻的话,它会容易得多,如果稀疏,这可能是最好的选择)...
现在我考虑一下,您可以完全在材质/位置subDataFrame上执行此操作,是否可以将这个问题简化为该DataFrame的函数(只是周+忽略材质/位置的值),或者应用速度太慢? / p>