Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在表示数字的字符串集合中查找最接近的匹配项_Python_Search - Fatal编程技术网

Python 在表示数字的字符串集合中查找最接近的匹配项

Python 在表示数字的字符串集合中查找最接近的匹配项,python,search,Python,Search,我有一个文本格式的日期时间排序列表。每个条目的格式为“2009-09-10T12:00:00” 我想找到离目标最近的条目。这里的条目比我需要搜索的数量要多得多 我可以将每个条目更改为一个数字,然后进行数字搜索(例如,接近),但这似乎是多余的努力 有没有比这更好的方法: def mid(res, target): #res is a list of entries, sorted by dt (dateTtime) #each entry is a dict with a dt and som

我有一个文本格式的日期时间排序列表。每个条目的格式为“2009-09-10T12:00:00”

我想找到离目标最近的条目。这里的条目比我需要搜索的数量要多得多

我可以将每个条目更改为一个数字,然后进行数字搜索(例如,接近),但这似乎是多余的努力

有没有比这更好的方法:

def mid(res, target): 
#res is a list of entries, sorted by dt (dateTtime) 
#each entry is a dict with a dt and some other info
    n = len(res)
    low = 0
    high = n-1

    # find the first res greater than target
    while low < high:
        mid = (low + high)/2
        t = res[int(mid)]['dt']
        if t < target:
            low = mid + 1
        else:
            high = mid

    # check if the prior value is closer
    i = max(0, int(low)-1)
    a = dttosecs(res[i]['dt'])
    b = dttosecs(res[int(low)]['dt'])
    t = dttosecs(target)
    if abs(a-t) < abs(b-t):
        return int(low-1)
    else:
        return int(low)

import time
def dttosecs(dt):
    # string to seconds since the beginning
    date,tim = dt.split('T')
    y,m,d = date.split('-')
    h,mn,s = tim.split(':')
    y = int(y)
    m = int(m)
    d = int(d)
    h = int(h)
    mn = int(mn)
    s = min(59,int(float(s)+0.5)) # round to neatest second
    s = int(s)
    secs = time.mktime((y,m,d,h,mn,s,0,0,-1))
    return secs
def mid(分辨率,目标):
#res是一个条目列表,按dt(dateTtime)排序
#每个条目都是带有dt和一些其他信息的dict
n=len(res)
低=0
高=n-1
#找到第一个大于目标的分辨率
低<高:
中=(低+高)/2
t=res[int(mid)]['dt']
如果t<目标:
低=中+1
其他:
高=中
#检查先验值是否更接近
i=最大值(0,整数(低)-1)
a=dttosecs(res[i]['dt'])
b=dttosecs(res[int(低)]['dt'])
t=dttosecs(目标)
如果abs(a-t)
您希望从标准库中获取。它将执行二进制搜索,并告诉您新值在已排序列表中的正确插入点。下面的示例将打印列表中插入
target
的位置:

from bisect import bisect
dates = ['2009-09-10T12:00:00', '2009-09-11T12:32:00', '2009-09-11T12:43:00']
target = '2009-09-11T12:40:00'
print bisect(dates, target)
从这里,您可以比较插入点前后的内容,在本例中,插入点是
日期[i-1]
日期[i]
,以查看哪一个最接近您的
目标

import bisect

def mid(res, target):
    keys = [r['dt'] for r in res]
    return res[bisect.bisect_left(keys, target)]
import datetime
def parse_dt(dt):
    return datetime.strptime( dt, "%Y-%m-%dT%H:%M:%S" )
这消除了很多“努力”

将此视为搜索

def mid( res, target ):
    """res is a list of entries, sorted by dt (dateTtime) 
       each entry is a dict with a dt and some other info
    """
    times = [ parse_dt(r['dt']) for r in res ]
    index= bisect( times, parse_dt(target) )
    return times[index]
这看起来不太像“努力”。这也不取决于时间戳的格式是否正确。您可以更改为任何时间戳格式,并确保此格式始终有效。

不建议使用“复制和粘贴编码”(将
的源代码一分为二),因为这会带来各种成本(需要测试和维护大量额外的源代码,难以处理复制的上游代码的升级等);重用标准库模块的最佳方法就是导入并使用它们

然而,将字典转换成有意义的可比条目的过程是O(N),这(即使过程的每一步都很简单)最终将淹没O(logn)搜索时间合适。既然
bisect
不能支持
key=
键提取器,比如
sort
的功能,那么这个难题的解决方案是什么?如何通过导入和调用重用
bisect
,而无需预先的O(N)步骤

正如大卫·韦勒所说,“计算机科学中的所有问题都可以通过另一种间接的方法来解决”。
import bisect

listofdicts = [
  {'dt': '2009-%2.2d-%2.2dT12:00:00' % (m,d) }
  for m in range(4,9) for d in range(1,30)
  ]

class Indexer(object):
  def __init__(self, lod, key):
    self.lod = lod
    self.key = key
  def __len__(self):
    return len(self.lod)
  def __getitem__(self, idx):
    return self.lod[idx][self.key]


lookfor = listofdicts[len(listofdicts)//2]['dt']

def mid(res=listofdicts, target=lookfor):
    keys = [r['dt'] for r in res]
    return res[bisect.bisect_left(keys, target)]

def midi(res=listofdicts, target=lookfor):
    wrap = Indexer(res, 'dt')
    return res[bisect.bisect_left(wrap, target)]

if __name__ == '__main__':
  print '%d dicts on the list' % len(listofdicts)
  print 'Looking for', lookfor
  print mid(), midi()
assert mid() == midi()
输出(仅运行此
indexer.py
作为检查,然后使用
timeit
,两种方式):

如您所见,即使在列表中有145个条目的普通任务中,间接方法的性能也比“密钥提取过程”方法好三倍。由于我们比较了O(N)与O(logn),因此间接方法的优势随着N的增加而无限增长。(对于非常小的N,由于间接性,乘法常数越高,键提取方法的速度越快,但很快就被大O差异所超越)。无可否认,Indexer类是额外的代码——但是,它可以在二进制搜索按每个dict中的一个条目排序的dict列表的所有任务中重复使用,因此在您的“集装箱公用事业背后的把戏”提供了良好的投资回报

对于主搜索循环而言,对于将两个条目(下面一个和一个正好在目标上方)和目标转换为几秒钟的次要任务,再考虑一个更高的重用方法,即:

import time

adt = '2009-09-10T12:00:00'

def dttosecs(dt=adt):
    # string to seconds since the beginning
    date,tim = dt.split('T')
    y,m,d = date.split('-')
    h,mn,s = tim.split(':')
    y = int(y)
    m = int(m)
    d = int(d)
    h = int(h)
    mn = int(mn)
    s = min(59,int(float(s)+0.5)) # round to neatest second
    s = int(s)
    secs = time.mktime((y,m,d,h,mn,s,0,0,-1))
    return secs

def simpler(dt=adt):
  return time.mktime(time.strptime(dt, '%Y-%m-%dT%H:%M:%S'))

if __name__ == '__main__':
  print adt, dttosecs(), simpler()
assert dttosecs() == simpler()

在这里,重用方法没有性能优势(事实上,相反,
dttosecs
更快)--但是,无论DICT列表上有多少条记录,每次搜索只需执行三次转换,因此不清楚性能问题是否有密切关系。同时,使用
simpler
只需编写、测试和维护一行简单的代码,而
dttosecs
只有十几行;考虑到这一比率o、 在大多数情况下(即,排除绝对瓶颈),我更喜欢
更简单的
。重要的是要了解这两种方法以及它们之间的权衡,以确保做出明智的选择。

您的缩进是错误的,请修复它。if和else子句是从while缩进的,而不是从同一级别缩进的。”这似乎是多余的努力?怎么会呢?解析日期并创建适当的
datetime
对象很简单。为什么不转换成适当的
datetime
对象呢?为什么要使用字符串?我使用的是bisect中的算法(请参阅bisect.py中的源代码)。我这样做而不是使用bisect,因为我的列表是[{'dt':dt1,'x':x1,'y':y1},{},{},{,…]而不是[dt1,dt2,…]。除了使用对分模块而不是对分算法之外,您的建议与我正在做的有什么不同?还是说对分是用C实现的?将您的输入转换为对分代码并对其进行测试比将其进行测试更容易,这在标准库中已经有效地完成了。如果我使用bisect代码,我不知道如何在中使用该代码
import time

adt = '2009-09-10T12:00:00'

def dttosecs(dt=adt):
    # string to seconds since the beginning
    date,tim = dt.split('T')
    y,m,d = date.split('-')
    h,mn,s = tim.split(':')
    y = int(y)
    m = int(m)
    d = int(d)
    h = int(h)
    mn = int(mn)
    s = min(59,int(float(s)+0.5)) # round to neatest second
    s = int(s)
    secs = time.mktime((y,m,d,h,mn,s,0,0,-1))
    return secs

def simpler(dt=adt):
  return time.mktime(time.strptime(dt, '%Y-%m-%dT%H:%M:%S'))

if __name__ == '__main__':
  print adt, dttosecs(), simpler()
assert dttosecs() == simpler()