Эффективный способ получить индекс минимального значения в длинном векторе, python

у меня есть длинный список значений долготы (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]

последняя строка вызывает ошибку, потому что есть несколько совпадений. На данный момент я не знаю, как найти только одно значение, скажем, первое. Любая помощь очень ценится!

6 ответов


могу я порекомендовать 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 ()) (~20x, используя мою настройку при использовании 420481 случайно созданных записей), хотя, конечно, вам нужно будет сохранить значения данных в numpy, чтобы воспользоваться этой скоростью.


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

вместо того, чтобы прыгать прямо с одной из многих альтернатив для решения этого (что можно увидеть в других ответах), стоит перечислить почему код в исходном примере настолько медленный.

SE_Lat = [Lat[x] for x,y in enumerate(Lon) if y == min(Lon)]

мы знаем из ОП, что 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]

есть много возможностей, хотя, и какой из них лучше зависит от точного варианта использования.


просто сначала найдите индекс:

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

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

согласно решению Игнасио, если вы используете python2, вы захотите использовать izip, а не zip. Это, однако, верно для всего, что вы делаете в python2.


вот мой первоначальный ответ:

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

этот однострочный может работать для вас, так как лямбда-аргумент minlon должен быть вычислен только один раз:

>>> filter(lambda latlon,minlon=min(lons):latlon[1]==minlon, izip(lats,lons))
[(2, 4), (4, 4)]

не уверен, насколько хорошо он будет работать Однако 420481-списки элементов. И для удобочитаемости и долгосрочной поддержки я, вероятно, выбрал бы более явное 2-линейное решение.

последний пункт: Иногда вы получаете только один проход через последовательность, например, когда это итератор или выход генератора. Чтобы поддержать несколько матчей и пройти только один проход через два списка, это было лучшее, что я мог сделать:

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