提问人:Bruno Di Franco Albuquerque 提问时间:3/23/2022 最后编辑:Bruno Di Franco Albuquerque 更新时间:3/23/2023 访问量:433
Python - 将周末值提前到星期一
Python - Push forward weekend values to Monday
问:
我有一个数据帧(称为 df),如下所示:
我正在尝试获取所有周末的“交易量”值(列“WEEKDAY”=5(星期六)或 6(星期日)的值)并将它们求和到随后的星期一(WEEKDAY=0)。
我尝试了一些东西,但没有真正起作用,从最后三行中举个例子:
我所期待的是:
若要重现该问题,请执行以下操作:
!wget https://raw.githubusercontent.com/brunodifranco/TCC/main/volume_por_dia.csv
df = pd.read_csv('volume_por_dia.csv').sort_values('Datas',ascending=True)
df['Datas'] = pd.to_datetime(df['Datas'])
df = df_volume_noticias.set_index('Datas')
df['WEEKDAY'] = df.index.dayofweek
df
答:
这使用 pd.shift 解决了您的问题。
import pandas as pd
df['prior_volume'] = df.Volume.shift(1)
df['prior_volume2'] = df.Volume.shift(2)
df.loc[df['WEEKDAY'] == 0, 'Volume'] = df.loc[df['WEEKDAY'] == 0, 'prior_volume'] + \
df.loc[df['WEEKDAY'] == 0, 'prior_volume2'] + \
df.loc[df['WEEKDAY'] == 0, 'Volume']
df = df[df['WEEKDAY'].isin(range(5))]
df = df[['Volume', 'WEEKDAY']]
df.head(10)
我使用.groupby来解决问题。
import pandas as pd
df = pd.read_csv('volume_por_dia.csv')
df['Datas'] = pd.to_datetime(df['Datas'])
df['WEEKDAY'] = df['Datas'].dt.dayofweek
df['index'] = df['Datas']
# Group df by date, setting frequency as week
#(beginning Tue - so that Sat and Sun will be added to the next Mon)
df_group = df.groupby([pd.Grouper(key = 'Datas', freq='W-MON'), \
'WEEKDAY', 'index']).agg({'Volume': 'sum'})
# In each group, add days 5, 6 (Sat and Sun) to day 0 (Mon)
df_group.loc[(slice(None), 0), 'Volume'] += \
df_group.loc[(slice(None), [5, 6]), 'Volume'].groupby(level=0).sum()
# In the grouped data, remove Sat and Sun
df_group = df_group.reset_index()
df_group = df_group[df_group['WEEKDAY'] != 5]
df_group = df_group[df_group['WEEKDAY'] != 6]
# Remove volume data from original df, and merge with volume from df_group
df = df.drop(['Volume'], axis=1)
df = pd.merge(df,df_group[['index','Volume']],on='index', how='left')
df = df.dropna(subset=['Volume'])
df = df.drop(['index'], axis=1)
# Optional: sort dates in ascending order
df = df.sort_values(by=['Datas'])
print (df)
您可以简单地循环这些行,并从周五开始继续累积交易量,并更新周日交易量中的值。然后,只需删除星期五和星期六的行。
values = df.values
volume_accumulated = 0
for idx, row in enumerate(values):
if row[1] in (5, 6):
volume_accumulated += row[0]
elif row[1] == 0:
volume_accumulated += row[0]
df["Volume"][idx] = volume_accumulated
else:
volume_accumulated = 0
df = df[~df["WEEKDAY"].isin([5, 6])]
输入:
!wget https://raw.githubusercontent.com/brunodifranco/TCC/main/volume_por_dia.csv
import pandas as pd
import numpy as np
df = pd.read_csv('volume_por_dia.csv').sort_values('Datas',ascending=True)
df['Datas'] = pd.to_datetime(df['Datas'])
df.set_index('Datas', inplace=True)
df['WEEKDAY'] = df.index.dayofweek
我假设索引日期已排序,索引是唯一的,并且没有丢失的日期。我不能做出的一些假设是:Datas
- 对于每个星期一,我都有上一个周末的完整交易量,这可能是错误的,因为数据帧可能从周日开始,而我的周末交易量不完整;
- 对于每个周末,我都会有一个下一个星期一,这可能是错误的,因为数据帧可能会在周六或周日完成。
出于这些原因,在计算周末交易量之前,我首先提取第一个星期六和最后一个星期一的日期:
first_saturday = df.index[df.WEEKDAY==5][0]
last_monday = df.index[df.WEEKDAY==0][-1]
现在,我可以提取周末卷,确保我始终有周六-周日的一对,并且对于这些一对中的每一个,数据帧中都存在下一个星期一:
df_weekend = df.loc[
(df.WEEKDAY.isin([5,6]))&
(df.index<=last_monday)&
(df.index>=first_saturday)
]
df_weekend
现在,由于我有一对周六至周日的交易量,我可以通过以下方式计算总和:
weekend_volumes = pd.Series(
df_weekend.Volume.values.reshape(-1,2).sum(axis=1), #sum of volume couples
index = df_weekend.index[1::2]+pd.Timedelta("1d"), #date of the following monday
name="weekend_volume"
).reindex(df.index).fillna(0) #zero weekend-volume for days that are not mondays
weekend_volumes
最后,将周末交易量添加到起始交易量中:
df["Volume"] = df.Volume+weekend_volumes
我在下面附上了 df 的最后 25 行:
# 2022-02-18 16.0 4
# 2022-02-19 2.0 5
# 2022-02-20 1.0 6
# 2022-02-21 10.0 0
# 2022-02-22 43.0 1
# 2022-02-23 36.0 2
# 2022-02-24 38.0 3
# 2022-02-25 28.0 4
# 2022-02-26 5.0 5
# 2022-02-27 3.0 6
# 2022-02-28 14.0 0
# 2022-03-01 10.0 1
# 2022-03-02 16.0 2
# 2022-03-03 18.0 3
# 2022-03-04 11.0 4
# 2022-03-05 8.0 5
# 2022-03-06 2.0 6
# 2022-03-07 32.0 0
# 2022-03-08 18.0 1
# 2022-03-09 32.0 2
# 2022-03-10 24.0 3
# 2022-03-11 18.0 4
# 2022-03-12 4.0 5
# 2022-03-13 1.0 6
# 2022-03-14 10.0 0
在此处添加 2 个解决方案:
使用(Lukas Hestermeyer 早些时候指出;我添加了一个简化版本)
pd.shift
使用滚动窗口(这实际上是单行)
两种解决方案都假设;
Dates
按升序排序(如果没有,则应在继续之前进行排序)- 每个周末(周六和周日)的记录都由周一的记录接替。如果数据错误,需要添加其他检查
第 1 部分 |数据准备:
import pandas as pd
import numpy as np
# STEP 1: Create DF
Datas = [
'2019-07-02',
'2019-07-03',
'2019-07-04',
'2019-07-05',
'2019-07-06',
'2019-07-07',
'2019-07-08',
'2022-03-10',
'2022-03-11',
'2022-03-12',
'2022-03-13',
'2022-03-14'
]
Volume = [17, 30, 20, 21, 5, 10, 12, 24, 18, 4, 1, 5]
WEEKDAY = [1, 2, 3, 4, 5, 6, 0, 3, 4, 5, 6, 0]
dic = {'Datas': Datas, 'Volume': Volume, 'WEEKDAY': WEEKDAY}
df['Datas'] = pd.to_datetime(df['Datas'])
df = df.set_index('Datas')
df = pd.DataFrame(dic)
第 2 部分 |解决 方案:
解决方案 1 [pd.shift] :
# STEP 1: add shifts
df['shift_1'] = df['Volume'].shift(1)
df['shift_2'] = df['shift_1'].shift(1)
# STEP 2: sum Volume with shifts where weekday==0
cols_to_sum = ['Volume', 'shift_1', 'shift_2']
df['Volume'] = df[['WEEKDAY'] + cols_to_sum].apply(lambda x: int(x[1]) if x[0] else int(x[1] + x[2] + x[3]), axis=1)
df = df.drop(['shift_1', 'shift_2'], axis=1)
df
解决方案 2 [滚动窗口] :
# use rolling window of size 3 to sum where weekday == 0
df['Volume'] = np.where(
df['WEEKDAY'] == 0,
df['Volume'].rolling(window=3, center=False).sum(),
df['Volume']
)
df
第 3 部分 |删除周末记录:
df = df.loc[~df['WEEKDAY'].isin([5, 6])]
df
例如,如果您认为几周从星期二开始,那么问题就会变得更简单。您只需要获取周末的值并将其求和为该周的星期一(这将是周末后的星期一)。这将自动处理您的数据可能在周末开始/结束的情况。
import numpy as np
import pandas as pd
np.random.seed(1)
# Sample data
dates = pd.date_range('2018-02-05', '2018-07-22', freq='D')
volume = np.random.randint(1, 50, len(dates))
df = pd.DataFrame(dict(Datas=dates, Volume=volume))
df = df.set_index('Datas')
# Week starting from Tuesday
week = ((df.index - pd.DateOffset(days=1)).isocalendar().week).values
def add_weekend_to_monday(week):
monday = week.index.weekday == 0
weekend = week.index.weekday >= 5
week[monday] += week[weekend].sum()
return week
df['Volume'] = df.groupby(week)['Volume'].apply(add_weekend_to_monday)
根据您提供的代码,您已将 CSV 文件加载到 DataFrame df 中,将“Datas”列转换为日期时间,按日期升序对 DataFrame 进行排序,并将“Datas”列设置为索引。
您还创建了一个新列“WEEKDAY”,其中包含索引中每个日期的星期几(0-6,其中 0 表示星期一,6 表示星期日)。
import pandas as pd
# Load the data and convert the 'Datas' column to a datetime
df = pd.read_csv('https://raw.githubusercontent.com/brunodifranco/TCC/main/volume_por_dia.csv')
df['Datas'] = pd.to_datetime(df['Datas'])
# Set the 'Datas' column as the index
df.set_index('Datas', inplace=True)
# Compute the sum of weekend days (Saturday and Sunday) and assign it to the next following Monday
weekend_sum = df.loc[df.index.weekday.isin([5,6])]['Volume'].resample('W-MON').sum()
df.loc[weekend_sum.index, 'Volume'] += weekend_sum.values
解释:
df.loc[df.index.weekday.isin([5,6])]
选择索引(即日期)位于星期六或星期日(分别为工作日 5 或 6)的行。['Volume'].resample('W-MON').sum()
计算从周一开始的每周至少包含一个周末的“交易量”列的总和。结果是一个序列,其中索引包含每周的开始日期,值包含相应的总和。df.loc[weekend_sum.index, 'Volume'] += weekend_sum.values
将计算的总和分配给下一个星期一。它选择与具有周末总和的周的开始日期相对应的行,并将相应的总和添加到“交易量”列中。请注意,运算符用于修改原始 DataFrame df。(weekend_sum.index)
(weekend_sum.values)
+=
评论