获取字典的缺失日期并对它进行排序

时间:2019-04-24 09:01:38

标签: python python-3.x dictionary

我有下一本字典,我需要获取确定期间之间的缺失期,换句话说,获取一个期间的结束日期与下一个开始日期之间的期间。

{'0': {'enddate': u'2015/08/31',
       'startdate': u'2015/01/01'},
 '1': {'enddate': u'2018/10/31',
       'startdate': u'2017/01/01'},
 '2': {'enddate': u'2019/03/29',
       'startdate': u'2019/01/01'}}

The function who get this data is the next:

def periods(periods):

    total_periods={}
    for period in periods:
        total_periods[period] = {}
        for  startdate in periods[period][0]:
            total_periods[period]['startdate'] = startdate
        for enddate in periods[period][-1]:
            total_periods[period]['enddate'] = enddate

I have the next code that get the missing periods but i cant to order whith my expect results.

gaps={}
s=[]
e=[]
for i in period:
    s.append(datetime.strftime(datetime.strptime(period[i]['enddate'],'%Y/%m/%d')+timedelta(days=1),'%Y/%m/%d'))
    e.append(datetime.strftime(datetime.strptime(period[i]['startdate'],'%Y/%m/%d')+timedelta(days=-1),'%Y/%m/%d'))

for i in range(len(s)):
    if i==len(s)-1:
        break
    d={}
    d['startdate']=s[i]
    d['enddate']=e[i+1]
    gaps[str(i)]=d

The output of these code is the next:

{'0': {'enddate': '2014/12/31', 'startdate': '2018/11/01'},
 '1': {'enddate': '2018/12/31', 'startdate': '2015/09/01'}}

但是错了,因为我需要下一个结果:

{'0': {'enddate': '2016/12/31', 'startdate': '2015/09/01'},
 '1': {'enddate': '2018/12/31', 'startdate': '2018/11/01'}}

希望我能正确解释并清除问题。

预先感谢

4 个答案:

答案 0 :(得分:0)

问题出在这一行:

for i in period:

字典不排序,因此不能保证在将它们循环时将按什么顺序访问这些项。您的其余代码假定它们将按顺序排列。试试这个:

for i in sorted(period):

但是请注意,如果您有更多元素对字符串进行排序,例如'8','9','10','11',则可能无法满足您的要求。

答案 1 :(得分:0)

好的,我想您没有很清楚地解释如何确定月经间隔。我查看了您的代码,我认为这就是您想要的:

from datetime import datetime, timedelta

periods = {'0': {'enddate': u'2015/08/31', 'startdate': u'2015/01/01'},
            '1': {'enddate': u'2018/10/31', 'startdate': u'2017/01/01'},
            '2': {'enddate': u'2019/03/29', 'startdate': u'2019/01/01'}}

gaps = {}
for i in range(len(periods) - 1):
    gap_period = {}
    gap_period['startdate'] = datetime.strftime(datetime.strptime(periods[str(i)]['enddate'], r'%Y/%m/%d') + timedelta(days=1), r'%Y/%m/%d')
    gap_period['enddate'] = datetime.strftime(datetime.strptime(periods[str(i+1)]['startdate'], r'%Y/%m/%d') + timedelta(days=-1), r'%Y/%m/%d')
    gaps[str(i)] = gap_period

>>>gaps
{'0': {'startdate': '2015/09/01', 'enddate': '2016/12/31'}, '1': {'startdate': '2018/11/01', 'enddate': '2018/12/31'}}

注意:您的空缺期由 Period i-1 的结束日期之后的一天和 Period i 的开始日期之前的一天定义>

答案 2 :(得分:0)

请注意,如果句点重叠,则某些方法将不起作用。如果周期重叠,则需要更复杂的算法。

首先,一些定义:

>>> from datetime import datetime, timedelta

>>> dates_by_num = {'0': {'enddate': '2015/08/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2018/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}}
>>> def to_date(s): return datetime.strptime(s, r'%Y/%m/%d')
>>> def to_string(d): return datetime.strftime(d, r'%Y/%m/%d')

关键思想是对所有日期进行排序,并保留一个值来指示它是开始(1)还是结束(-1)日期。

>>> dates = sorted(d_s  for _, v in dates_by_num.items() for d_s in ((to_date(v['startdate']), 1), (to_date(v['enddate']), -1)))
>>> dates
[(datetime.datetime(2015, 1, 1, 0, 0), 1), (datetime.datetime(2015, 8, 31, 0, 0), -1), (datetime.datetime(2017, 1, 1, 0, 0), 1), (datetime.datetime(2018, 10, 31, 0, 0), -1), (datetime.datetime(2019, 1, 1, 0, 0), 1), (datetime.datetime(2019, 3, 29, 0, 0), -1)]

现在,我们遍历日期并保持c的间隔数:每个开始日期,c增加,每个结束日期,c减少,因此c是开始日期数减去结束日期数。当(且仅当)c == 0时,我们没有时间了。

>>> gaps = []
>>> last_c, last_d = 1, 0
>>> c = 0
>>> for d, s in dates:
...     c += s
...     assert c >= 0 # c is the number of intervals we are in
...     if last_c == 0 and d - last_d > timedelta(days=1): # we were out of all the intervals
...         gaps.append({'startdate': to_string(last_d+timedelta(days=1)), 'endate': to_string(d+timedelta(days=-1))})
...     last_c, last_d = c, d
...

我添加了一个测试以消除空隙(d - last_d > timedelta(days=1))。要获得您想要的演示文稿:

>>> {str(i): d for i, d in enumerate(gaps)}
{'0': {'startdate': '2015/09/01', 'endate': '2016/12/31'}, '1': {'startdate': '2018/11/01', 'endate': '2018/12/31'}}

代码:

from datetime import datetime, timedelta

def to_date(s): return datetime.strptime(s, r'%Y/%m/%d')
def to_string(d): return datetime.strftime(d, r'%Y/%m/%d')

def find_gaps(dates_by_num):
    dates = sorted(d_s  for _, v in dates_by_num.items() for d_s in ((to_date(v['startdate']), 1), (to_date(v['enddate']), -1)))
    gaps = []
    last_c, last_d = 1, 0
    c = 0
    for d, s in dates:
        c += s
        assert c >= 0 # c is the number of interval we are in
        if last_c == 0 and d - last_d > timedelta(days=1): # we were not in any interval
            gaps.append({'startdate': to_string(last_d+timedelta(days=1)), 'endate': to_string(d+timedelta(days=-1))})
        last_c, last_d = c, d
    return {str(i): d for i, d in enumerate(gaps)}

示例:

>>> find_gaps({'0': {'enddate': '2018/08/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2018/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}})
{'0': {'startdate': '2018/11/01', 'endate': '2018/12/31'}}
>>> find_gaps({'0': {'enddate': '2016/12/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2018/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}})
{'0': {'startdate': '2018/11/01', 'endate': '2018/12/31'}}
>>> find_gaps({'0': {'enddate': '2016/08/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2019/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}})
{'0': {'startdate': '2016/09/01', 'endate': '2016/12/31'}}

答案 3 :(得分:-1)

您没有告诉您正在使用哪个版本的Python,但是请注意,字典中的元素顺序不会保留。如果希望字典保持顺序,则应使用collections.OrderedDict代替:https://docs.python.org/2/library/collections.html#collections.OrderedDict