Akses beberapa elemen daftar mengetahui indeks mereka

232

Saya perlu memilih beberapa elemen dari daftar yang diberikan, mengetahui indeksnya. Katakanlah saya ingin membuat daftar baru, yang berisi elemen dengan indeks 1, 2, 5, dari daftar yang diberikan [-2, 1, 5, 3, 8, 5, 6]. Apa yang saya lakukan adalah:

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

Apakah ada cara yang lebih baik untuk melakukannya? sesuatu seperti c = a [b]?

hoang tran
sumber
1
Omong-omong, saya menemukan solusi lain di sini. Saya belum mengujinya, tapi saya pikir saya bisa mempostingnya di sini setelah Anda tertarik dengan code.activestate.com/recipes/…
hoang tran
Itu adalah solusi yang sama seperti yang disebutkan dalam pertanyaan, tetapi dibungkus dalam suatu lambdafungsi.
Will Dereham

Jawaban:

218

Anda bisa menggunakan 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)

Atau Anda bisa menggunakan 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]

Tapi sungguh, solusi Anda saat ini baik-baik saja. Mungkin yang paling rapi dari semuanya.

TerryA
sumber
35
+1 untuk menyebutkan itu c = [a[i] for i in b]tidak masalah. Perhatikan bahwa itemgettersolusi tidak akan melakukan hal yang sama jika b memiliki kurang dari 2 elemen.
flornquake
Catatan Sisi : Menggunakan itemgetter saat bekerja dalam multi-proses tidak berfungsi. Numpy bekerja sangat baik dalam multi-proses.
Lior Magen
3
Komentar tambahan, a[b]bekerja hanya ketika aadalah numpy array, yaitu Anda menciptakannya dengan fungsi numpy.
Ludwig Zhou
Saya telah membandingkan opsi non numpy dan itemgetter tampaknya menjadi yang tercepat, bahkan sedikit lebih cepat daripada hanya mengetik indeks yang diinginkan di dalam tanda kurung, menggunakan Python 3.44
ragardner
@ citizen2077, dapatkah Anda memberikan contoh sintaks yang Anda jelaskan?
alancalvitti
47

Alternatif:

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

>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)
falsetru
sumber
yang pertama bagus karena Anda menggunakan build-infungsi
silgon
Masalahnya w / yang pertama adalah yang __getitem__tampaknya tidak komparabel misalnya bagaimana memetakan jenis item? map(type(a.__getitem__), b)
alancalvitti
@alancalvitti lambda x: type(a.__getitem__(x)), b,. Dalam hal ini menggunakan [..]lebih kompak:lambda x: type(a[x]), b
falsetru
9

Solusi lain bisa melalui Seri panda:

import pandas as pd

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

Anda kemudian dapat mengonversi c kembali ke daftar jika Anda ingin:

c = list(c)
BossaNova
sumber
7

Pengujian dasar dan tidak sangat ekstensif membandingkan waktu pelaksanaan dari lima jawaban yang disediakan:

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]

menggunakan input berikut:

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

python loop sederhana adalah yang tercepat dengan operasi lambda sedetik dekat, mapIndexValues ​​dan getIndexValues ​​secara konsisten sangat mirip dengan metode numpy secara signifikan lebih lambat setelah mengkonversi daftar ke array numpy. Jika data sudah dalam array numpy, metode numpyIndexValues ​​dengan numpy. tercepat.

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
Don Smythe
sumber
Saya tidak tahu apa interpreter Python yang Anda gunakan tetapi metode pertama numpyIndexValuestidak bekerja karena a, badalah tipe range. Saya menduga bahwa Anda ment mengkonversi a, buntuk numpy.ndarrayspertama?
strpeter
@ strpeter Ya saya tidak membandingkan apel dengan apel, saya telah membuat array numpy sebagai input dalam kasus uji untuk numpyIndexValues. Saya telah memperbaikinya sekarang dan semua menggunakan daftar yang sama dengan input.
Don Smythe
4

Saya yakin ini sudah dipertimbangkan: Jika jumlah indeks dalam b kecil dan konstan, kita bisa menuliskan hasilnya seperti:

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

Atau bahkan lebih sederhana jika indeks itu sendiri adalah konstanta ...

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

Atau jika ada kisaran indeks berurutan ...

c = a[1:3] + [a[5]]
ecp
sumber
Terima kasih telah mengingatkan saya bahwa[a] + [b] = [a, b]
onewhaleid
3

Inilah cara yang lebih sederhana:

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

Jawaban saya tidak menggunakan koleksi numpy atau python.

Satu cara sepele untuk menemukan elemen adalah sebagai berikut:

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

Kelemahan: Metode ini mungkin tidak berfungsi untuk daftar yang lebih besar. Disarankan menggunakan numpy untuk daftar yang lebih besar.

Lavya 'Orion'
sumber
5
Tidak perlu diulang a. [a[i] for i in b]
falsetru
1
Metode ini bahkan tidak berfungsi dalam hal lain. Bagaimana jika aada 5 lainnya di dalamnya?
TerryA
IMO, lebih cepat untuk melakukan persimpangan semacam ini menggunakan set
sirgogo
Jika Anda khawatir tentang IndexErrors jika b memiliki angka yang melebihi ukuran a, coba[a[i] if i<len(a) else None for i in b]
576i
0

Indeks statis dan daftar kecil?

Jangan lupa bahwa jika daftar kecil dan indeks tidak berubah, seperti dalam contoh Anda, kadang-kadang hal terbaik adalah menggunakan urutan pembongkaran :

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

Performanya jauh lebih baik dan Anda juga dapat menyimpan satu baris kode:

 %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
GM
sumber
0

Jenis cara pythonic:

c = [x for x in a if a.index(x) in b]
Dmitry K. Chebanov
sumber
2
Saya akan mengatakan ini kurang "pythonic" daripada contoh OP - Anda telah berhasil mengubah O(n)solusi mereka menjadi O(n^2)solusi sementara juga hampir dua kali lipat panjang kode. Anda juga ingin mencatat bahwa pendekatan akan gagal jika daftar berisi objek akan kabur atau kesetaraan parsial, misalnya jika aberisi float('nan'), ini akan selalu meningkatkan a ValueError.
Brian