高效获取长向量中最小值索引的方法,Python

9

我有一长串经度数值(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]

最后一行会抛出错误,因为有多个匹配项。目前我不知道如何只找到一个值,比如第一个。非常感谢您的帮助!

6个回答

7

我可以推荐使用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中才能利用此加速。


6
min(itertools.izip(Lat, Lon), key=operator.itemgetter(1))[0]

导入itertools的lazy-zip是否必要,因为查找最小值必须查看每个元素,因此将展开迭代器中的每个元素(此外,在Python3中,zip默认是惰性的)。 - ninjagecko
这仍然是很多元素,而且首先生成列表会很慢。 - Ignacio Vazquez-Abrams
1
еңЁPython3дёӯиҝҷдёҚжҳҜй—®йўҳпјҢдҪҶз»ҸиҝҮжөӢиҜ•пјҢдҪ еҜ№Python2жҳҜжӯЈзЎ®зҡ„гҖӮ+1 =пјү-- дёәдәҶи®°еҪ•пјҢеҸӘйңҖеңЁPythonе’ҢPython3дёӯдҪҝз”Ёzipе’Ңizipжү§иЎҢx=min(zip(range(10**6)))пјӣеңЁPython3дёӯпјҢzipеҫҲеҝ«пјҢиҖҢеңЁPython2дёӯпјҢizipеҗҢж ·еҝ«пјҢиҖҢzipйқһеёёж…ўгҖӮ - ninjagecko

4

不要立即使用解决此问题的众多替代方案(可在其他答案中看到),值得列举一下原始示例中的代码为什么如此缓慢。

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]

虽然有很多可能性,但哪种最好取决于具体的使用情况。


0
pairs = zip(latitudes, longitudes)
minLonPair = min(pairs, key=lambda p:p[1])
print(minLonPair[0])

根据Ignacio的解决方案,如果您使用的是Python2,则应该使用izip而不是zip。然而,在Python2中,无论您做什么都是如此。

0

这是我的原始答案:

>>> 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)

但我看到原帖似乎允许最小经度值有多个匹配项,对于这种情况,我认为没有一行代码可以解决。关键在于,你只想找到一次 min(lons),而不是每个纬度和经度对都找一次:

>>> 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元素列表上的表现如何。为了可读性和长期支持,我可能会选择更明确的两行解决方案。

最后一点: 有时您只能通过一个序列进行一次遍历,例如当它是迭代器或生成器的输出时。为了支持多个匹配并仅通过两个列表进行一次遍历,这是我所能做的最好的:

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])

感谢大家的回答!你们提出的几乎所有建议都很好用,而且速度很快。我使用了带有过滤器的一行代码,效果非常棒。 - Ronja

0

首先找到索引:

index = min(enumerate(Lon), key=operator.itemgetter(1))[1] 
Lat[index]

1
你确定最终的 [1] 吗?我认为应该是 [0],因为这是你想要的索引。 - tzot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接