เข้าถึงหลายองค์ประกอบของรายการรู้ดัชนีของพวกเขา


232

ฉันจำเป็นต้องเลือกองค์ประกอบบางอย่างจากรายการที่กำหนดรู้ดัชนีของพวกเขา สมมติว่าฉันต้องการสร้างรายการใหม่ซึ่งมีองค์ประกอบที่มีดัชนี 1, 2, 5 จากรายการที่ระบุ [-2, 1, 5, 3, 8, 5, 6] สิ่งที่ฉันทำคือ:

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

มีวิธีที่ดีกว่าที่จะทำหรือไม่ อะไรอย่าง c = a [b]?


1
ฉันพบวิธีแก้ไขปัญหาอื่นที่นี่ ฉันยังไม่ได้ทดสอบมัน แต่ผมคิดว่าผมสามารถโพสต์ได้ที่นี่เมื่อคุณมีความสนใจในcode.activestate.com/recipes/...
Hoang tran

นั่นคือทางออกเดียวกับที่กล่าวถึงในคำถาม แต่ห่อหุ้มในlambdaฟังก์ชั่น
Will Dereham

คำตอบ:


218

คุณสามารถใช้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)

หรือคุณสามารถใช้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]

แต่จริงๆแล้วทางออกปัจจุบันของคุณดี มันอาจจะเป็นสิ่งที่ประณีตที่สุดของพวกเขาทั้งหมด


35
+1 สำหรับการกล่าวถึงที่c = [a[i] for i in b]ดีมาก โปรดทราบว่าการitemgetterแก้ปัญหาจะไม่ทำสิ่งเดียวกันถ้า b มีองค์ประกอบน้อยกว่า 2
flornquake

หมายเหตุด้านข้าง : การใช้itemgetterในขณะที่ทำงานในหลายกระบวนการไม่ทำงาน Numpy ใช้งานได้ดีในหลายกระบวนการ
Lior Magen

3
แสดงความคิดเห็นเพิ่มเติมa[b]ทำงานเฉพาะเมื่อaเป็นnumpyอาร์เรย์คือคุณสร้างมันด้วยฟังก์ชั่น numpy
ลุดวิกโจว

ฉันได้ทำการเปรียบเทียบตัวเลือกที่ไม่ใช่ numpy และ itemgetter ดูเหมือนจะเร็วที่สุดแม้จะเร็วกว่าเพียงแค่พิมพ์ดัชนีที่ต้องการในวงเล็บโดยใช้ Python 3.44
ragardner

@ citizen2077 คุณสามารถยกตัวอย่างของไวยากรณ์ที่คุณอธิบายได้หรือไม่?
alancalvitti

47

ทางเลือก:

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

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

อันแรกดีเพราะคุณใช้build-inฟังก์ชั่น
silgon

ปัญหาที่เกิดขึ้นกับปัญหาแรกนั้น__getitem__ดูเหมือนจะไม่สามารถใช้งานได้เช่นวิธีการแมปประเภทของรายการ map(type(a.__getitem__), b)
alancalvitti

lambda x: type(a.__getitem__(x)), b@alancalvitti, ในกรณีนี้การใช้งาน[..]มีขนาดกะทัดรัดมากขึ้น:lambda x: type(a[x]), b
falsetru

9

วิธีแก้ปัญหาอื่นอาจทำได้ผ่านทางซีรีย์แพนด้า:

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)

7

การทดสอบขั้นพื้นฐานและไม่ครอบคลุมมากเปรียบเทียบเวลาดำเนินการของคำตอบทั้งห้าที่ให้มา:

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)

simple python loop เป็นวิธีที่เร็วที่สุดในการดำเนินการแลมบ์ดาในไม่ช้าวินาที MapIndexValues ​​และ getIndexValues ​​ก็ค่อนข้างคล้ายกันกับวิธีการของ numpy ช้าลงอย่างมากหลังจากแปลงรายการเป็น numpy arrays วิธีการที่ numpyIndexValues ที่เร็วที่สุด

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

ผมไม่ทราบว่าสิ่งที่หลามล่ามคุณใช้ แต่วิธีแรกnumpyIndexValuesไม่ทำงานตั้งแต่a, เป็นประเภทb rangeฉันเดาว่าคุณ ment การแปลงa, bการnumpy.ndarraysครั้งแรก?
นักเลง

@strpeter ใช่ฉันไม่ได้เปรียบเทียบแอปเปิ้ลกับแอปเปิ้ลฉันได้สร้างอาร์เรย์ numpy เป็นอินพุตในกรณีทดสอบสำหรับ numpyIndexValues ฉันได้แก้ไขสิ่งนี้แล้วและทุกคนใช้รายการเดียวกับอินพุต
Don Smythe

4

ฉันแน่ใจว่าสิ่งนี้ได้รับการพิจารณาแล้ว: หากปริมาณของดัชนีใน b มีขนาดเล็กและคงที่เราสามารถเขียนผลลัพธ์ดังนี้:

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

หรือง่ายยิ่งขึ้นถ้าดัชนีของตัวเองเป็นค่าคงที่ ...

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

หรือหากมีช่วงดัชนีที่ต่อเนื่องกัน ...

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

ขอบคุณสำหรับการเตือนฉันว่า[a] + [b] = [a, b]
onewhaleid


1

คำตอบของฉันไม่ได้ใช้คอลเลกชัน numpy หรือหลาม

วิธีหนึ่งเล็กน้อยในการค้นหาองค์ประกอบจะเป็นดังนี้:

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

ข้อเสียเปรียบ: วิธีนี้อาจใช้ไม่ได้กับรายการขนาดใหญ่ แนะนำให้ใช้ numpy สำหรับรายการขนาดใหญ่


5
aไม่จำเป็นต้องย้ำ [a[i] for i in b]
falsetru

1
วิธีนี้ไม่สามารถใช้งานได้ในกรณีอื่น ๆ ถ้าaมีอีก 5 อยู่ล่ะ?
TerryA

IMO เร็วกว่าที่จะทำสี่แยกนี้โดยใช้เซต
sirgogo

หากคุณกังวลเกี่ยวกับ IndexErrors ถ้า b มีตัวเลขที่เกินขนาดของลอง[a[i] if i<len(a) else None for i in b]
576i

0

ดัชนีคงที่และรายการเล็ก ๆ ?

อย่าลืมว่าถ้ารายการมีขนาดเล็กและดัชนีไม่เปลี่ยนแปลงเช่นในบางครั้งสิ่งที่ดีที่สุดคือการใช้ลำดับการเปิดออก :

_,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

0

ชนิดของ pythonic way:

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

2
ฉันจะบอกว่านี่เป็น "pythonic" น้อยกว่าแม้แต่ตัวอย่างของ OP - คุณสามารถเปลี่ยนO(n)โซลูชันเป็นO(n^2)โซลูชันในขณะที่ยังเพิ่มความยาวของรหัสเป็นสองเท่า นอกจากนี้คุณยังจะต้องการที่จะทราบวิธีการที่จะล้มเหลวถ้ารายการประกอบด้วยวัตถุจะเลือนหรือบางส่วนความเท่าเทียมกันเช่นถ้าaมีfloat('nan')นี้จะเสมอValueErrorยก
Brian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.