【问题标题】:How can I find all datetimes in a list within n minutes of a given value?如何在给定值的 n 分钟内找到列表中的所有日期时间?
【发布时间】:2022-01-12 14:04:21
【问题描述】:

我有两个时间列表(Hour:Min:Sec 格式),我一直在努力将 list_a 中的每个条目与 list_b 中的所有条目进行比较,以确定 30 分钟内的值:

list_a = ["10:26:42", "8:55:43", "7:34:11"]
list_b = ["10:49:20", "8:51:10", "10:34:35", "8:39:47", "7:11:49", "7:42:10"]

预期输出:

10:26:42 is within 30m of 10:49:20, 10:34:35
8:55:43 is within 30m of 8:51:10, 8:39:47
7:34:11 is within 30m of 7:11:49, 7:42:10

到目前为止,我一直在做的是:

import datetime

# Convert the Lists to Datetime Format

for data in list_a:
    convert = datetime.datetime.strptime(data,"%H:%M:%S")
    list_a_times.append(convert)

for data in list_b:
    convert = datetime.datetime.strptime(data,"%H:%M:%S")
    list_b_times.append(convert)

# Using a Value of List A, Find the Closest Value in List B

for data in list_a_times:
     closest_to_data = min(list_b_times, key=lambda d: abs(d - data))

     print(data, closest_to_data)

这种方法有效,但它只能找到一个最接近的值!只要值在所需的 30 分钟或更短的时间内,我如何操纵 min() 函数来继续提供值?

【问题讨论】:

  • 您的代码中甚至没有包含与“30 分钟”的比较。
  • 请注意,您使用的是datetime,它将时间转换为包含日期的日期时间对象(默认为 1900-1-1)。在 23:50 和 00:10 的边缘情况下,您会遇到问题。纯粹从时间上讲,这些都在 30 分钟之内,但如果包括日期,它们将相隔 23 小时和 40 小时。
  • @vaizki 我知道,我相信这是一个小误会!这就是为什么我结束了帖子,询问我如何操纵函数来做到这一点,这就是我想要弄清楚的。
  • @9769953 感谢您指出这一点!我应该使用不同的方法还是应该使用某种 if-else 树来解决这种边缘情况?
  • 我不知道;这将取决于用例和输入。上述时间实际上可能相隔 23:40 小时,或 20 小时。没有上下文,这是不可能说的。您可以记录此类边缘情况(例如,午夜后 30 分钟内的所有时间),以供将来的读者(例如您自己)查看最终结果。

标签: python list datetime min python-datetime


【解决方案1】:

IIUC,你想比较所有的组合,所以你需要检查所有。

请阅读答案末尾的datetime/timedelta

使用itertools.product

list_a = ['10:26:42', '8:55:43', '7:34:11']
list_b = ['10:49:20', '8:51:10', '10:34:35', '8:39:47', '7:11:49', '7:42:10']

import datetime
from itertools import product

str2time = lambda s: datetime.datetime.strptime(s, "%H:%M:%S")

for a,b in product(map(str2time, list_a), map(str2time, list_b)):
    if abs(a-b).total_seconds() <= 1800:
        print(f'{a:%H:%M:%S} is within 30m of {b:%H:%M:%S}')

输出:

10:26:42 is within 30m of 10:49:20
10:26:42 is within 30m of 10:34:35
08:55:43 is within 30m of 08:51:10
08:55:43 is within 30m of 08:39:47
07:34:11 is within 30m of 07:11:49
07:34:11 is within 30m of 07:42:10

使用嵌套的 for 循环:

import datetime

str2time = lambda s: datetime.datetime.strptime(s, "%H:%M:%S")

for a in map(str2time, list_a):
    start = f'{a:%H:%M:%S} is within 30m of'
    for b in map(str2time, list_b):
        if abs(a-b).total_seconds() <= 1800:
            print(f'{start} {b:%H:%M:%S}', end='')
            start = ','
    if start == ',':
        print()

输出:

10:26:42 is within 30m of 10:49:20, 10:34:35
08:55:43 is within 30m of 08:51:10, 08:39:47
07:34:11 is within 30m of 07:11:49, 07:42:10

注意datetime

使用不带日期的 datetime 将默认为 1900-01-01,这可能会在接近午夜时产生边缘效应。相反,您可以使用 timedelta 对象。使用我的代码,您需要将 str2time 函数更改为:

def str2time(s):
    h,m,s = map(int, s.split(':'))
    return datetime.timedelta(hours=h, minutes=m, seconds

并稍微修改一下代码,以便能够转换为字符串:

z = datetime.datetime(1900,1,1)

for a in map(str2time, list_a):
    start = f'{z+a:%H:%M:%S} is within 30m of'
    for b in map(str2time, list_b):
        if abs(a-b).total_seconds() <= 1800:
            print(f'{start} {z+b:%H:%M:%S}', end='')
            start = ','
    if start == ',':
        print()

【讨论】:

    【解决方案2】:

    你循环和掠夺所有元素的绝对时间差异,而不是使用min

    list_a = ["10:26:42", "8:55:43", "7:34:11"]
    list_b = ["10:49:20", "8:51:10", "10:34:35", "8:39:47", "7:11:49", "7:42:10"]
    
    import datetime
    import datetime
    
    # Convert the Lists to Datetime Format
    list_a = [datetime.datetime.strptime(d,"%H:%M:%S") for d in list_a]
    list_b = [datetime.datetime.strptime(d,"%H:%M:%S") for d in list_b]
    
    for value in list_a:
        for v in list_b:
            if abs(value-v) < datetime.timedelta(minutes=30):
                print (value, "=>", v, "diff: ", (value-v).total_seconds() // 60)
        print()
                
    

    输出:

    1900-01-01 10:26:42 => 1900-01-01 10:49:20 diff:  -23.0
    1900-01-01 10:26:42 => 1900-01-01 10:34:35 diff:  -8.0
    
    1900-01-01 08:55:43 => 1900-01-01 08:51:10 diff:  4.0
    1900-01-01 08:55:43 => 1900-01-01 08:39:47 diff:  15.0
    
    1900-01-01 07:34:11 => 1900-01-01 07:11:49 diff:  22.0
    1900-01-01 07:34:11 => 1900-01-01 07:42:10 diff:  -8.0
    

    这对于像 0:05:00 和 23:55:00 这样的日期时间会出错,因为它们位于不同的日期。

    您可以通过自己编写的增量计算来解决这个问题:

    def abs_time_diff(dt1, dt2, *, ignore_date = False):
        if not ignore_date:
            return abs(dt1-dt2)
        # use day before, this day and day after, report minimum
        return min ( (abs(dt1 + datetime.timedelta(days = delta) - dt2) 
                      for delta in range(-1,2)))
    
    list_a = ["0:5:0"]
    list_b = ["0:20:0", "23:55:0"]
    
    list_a = [datetime.datetime.strptime(d,"%H:%M:%S")  for d in list_a]
    list_b = [datetime.datetime.strptime(d,"%H:%M:%S")  for d in list_b]
    
    for value in list_a:
        for v in list_b:
            print (value, v, abs_time_diff(value,v))
            print (value, v, abs_time_diff(value,v, ignore_date = True))
    

    输出:

    1900-01-01 00:05:00 1900-01-01 00:20:00 0:15:00
    1900-01-01 00:05:00 1900-01-01 00:20:00 0:15:00
    
    1900-01-01 00:05:00 1900-01-01 23:55:00 23:50:00 # with date
    1900-01-01 00:05:00 1900-01-01 23:55:00 0:10:00  # ignores date
    

    【讨论】:

      【解决方案3】:

      我会为此提出使用pandas 的建议:

      # Convert to pandas datetime series
      import pandas as pd
      dt_a = pd.Series(list_a, dtype='datetime64[ns]')
      dt_b = pd.Series(list_b, dtype='datetime64[ns]')
      
      # Comparison loop
      interv_size = '30m'   # Thirty minutes
      for el in dt_a:
          hits = df_b.loc[ abs(el - df_b) < interv_size ].dt.time
          print(f'{el.time()} is within {interv_size} of', *hits) 
      

      优势? 你让 python 处理你的日期格式

      【讨论】:

        【解决方案4】:
        from datetime import datetime, timedelta
        
        list_a = ["10:26:42", "8:55:43", "7:34:11"]
        list_b = ["10:49:20", "8:51:10", "10:34:35", "8:39:47", "7:11:49", "7:42:10"]
        
        time_format = "%H:%M:%S"
        
        
        def convert_to_datetime(time_str):
            return datetime.strptime(time_str, time_format)
        
        
        # Overriding list_a and list_ to avoid polluting the namespace
        # Sorting for simple optimization
        list_a = sorted([convert_to_datetime(time_str) for time_str in list_a])
        list_b = sorted([convert_to_datetime(time_str) for time_str in list_b])
        
        time_range_limit_in_seconds = timedelta(minutes=30).total_seconds()
        
        result = []
        for list_a_datetime in list_a:
            with_in_time_limit = []
            for list_b_datetime in list_b:
                difference_in_seconds = (
                    list_a_datetime-list_b_datetime).total_seconds()
        
                if difference_in_seconds <= time_range_limit_in_seconds:
                    # Convert back to string
                    with_in_time_limit.append(
                        list_b_datetime.strftime(time_format)
                    )
        
                # Since the list is sorted, all the rest don't fall in time range
                if difference_in_seconds < 0:
                    break
        
            print(list_a_datetime.strftime(time_format), with_in_time_limit)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-16
          • 1970-01-01
          • 2016-01-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多