Truy cập nhiều yếu tố của danh sách biết chỉ mục của họ


232

Tôi cần chọn một số yếu tố từ danh sách nhất định, biết chỉ số của chúng. Giả sử tôi muốn tạo một danh sách mới, chứa phần tử có chỉ số 1, 2, 5, từ danh sách đã cho [-2, 1, 5, 3, 8, 5, 6]. Những gì tôi đã làm là:

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

Có cách nào tốt hơn để làm điều đó? một cái gì đó như c = a [b]?


1
Nhân tiện, tôi tìm thấy một giải pháp khác ở đây. Tôi chưa kiểm tra nó, nhưng tôi nghĩ rằng tôi có thể đăng nó ở đây một khi bạn quan tâm đến code.activestate.com/recipes/iêu
hoang tran

Đó là giải pháp tương tự như được đề cập trong câu hỏi, nhưng được bao bọc trong một lambdachức năng.
Will Dereham

Câu trả lời:


218

Bạn có thể sử dụng operator.itemgetter:

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

Hoặc bạn có thể sử dụng 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]

Nhưng thực sự, giải pháp hiện tại của bạn là tốt. Nó có lẽ là gọn gàng nhất trong số họ.


35
+1 để đề cập đến điều đó c = [a[i] for i in b]là hoàn toàn tốt. Lưu ý rằng itemgettergiải pháp sẽ không làm điều tương tự nếu b có ít hơn 2 phần tử.
flornquake

Lưu ý bên lề : Sử dụng itemgetter trong khi làm việc trong nhiều quy trình không hoạt động. Numpy hoạt động tuyệt vời trong nhiều quá trình.
Lior Magen

3
Nhận xét bổ sung, chỉa[b] hoạt động khi là một mảng numpy , tức là bạn tạo nó với một hàm numpy. a
Ludwig Zhou

Tôi đã điểm chuẩn các tùy chọn không khó chịu và itemgetter dường như là nhanh nhất, thậm chí nhanh hơn một chút so với việc chỉ cần gõ các chỉ mục mong muốn bên trong dấu ngoặc đơn, sử dụng Python 3.44
ragardner

@ civen2077, bạn có thể cho một ví dụ về cú pháp bạn mô tả không?
alancalvitti

47

Lựa chọn thay thế:

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

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

cái đầu tiên là tốt bởi vì bạn sử dụng các build-inchức năng
silgon

Vấn đề với cái đầu tiên là __getitem__dường như không thể so sánh được, ví dụ như làm thế nào để ánh xạ loại vật phẩm? map(type(a.__getitem__), b)
alancalvitti

@alancalvitti lambda x: type(a.__getitem__(x)), b,. Trong trường hợp này, việc sử dụng [..]sẽ gọn hơn:lambda x: type(a[x]), b
falsetru

9

Một giải pháp khác có thể thông qua Sê-ri gấu trúc:

import pandas as pd

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

Sau đó, bạn có thể chuyển đổi c trở lại danh sách nếu bạn muốn:

c = list(c)

7

Thử nghiệm cơ bản và không rộng rãi so sánh thời gian thực hiện của năm câu trả lời được cung cấp:

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]

sử dụng đầu vào sau:

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

Vòng lặp python đơn giản là nhanh nhất với lambda hoạt động gần một giây, mapIndexValues ​​và getIndexValues ​​luôn khá giống nhau với phương thức numpy chậm hơn đáng kể sau khi chuyển đổi danh sách sang mảng numpy. Nếu dữ liệu đã có trong numpy Index thì phương thức numpy IndexValues ​​bị loại bỏ. nhanh nhất

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

Tôi không biết những gì thông dịch Python bạn sử dụng nhưng phương pháp đầu tiên numpyIndexValueskhông làm việc kể từ a, blà loại range. Tôi đoán rằng bạn có ý định chuyển đổi a, bđể numpy.ndarraysđầu tiên?
strpeter

@strpeter Có Tôi không so sánh táo với táo, tôi đã tạo ra các mảng numpy làm đầu vào trong trường hợp thử nghiệm cho numpyIndexValues. Tôi đã sửa lỗi này ngay bây giờ và tất cả đều sử dụng cùng một danh sách làm đầu vào.
Don Smythe

4

Tôi chắc chắn rằng điều này đã được xem xét: Nếu số lượng chỉ số trong b nhỏ và không đổi, người ta có thể viết kết quả như sau:

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

Hoặc thậm chí đơn giản hơn nếu chính các chỉ số là hằng số ...

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

Hoặc nếu có một loạt các chỉ số liên tiếp ...

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

Cảm ơn bạn đã nhắc nhở tôi rằng[a] + [b] = [a, b]
onewhaleid

3

Đây là một cách đơn giản hơn:

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

1

Câu trả lời của tôi không sử dụng bộ sưu tập numpy hoặc python.

Một cách tầm thường để tìm các yếu tố sẽ như sau:

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

Nhược điểm: Phương pháp này có thể không hoạt động cho danh sách lớn hơn. Sử dụng numpy được khuyến khích cho danh sách lớn hơn.


5
Không cần phải lặp đi lặp lại a. [a[i] for i in b]
falsetru

1
Phương pháp này thậm chí không hoạt động trong mọi trường hợp khác. Điều gì nếu acó thêm 5 trong đó?
TerryA 21/07/2015

IMO, nhanh hơn để thực hiện loại giao lộ này bằng cách sử dụng các bộ
sirgogo

Nếu bạn lo lắng về IndexErrors nếu b có số lượng vượt quá kích thước của một, hãy thử[a[i] if i<len(a) else None for i in b]
576i

0

Chỉ số tĩnh và danh sách nhỏ?

Đừng quên rằng nếu danh sách nhỏ và các chỉ mục không thay đổi, như trong ví dụ của bạn, đôi khi điều tốt nhất là sử dụng giải nén chuỗi :

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

Hiệu suất tốt hơn nhiều và bạn cũng có thể lưu một dòng mã:

 %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

0

Loại cách pythonic:

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

2
Tôi sẽ nói rằng điều này ít "pythonic" hơn cả ví dụ của OP - bạn đã xoay sở để biến O(n)giải pháp của họ thành một O(n^2)giải pháp trong khi cũng tăng gần gấp đôi độ dài của mã. Bạn cũng sẽ muốn lưu ý rằng cách tiếp cận sẽ thất bại nếu danh sách chứa các đối tượng sẽ mờ hoặc bằng một phần, ví dụ nếu achứa float('nan'), điều này sẽ luôn tăng a ValueError.
Brian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.