如何用datetime.timedelta生成预算重复日期CSV并修正日期偏移
问题原因
pandas 周期频率默认带固定锚点规则,和自定义起始日期冲突是日期偏移的核心原因:
W(周频)默认锚定每周日,传入2022-06-03(周五)作为起始点时,pandas会自动选取时间范围内第一个周日(2022-06-05)作为首条记录日期M(月频)默认锚定每月最后一天,Q(季频)默认锚定每季度最后一天,同理不会以传入的自定义日期作为首个周期节点
不需要硬写循环套datetime.timedelta算日期,pandas原生支持固定间隔频率和自定义偏移,少量修改代码即可解决问题。
优化方案
1. 修改CashFlow类的频率映射逻辑
修改cashflow.py中的generate_series方法,替换默认带锚点的频率为固定间隔规则,原有业务调用逻辑不需要改动:
import datetime import pandas as pd import csv import os class CashFlow(object): def __init__(self, name='cashflow'): self.name = name self.columns = ['date_of_activity', 'description', 'amount'] self.series = pd.DataFrame(columns=self.columns) def generate_series(self, description, amount, frequency='MS', start=datetime.date(datetime.date.today().year, 1, 1), end=datetime.date(datetime.date.today().year, 12, 31)): """Generate a new data series and append it to the existing data""" # 替换默认带锚点的频率为固定间隔规则,对齐自定义起始日 freq_mapping = { 'W': '7D', # 周度改为从起始日开始每7天一笔,不对齐周日 'M': pd.DateOffset(months=1), # 月度改为从起始日开始每1个月一笔 'Q': pd.DateOffset(months=3) # 季度改为从起始日开始每3个月一笔 } # 已定义的特殊频率走映射,其余频率(如日频'D'、月初'MS')保留原有逻辑 actual_freq = freq_mapping.get(frequency, frequency) data = [(date_of_activity, description, amount) for date_of_activity in pd.date_range(start, end, freq=actual_freq)] self.series = pd.concat( [self.series, pd.DataFrame(data, columns=self.columns)]) def add_event(self, description, amount, date_of_activity): """Add a single event""" self.generate_series(description, amount, frequency='D', start=date_of_activity, end=date_of_activity), def export_to_csv(self, dirpath, filename): """ Export the data to a csv file.""" """ Sort data by 1) date_of_activity ascending and 2) amount ascending before the export.""" path = os.path.join(dirpath, filename) with open(path, 'w') as csvfile: csv_writer = csv.writer(csvfile, delimiter=',') csv_writer.writerow(self.columns) self.series.sort_values(by=['date_of_activity', 'amount'], ascending=[ True, True], inplace=True) for index, record in self.series.iterrows(): csv_writer.writerow(record)
2. (可选)使用datetime.timedelta实现自定义间隔
如果需要设置非标准周期(比如每10天一笔、每20天一笔的收支),可以直接传入datetime.timedelta对象作为frequency参数,pd.date_range原生支持该类型参数,示例:
# 每10天一笔交通支出,从2022-06-03开始计算 cf.generate_series('Traffic', -30, frequency=datetime.timedelta(days=10), start=datetime.date(2022, 6, 3), end=datetime.date(2022, 12, 29))
效果验证
修改后运行原有balance.py脚本,输出结果会完全匹配配置的起始日期:
- Food条目首条记录为
2022-06-03,之后每7天生成一条 - Rent条目首条记录为
2022-06-03,之后每月3号生成一条 - subscription条目首条记录为
2022-06-15,之后每3个月15号生成一条
内容的提问来源于stack exchange,提问作者usrxxx
相关产品推荐
相关产品推荐

