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