source

인덱스를 알고 목록의 여러 요소에 액세스

goodcode 2022. 9. 8. 22:00
반응형

인덱스를 알고 목록의 여러 요소에 액세스

지정된 목록에서 인덱스를 알고 몇 가지 요소를 선택해야 합니다.지정된 목록 [-2, 1, 5, 3, 8, 5, 6]에서 인덱스가 1, 2, 5인 요소를 포함하는 새 목록을 만들고 싶다고 가정합니다.내가 한 일은:

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [ a[i] for i in b]

c=a[b]와 같은 더 좋은 방법은 없을까?

다음을 사용할 수 있습니다.

from operator import itemgetter 
a = [-2, 1, 5, 3, 8, 5, 6]
b = [1, 2, 5]
print(itemgetter(*b)(a))
# Result:
(1, 5, 5)

또는 numpy를 사용할 수 있습니다.

import numpy as np
a = np.array([-2, 1, 5, 3, 8, 5, 6])
b = [1, 2, 5]
print(list(a[b]))
# Result:
[1, 5, 5]

하지만 현재의 솔루션은 문제 없습니다.아마 그중에서 제일 깔끔할 거예요.

다른 방법:

>>> map(a.__getitem__, b)
[1, 5, 5]

>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)

또 다른 솔루션은 Panda Series를 통한 것입니다.

import pandas as pd

a = pd.Series([-2, 1, 5, 3, 8, 5, 6])
b = [1, 2, 5]
c = a[b]

다음으로 필요에 따라 c를 목록으로 변환할 수 있습니다.

c = list(c)

제공된 5가지 답변의 실행 시간을 비교하는 기본적이고 그다지 광범위하지 않은 테스트:

def numpyIndexValues(a, b):
    na = np.array(a)
    nb = np.array(b)
    out = list(na[nb])
    return out

def mapIndexValues(a, b):
    out = map(a.__getitem__, b)
    return list(out)

def getIndexValues(a, b):
    out = operator.itemgetter(*b)(a)
    return out

def pythonLoopOverlap(a, b):
    c = [ a[i] for i in b]
    return c

multipleListItemValues = lambda searchList, ind: [searchList[i] for i in ind]

다음 입력을 사용합니다.

a = range(0, 10000000)
b = range(500, 500000)

단순 python 루프는 lambda 연산에서 가장 빨랐고 mapIndexValues와 getIndexValues는 목록을 numpy 배열로 변환한 후 numpy 메서드에서 상당히 느렸습니다.데이터가 이미 numpy 배열에 있는 경우 numpy는numpy.array 변환을 제거한 IndexValues 메서드가 가장 빠릅니다.

numpyIndexValues -> time:1.38940598 (when converted the lists to numpy arrays)
numpyIndexValues -> time:0.0193445 (using numpy array instead of python list as input, and conversion code removed)
mapIndexValues -> time:0.06477512099999999
getIndexValues -> time:0.06391049500000001
multipleListItemValues -> time:0.043773591
pythonLoopOverlap -> time:0.043021754999999995

보다 심플한 방법은 다음과 같습니다.

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [e for i, e in enumerate(a) if i in b]

이것은 이미 고려되었을 것입니다.b의 지수 양이 작고 일정하다면 다음과 같이 결과를 쓸 수 있다.

c = [a[b[0]]] + [a[b[1]]] + [a[b[2]]]

또는 지수 자체가 상수인 경우에는...

c = [a[1]] + [a[2]] + [a[5]]

또는 연속된 지수 범위가 있는 경우...

c = a[1:3] + [a[5]]

목록 이해는 분명히 가장 즉각적이고 기억하기 쉬운 것이다 - 게다가 꽤 버마적인 것이다!

어쨌든 제안된 솔루션 중 가장 빠르지 않습니다(Python 3.8.3을 사용하여 Windows에서 테스트를 실행했습니다).

import timeit
from itertools import compress
import random
from operator import itemgetter
import pandas as pd

__N_TESTS__ = 10_000

vector = [str(x) for x in range(100)]
filter_indeces = sorted(random.sample(range(100), 10))
filter_boolean = random.choices([True, False], k=100)

# Different ways for selecting elements given indeces

# list comprehension
def f1(v, f):
   return [v[i] for i in filter_indeces]

# itemgetter
def f2(v, f):
   return itemgetter(*f)(v)

# using pandas.Series
# this is immensely slow
def f3(v, f):
   return list(pd.Series(v)[f])

# using map and __getitem__
def f4(v, f):
   return list(map(v.__getitem__, f))

# using enumerate!
def f5(v, f):
   return [x for i, x in enumerate(v) if i in f]

# using numpy array
def f6(v, f):
   return list(np.array(v)[f])

print("{:30s}:{:f} secs".format("List comprehension", timeit.timeit(lambda:f1(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Operator.itemgetter", timeit.timeit(lambda:f2(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Using Pandas series", timeit.timeit(lambda:f3(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Using map and __getitem__", timeit.timeit(lambda: f4(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Enumeration (Why anyway?)", timeit.timeit(lambda: f5(vector, filter_indeces), number=__N_TESTS__)))

결과는 다음과 같습니다.

목록 이해: 0.007113초
Operator.itemgetter : 0.003247초
팬더 시리즈 사용 : 2.977286초
맵과 get item 사용 : 0.005029초
열거(어차피 왜?): 0.135156초
수치: 0.157018초

제 답변은 numpy나 python 컬렉션은 사용하지 않습니다.

요소를 찾는 간단한 방법 중 하나는 다음과 같습니다.

a = [-2, 1, 5, 3, 8, 5, 6]
b = [1, 2, 5]
c = [i for i in a if i in b]

결점:이 방법은 더 큰 목록에서는 작동하지 않을 수 있습니다.더 큰 목록에는 numpy를 사용하는 것이 좋습니다.

정적 인덱스 및 소규모 목록?

리스트가 작고 인덱스가 변경되지 않는 경우, 경우에 따라 가장 좋은 방법은 시퀀스 언팩을 사용하는 것입니다.

_,a1,a2,_,_,a3,_ = a

퍼포먼스가 훨씬 향상되어 코드 한 줄도 저장할 수 있습니다.

 %timeit _,a1,b1,_,_,c1,_ = a
10000000 loops, best of 3: 154 ns per loop 
%timeit itemgetter(*b)(a)
1000000 loops, best of 3: 753 ns per loop
 %timeit [ a[i] for i in b]
1000000 loops, best of 3: 777 ns per loop
 %timeit map(a.__getitem__, b)
1000000 loops, best of 3: 1.42 µs per loop

일종의 비토닉 방식:

c = [x for x in a if a.index(x) in b]

<<고객명>>의 결과pandas==1.4.22022년 6월 6일

단순한 슬라이스는 더 이상 불가능하며 벤치마크 결과가 더 빨라집니다.

import timeit
import pandas as pd
print(pd.__version__)
# 1.4.2

pd.Series([-2, 1, 5, 3, 8, 5, 6])[1, 2, 5]
# KeyError: 'key of type tuple not found and not a MultiIndex'

pd.Series([-2, 1, 5, 3, 8, 5, 6]).iloc[[1, 2, 5]].tolist()
# [1, 5, 5]

def extract_multiple_elements():
    return pd.Series([-2, 1, 5, 3, 8, 5, 6]).iloc[[1, 2, 5]].tolist()

__N_TESTS__ = 10_000
t1 = timeit.timeit(extract_multiple_elements, number=__N_TESTS__)
print(round(t1, 3), 'seconds')
# 1.035 seconds

언급URL : https://stackoverflow.com/questions/18272160/access-multiple-elements-of-list-knowing-their-index

반응형