Skip to main content
 首页 » 编程设计

python之如何创建持续时间总计列表

2025年05月04日9youxin

我想根据销售额最高的连续两个月来计算奖金。所以我可以每连续两个月迭代一次总数来找到最大值,即得到

value = Max[total_between_firstdayMonth1_and_lastDayMonth2, total_between_firstdayMonth2_and_lastDayMonth3, ... , total_between_firstdaySecondToLastMonth_andlastDayLastMonth]

所以我可能需要一组日期时间对象或类似的东西。

start= model.Order.order('created').get().created # get the oldest order 
end = model.Order.order('-created').get().created # get the newest order 

所以在开始和结束之间,我必须将时间分成连续 2 个月的重叠对,例如。如果第一个订单是在 2008 年 12 月,最后一个订单是在 2011 年 11 月,那么从中选择最大值的列表应该是 [total_december2008 + total_january2009, total_january2009 + total_february2009, ... , total_october2011 + total_november2011]

但是,如果我像上面那样知道开始时间,那么如何获得第二个月的最后一天呢?如何创建时间和总计列表?

我可能无法立即创建总计列表,但如果我可以创建开始和结束列表,那么我可以调用我们可以假设的辅助函数,例如。

total(start_datetime, end_datetime)

感谢您的帮助

更新

我想我找到了如何计算示例间隔的时间,其中时间线是从任何日期到下个月的最后一天:

>>> d = date(2007,12,18) 
>>> print d 
2007-12-18 
>>> d + relativedelta(months=2) - timedelta(days=d.day) 
datetime.date(2008, 1, 31) 

更新2

我可以计算到第一级的第一个持续时间。现在我只需要概括它来遍历所有持续时间并检查哪个是最高级别:

def level(self): 
    startdate = model.Order.all().filter('status =', 'PAID').filter('distributor_id =' , self._key.id()).get().created.date() 
    last_day_nextmonth =startdate + relativedelta(months=2) - timedelta(days=1) 
    if self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) < 25: 
        maxlevel = _('New distributor') 
    elif self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)   > 25: 
        maxlevel = _('Assistant Teamleader') 
    return maxlevel 

更新3

更接近我的意思是从开始到现在取一些函数值的最大值。 Basecase 可以是下个月的最后一天是 future ,辅助函数可以是递归的,但我没有时间或帮助使其递归到它现在只适用于前 2 个时期,即从开始后的 4 个月:

def level(self): 
    startdate = model.Order.all().filter('status =', 'PAID' 
            ).filter('distributor_id =', 
                     self._key.id()).get().created.date() 
    last_day_nextmonth = startdate + relativedelta(months=2) \ 
        - timedelta(days=1) 
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) 
    if total >= 125: 
        level = 5 
    elif total >= 75: 
        level = 4 
    elif total >= 25: 
        level = 3 
    elif total >= 2: 
        level = 2 
    else: 
        level = 1 
    return self.levelHelp(level, last_day_nextmonth + timedelta(days=1)) 
 
def levelHelp(self, level, startdate): 
    #if startdate in future return level 
    last_day_nextmonth = startdate + relativedelta(months=2) \ 
        - timedelta(days=1) 
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) 
    if total >= 125: 
        newlevel = 5 
    elif total >= 75: 
        newlevel = 4 
    elif total >= 25: 
        newlevel = 3 
    elif total >= 2: 
        newlevel = 2 
    else: 
        newlevel = 1 
    return level if level > newlevel else newlevel 

更新4

我添加了递归,其中基本情况是下一步是在未来,如果是这样它将返回最大级别:

def level(self): 
    startdate = model.Order.all().filter('status =', 'PAID' 
            ).filter('distributor_id =', 
                     self._key.id()).get().created.date() 
    last_day_nextmonth = startdate + relativedelta(months=2) \ 
        - timedelta(days=1) 
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) 
    if total >= 125: 
        level = 5 
    elif total >= 75: 
        level = 4 
    elif total >= 25: 
        level = 3 
    elif total >= 2: 
        level = 2 
    else: 
        level = 1 
    return self.levelHelp(level, last_day_nextmonth + timedelta(days=1)) 
 
def levelHelp(self, level, startdate): 
 
    last_day_nextmonth = startdate + relativedelta(months=2) \ 
        - timedelta(days=1) 
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) 
    if total >= 125: 
        newlevel = 5 
    elif total >= 75: 
        newlevel = 4 
    elif total >= 25: 
        newlevel = 3 
    elif total >= 2: 
        newlevel = 2 
    else: 
        newlevel = 1 
 
    maxlevel = level if level > newlevel else newlevel 
 
    nextstart = last_day_nextmonth + timedelta(days=1) 
    now = datetime.now().date() 
    if nextstart > now: #next start in is the future 
        return maxlevel 
    else: return self.levelHelp(maxlevel, nextstart) 

请您参考如下方法:

这对于函数式方法来说听起来不错。最后有一个完整的工作示例,但我只想强调以 FP 风格编写的核心功能的优雅和简单:

def find_best_two_months(orders): 
  first  = lambda x: x[0] 
  second = lambda x: x[1] 
 
  orders_by_year_and_month = [  
    ("%04d-%02d" % (date.year, date.month), amount)  
    for date, amount in orders] 
 
  sorted_orders = sorted(orders_by_year_and_month, key=first) 
 
  totals_by_month = [  
    (ym, sum(map(second, groupped_orders)))  
    for ym, groupped_orders in groupby(sorted_orders, key=first)] 
 
  totals_two_months = [  
    ( "%s - %s" % (m1[0], m2[0]), m1[1]+m2[1] )  
    for m1, m2 in zip(totals_by_month, totals_by_month[1:]) ] 
 
  return max(totals_two_months, key=second) 

这是一个带有注释的完整工作示例:

#!/usr/bin/python 
from random import randint 
from datetime import date, timedelta 
from itertools import groupby 
 
""" finding best two months the functional way """ 
 
def find_best_two_months(orders): 
  """ 
  Expect a list of tuples of form (date_of_order, amount): 
 
  [ (date1, amount1), (date2, amount2), ...] 
  """ 
 
  " helper functions for extracting first or second from tuple " 
  first  = lambda x: x[0] 
  second = lambda x: x[1] 
 
  " converts [(date, amount)] -> [(YYYY-MM, amount)] " 
  orders_by_year_and_month = [ ("%04d-%02d" % (date.year, date.month), amount) for date, amount in orders] 
 
  " Sorts by YYYY-MM. This step can be omitted if orders were already sorted by date" 
  sorted_orders = sorted(orders_by_year_and_month, key=first) 
 
  " Compresses orders from the same month, so ve get [(YYYY-MM), total_amount_of_orders]" 
  totals_by_month = [ (ym, sum(map(lambda x:x[1], groupped_orders)))  
    for ym, groupped_orders in groupby(sorted_orders, key=first)] 
 
  " Zips orders to two month periods" 
  totals_two_months = [ ("%s - %s" % (m1[0], m2[0]), m1[1]+m2[1]) for m1, m2 in zip(totals_by_month, totals_by_month[1:]) ] 
 
  " Returns two-month period with maximum total amount. If there were many periods with max amount, only the first is returned " 
  return max(totals_two_months, key=second) 
 
"""  
this code is for generating random list of orders  
and is not a part of the solution  
""" 
MIN_AMOUNT=70 
MAX_AMOUNT=500 
MAX_DAY_SPREAD=5 
 
def gen_order(last_date): 
  """ returns (order_date, amount) """ 
  days = timedelta() 
  return ( 
      last_date+timedelta(days=randint(0, MAX_DAY_SPREAD)),  # new date 
      randint(MIN_AMOUNT, MAX_AMOUNT)) # amount 
 
def gen_orders(total, start_date): 
  orders = [] 
  last_date = start_date 
  for i in range(total): 
    order = gen_order(last_date) 
    orders.append(order) 
    last_date = order[0] 
  return orders 
 
if __name__ == "__main__": 
  orders = gen_orders(300, date(2010,1,1))  
  print find_best_two_months(orders)