Dizinlerini bilerek listenin birden çok öğesine erişin


233

Dizinlerini bilerek verilen listeden bazı öğeler seçmem gerekiyor. Diyelim ki verilen listeden 1, 2, 5 indeksli eleman içeren yeni bir liste oluşturmak istiyorum [-2, 1, 5, 3, 8, 5, 6]. Yaptığım şey:

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

Bunu yapmanın daha iyi bir yolu var mı? c = a [b] gibi bir şey mi?


1
Bu arada, burada başka bir çözüm buldum. Henüz test etmedim, ancak sanırım code.activestate.com/recipes/…
hoang tran

Bu, soruda belirtilenle aynı çözümdür, ancak bir lambdaişleve sarılır .
Dereham

Yanıtlar:


220

Şunları kullanabilirsiniz 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)

Veya numpy kullanabilirsiniz :

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]

Ama gerçekten, mevcut çözümünüz iyi. Muhtemelen hepsinden en güzeli.


36
+1 bahsettiğim c = [a[i] for i in b]için gayet iyi. B'nin itemgetter2'den az elemanı varsa , çözümün aynı şeyi yapmayacağını unutmayın .
flornquake

Yan Not : Çoklu işlemde itemgetter kullanmak işe yaramıyor. Numpy çok işlemde harika çalışıyor.
Lior Magen

3
Ek yorum, yalnızca bir numpy dizisi olduğunda a[b]çalışır , yani bir numpy işleviyle oluşturursunuz. a
Ludwig Zhou

Numpy olmayan seçenekleri karşılaştırdım ve itemgetter, Python 3.44
ragardner

@ citizen2077, tarif ettiğiniz sözdizimine bir örnek verebilir misiniz?
alancalvitti

47

Alternatifler:

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

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

ilki güzel çünkü build-infonksiyonları kullanıyorsunuz
silgon

İlk sorun w / karşılaştırılabilir __getitem__gibi görünmüyor örneğin öğenin türü nasıl eşlenir? map(type(a.__getitem__), b)
alancalvitti

@alancalvitti lambda x: type(a.__getitem__(x)), b,. Bu durumda kullanmak [..]daha kompakt:lambda x: type(a[x]), b
falsetru

9

Başka bir çözüm panda Serisi aracılığıyla olabilir:

import pandas as pd

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

Daha sonra isterseniz c'yi tekrar listeye dönüştürebilirsiniz:

c = list(c)

7

Verilen beş cevabın yürütme süresini karşılaştıran temel ve çok kapsamlı olmayan testler:

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şağıdaki girişi kullanarak:

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

Basit bir python döngüsü, lambda işlemiyle en yakın saniye oldu, mapIndexValues ​​ve getIndexValues, numpy.ndrayValues ​​yönteminin numpy.array dönüşümü ile listelerin dönüştürülmesinden sonra sürekli olarak oldukça benzerdi. En hızlı.

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

Kullanmak tercüman ne Python bilmiyorum ama ilk yöntem numpyIndexValuesberi çalışmalarını değil a, btiptedir range. Sana dönüştürmek ment olduğunu tahmin ediyorum a, bhiç numpy.ndarraysilk?
strpeter

@strpeter Evet Elmaları elmalarla karşılaştırmıyordum, numpyIndexValues ​​için test durumunda girdi olarak numpy dizileri oluşturmuştum. Ben şimdi bu düzeltti ve tüm giriş aynı listeleri kullanın.
Don Smythe

4

Eminim bu zaten dikkate alınmıştır: b indeks miktarı küçük ve sabit ise, sadece sadece sonuç şöyle yazabilirsiniz:

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

Veya endekslerin kendisi sabitse daha da basittir ...

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

Veya ardışık bir dizi indeks varsa ...

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

Bunu hatırlattığınız için teşekkür ederim[a] + [b] = [a, b]
onewhaleid

3

İşte daha basit bir yol:

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

Cevabım numpy veya python koleksiyonları kullanmıyor.

Öğeleri bulmanın önemsiz bir yolu aşağıdaki gibi olacaktır:

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

Dezavantaj: Bu yöntem daha büyük listeler için çalışmayabilir. Daha büyük listeler için numpy kullanılması önerilir.


5
Yinelemeye gerek yok a. [a[i] for i in b]
falsetru

1
Bu yöntem başka hiçbir durumda çalışmaz. Ya a5 tane daha olsaydı?
TerryA


B'nin boyutunu aşan sayıları varsa IndexErrors konusunda endişeleriniz varsa[a[i] if i<len(a) else None for i in b]
576i

0

Statik dizinler ve küçük liste?

Liste küçükse ve dizinler değişmezse, örneğin örneğinizde olduğu gibi, bazen en iyi şeyin sekans açma işlemini kullanmak olduğunu unutmayın :

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

Performans çok daha iyidir ve ayrıca bir satır kod kaydedebilirsiniz:

 %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

Bir tür pitonik yol:

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

2
Bunun OP örneğinden bile daha az "pythonic" olduğunu söyleyebilirim - kodun uzunluğunu neredeyse iki katına çıkarırken O(n)çözümlerini bir O(n^2)çözüme dönüştürmeyi başardınız . Ayrıca liste nesneler bulanık veya kısmi eşitlik içerecekse yaklaşımın başarısız olacağını da belirtmek isteyeceksiniz, örneğin aiçeriyorsa float('nan'), bu her zaman bir a yükselecektir ValueError.
Brian
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.