获取长向量中最小值索引的有效方法,python
Posted
技术标签:
【中文标题】获取长向量中最小值索引的有效方法,python【英文标题】:Efficient way to get index of minimum value in long vector, python 【发布时间】:2011-08-28 00:41:37 【问题描述】:我有一长串经度值 (len(Lon) = 420481) 和另一个纬度值。我想找到经度最小值对应的纬度。
我试过了:
SE_Lat = [Lat[x] for x,y in enumerate(Lon) if y == min(Lon)]
但这需要很长时间才能完成。
有人知道更有效的方法吗?
也许您对此也有建议: 我现在尝试找到与新经度最接近的对应纬度,该经度不在原始经度向量中。我试过这个:
minDiff = [min(abs(x - lon_new) for x in lons)] # not very quick, but works
[(lat,lon) for lat,lon in izip(lats,lons) if abs(lon-lon_new)==minDiff]
最后一行抛出错误,因为有多个匹配项。我目前不知道如何只找到一个值,让我们说第一个。非常感谢任何帮助!
【问题讨论】:
【参考方案1】:我可以推荐 numpy 吗?
import numpy
nplats = numpy.array(lats)
nplons = numpy.array(lons)
# this part is 20x faster than using the built-in python functions
index = numpy.argmin(nplats)
print nplats[index], nplons[index]
这比 min(izip()) 解决方案快得多(在使用 420481 个随机创建的记录时使用我的设置大约 20 倍),当然您需要将数据值存储在 numpy 中以利用这种速度向上。
【讨论】:
【参考方案2】:与其直接使用解决此问题的众多替代方法之一(可以在其他答案中看到),不如列举为什么原始示例中的代码如此缓慢。
SE_Lat = [Lat[x] for x,y in enumerate(Lon) if y == min(Lon)]
我们从 OP 得知 len(Lon) == 420481
。现在,找到最小值是一个 O(N) 操作(您必须至少查看每个值一次)。在列表推导中,每次迭代都会重新评估条件。上面的代码在每次通过循环时重新计算最小值,将原本应该是 O(N) 的操作变为 O(N^2)(在这种情况下,只有 177 十亿 次迭代) .
简单地将min(Lon)
的结果缓存在一个局部变量中并在循环条件中使用它而不是每次迭代都重新计算它,这可能会将运行时间降低到可接受的水平。
但是,我个人会采用的方式(假设我以后想要所有的纬度、经度和索引):
min_longitude, min_index = min(longitude, index for index, longitude in enumerate(Lon))
min_latitude = Lat[min_index]
虽然有很多可能性,但哪一种最好会根据具体的用例而有所不同。
【讨论】:
【参考方案3】:这是我原来的答案:
>>> lats = [1,2,3,4]
>>> lons = [5,4,8,9]
>>> from itertools import izip
>>> min(izip(lats,lons), key=lambda x:x[1])
(2, 4)
但我看到 OP 似乎允许以最小 lon 值进行多个匹配,为此,我认为没有单行。诀窍是,您只想找到一次 min(lons),而不是每个 lat,lon 对一次:
>>> lats = [1,2,3,4]
>>> lons = [5,4,8,4]
>>> minlon = min(lons)
>>> [(lat,lon) for lat,lon in izip(lats,lons) if lon==minlon]
[(2, 4), (4, 4)]
这个单行代码可能对你有用,因为 lambda 参数 minlon 应该只计算一次:
>>> filter(lambda latlon,minlon=min(lons):latlon[1]==minlon, izip(lats,lons))
[(2, 4), (4, 4)]
但不确定它在 420481 元素列表上的效果如何。为了可读性和长期支持,我可能会选择更明确的 2-liner 解决方案。
最后一点: 有时您只能通过一个序列,例如当它是一个迭代器或生成器的输出时。为了支持多个匹配并只通过两个列表,这是我能做的最好的:
from itertools import izip
def get_lats_at_min_lon(lats, lons):
minlon = 200
minlats = []
for lat,lon in izip(lats, lons):
if lon < minlon:
minlats = [lat]
minlon = lon
elif lon == minlon:
minlats.append(lat)
return minlon, minlats
lats = iter([1,2,3,4])
lons = iter([5,4,8,4])
print get_lats_at_min_lon(lats,lons)
打印:
(4, [2, 4])
【讨论】:
感谢大家的回答!您建议的几乎所有内容都运行良好且快速。我使用了带过滤器的单线,效果很好。【参考方案4】:pairs = zip(latitudes, longitudes)
minLonPair = min(pairs, key=lambda p:p[1])
print(minLonPair[0])
根据 Ignacio 的解决方案,如果您使用的是 python2,您将希望使用 izip
而不是 zip
。然而,这对于你在 python2 中所做的一切都是正确的。
【讨论】:
【参考方案5】:只要先找到索引:
index = min(enumerate(Lon), key=operator.itemgetter(1))[1]
Lat[index]
【讨论】:
你确定最后的[1]
吗?我认为应该是[0]
,因为它是您想要的索引。【参考方案6】:
min(itertools.izip(Lat, Lon), key=operator.itemgetter(1))[0]
【讨论】:
导入 itertools 的lazy-zip 是必要的,因为找到最小值必须查看每个元素,因此会扩展迭代器中的每个元素(另外在 python3 中,zip 是默认的惰性) 那还是很多元素,一开始生成列表会很慢。 这在python3中不是问题,但是经过测试,你对python2是正确的。 +1 =) -- 作为记录,只需在 python 和 python3 中同时使用 zip 和 izip 执行x=min(zip(range(10**6)))
; zip
在 python3 中很快,izip
在 python2 中一样快,zip
在 python2 中非常慢。以上是关于获取长向量中最小值索引的有效方法,python的主要内容,如果未能解决你的问题,请参考以下文章
Python | 快速获取某一列数组中前 N 个最大值/最小值的索引 | 三种方法总结