Python 补充缺失的日期,你会怎么做?

花菜 · 2022年07月06日 · 最后由 Tester_谜城 回复于 2022年07月08日 · 2843 次阅读

问题

有一个列表,里面元素是 dict
dict 有个 key 是表示日期的
现在知道开始和结束日期,按天计算
希望把列表中缺少的日期补充完成
比如:
开始日期是 2022-06-19,结束日期是 2022-06-22
[{"dt": "2022-06-20","key": 33}, {"dt": "2022-06-22","key": 45}]
这里就缺少了 2022-06-19 和 2022-06-21 的数据
需要把这个两个日期补上

思路

先根据开始和结束日期生成一个日期列表
创建一个新的列表
然后遍历日期列表,

  • 如果日期存在,就取现有的元素,放入新列表
  • 否则就生成一个该日期的默认值

代码

import time
import datetime
import copy

import arrow
from typing import List


def timestamp2datetime(timestamp, fmt='%Y-%m-%d %H:%M:%S') -> str:
    """timestamp to date string"""
    return time.strftime(fmt, time.localtime(timestamp))

def get_full_dt_sequence(
    *,
    start_ts: int,  # timestamp, ms
    end_ts: int,  # timestamp, ms
    source: List[dict],
    date_key: str,
    date_fmt: str = '%Y-%m-%d',
) -> List[dict]:
    """
    >>> s = [{'dt': '2020-01-02', 'value': 1}, {'dt': '2020-01-04', 'value': 2}]
    >>> get_full_dt_sequence(start_ts=1577808000000, end_ts=1578182400000, source=s, date_key='dt')
    [{'dt': '2020-01-01', 'value': 0}, {'dt': '2020-01-02', 'value': 1}, {'dt': '2020-01-03', 'value': 0}, {'dt': '2020-01-04', 'value': 2}, {'dt': '2020-01-05', 'value': 0}]

    >>> s = [{'dt': '2020-01-02', 'value': 1}, {'dt': '2020-01-04', 'value': 2}]
    >>> get_full_dt_sequence(start_ts=None, end_ts=None, source=s, date_key='dt')
    [{'dt': '2020-01-02', 'value': 1}, {'dt': '2020-01-03', 'value': 0}, {'dt': '2020-01-04', 'value': 2}]
    """

    if not source:
        return source
    if start_ts is None:
        start_ts: int = datestr2timestamp(source[0][date_key], fmt=date_fmt, ts='ms')
    if end_ts is None:
        end_ts: int = datestr2timestamp(source[-1][date_key], fmt=date_fmt, ts='ms')

    start_dt = arrow.get(timestamp2datetime(start_ts / 1000))
    end_dt = arrow.get(timestamp2datetime(end_ts / 1000))
    date_range: List[str] = [str(arrow_obj.date()) for arrow_obj in
                             arrow.Arrow.range('day', start=start_dt, end=end_dt)]
    default_item: dict = copy.deepcopy(source[0])
    # set default value
    for k in default_item.keys():
        if k != date_key:
            default_item[k] = 0

    new_list: List[dict] = []
    source_date_list: List[str] = [str(item[date_key]) for item in source]
    for date in date_range:
        if date in source_date_list:
            # use source data
            item: dict = source[source_date_list.index(date)]
            new_list.append(item)
        else:
            # use default value
            default_item_copy = copy.deepcopy(default_item)
            default_item_copy[date_key] = date
            new_list.append(default_item_copy)
    return new_list

if __name__ == '__main__':
    import doctest

    doctest.testmod()

总结

  • 创建新列表来接收结果,实现起来更加简单
  • 设置默认值时,需要深拷贝,否则会修改原始数据
  • 默认元素多次复制时,同样需要使用深拷贝
共收到 6 条回复 时间 点赞

交作业啦

from datetime import timedelta
from datetime import datetime
def fill_data(start_time, end_time, date_map):
    """补充数据"""
    from datetime import timedelta
    start = start_time
    weekly_data = []
    while start <= end_time:
        date = start.strftime("%Y-%m-%d")
        weekly_data.append(dict(dt=date, key=date_map.get(date).get('key') if date in date_map else 0))
        start += timedelta(days=1)
    return weekly_data

if __name__ == '__main__':
    data = [{"dt": "2022-06-20", "key": 33}, {"dt": "2022-06-22", "key": 45}]
    date_map = {i['dt']: dict(key=i['key']) for i in data}
    _day = datetime.strptime('2022-06-22','%Y-%m-%d')
    last_7_day = (_day - timedelta(days=6))
    print(fill_data(last_7_day, _day, date_map))

看懵了,看完题目,再看看大佬们大答案,跟我想的差别好大啊

这个实现入参之前还要做比较多操作哦
并且,如果列表字典的字段发生变化,还要每次都调整入参和函数里面的内容
所以,函数的入参是开始时间,结束时间,原始列表,还有日期的 key 就行

最好用 doctest 加上测试,print 这个不够直观呢
typehint 标注入参和出参类型是挺好的

花菜 回复

大佬牛逼🐂🍺

围观大佬, 学习了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册