Python: Chọn tập hợp con từ danh sách dựa trên tập hợp chỉ mục


98

Tôi có một số danh sách có cùng số lượng mục nhập (mỗi mục chỉ định một thuộc tính đối tượng):

property_a = [545., 656., 5.4, 33.]
property_b = [ 1.2,  1.3, 2.3, 0.3]
...

và liệt kê với các cờ có cùng độ dài

good_objects = [True, False, False, True]

(có thể dễ dàng thay thế bằng danh sách chỉ mục tương đương:

good_indices = [0, 3]

Cách dễ nhất để tạo danh sách mới là gì property_asel, property_bsel... mà chỉ chứa các giá trị chỉ định hoặc bằng các Truemục hoặc các chỉ số?

property_asel = [545., 33.]
property_bsel = [ 1.2, 0.3]

Câu trả lời:


126

Bạn chỉ có thể sử dụng khả năng hiểu danh sách :

property_asel = [val for is_good, val in zip(good_objects, property_a) if is_good]

hoặc là

property_asel = [property_a[i] for i in good_indices]

Cái thứ hai nhanh hơn vì có ít good_indiceshơn độ dài của property_a, giả sử good_indicesđược tính toán trước thay vì được tạo ngay lập tức.


Chỉnh sửa : Tùy chọn đầu tiên tương đương với itertools.compresscó sẵn kể từ Python 2.7 / 3.1. Xem câu trả lời của @Gary Kerr .

property_asel = list(itertools.compress(property_a, good_objects))

1
@fuen: Vâng. Gây ra rất nhiều trên Python 2 (sử dụng itertools.izip thay thế), không quá nhiều trên Python 3. Điều này là do ziptrong Python 2 sẽ tạo một danh sách mới, nhưng trên Python 3, nó sẽ chỉ trả về một trình tạo (lười biếng).
kennytm

Được rồi, vì vậy tôi nên tuân theo đề xuất thứ hai của bạn, vì điều này tạo nên phần trung tâm trong mã của tôi.
fuenfundachtzig

4
@ 85: tại sao bạn lại lo lắng về hiệu suất? Viết những việc phải làm, nếu chậm thì chạy thử để tìm điểm nghẽn.
Gary Kerr

1
@PreludeAndFugue: Nếu có hai tùy chọn tương đương, bạn nên biết cái nào nhanh hơn và sử dụng cái đó ngay lập tức.
fuenfundachtzig

1
Bạn chỉ có thể sử dụng from itertools import izipvà sử dụng nó thay vì ziptrong ví dụ đầu tiên. Điều đó tạo ra một trình lặp, giống như Python 3.
Chris B.

28

Tôi thấy có 2 lựa chọn.

  1. Sử dụng numpy:

    property_a = numpy.array([545., 656., 5.4, 33.])
    property_b = numpy.array([ 1.2,  1.3, 2.3, 0.3])
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = property_a[good_objects]
    property_bsel = property_b[good_indices]
  2. Sử dụng khả năng hiểu danh sách và nén nó:

    property_a = [545., 656., 5.4, 33.]
    property_b = [ 1.2,  1.3, 2.3, 0.3]
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = [x for x, y in zip(property_a, good_objects) if y]
    property_bsel = [property_b[i] for i in good_indices]

2
Sử dụng Numpy là một gợi ý hay vì OP dường như muốn lưu trữ các số trong danh sách. Một mảng hai chiều thậm chí sẽ tốt hơn.
Philipp

Đó cũng là một gợi ý hay vì đây sẽ là cú pháp rất quen thuộc với người dùng R, nơi kiểu lựa chọn này rất mạnh mẽ, đặc biệt là khi lồng nhau và / hoặc đa chiều.
Thomas Browne

[property_b[i] for i in good_indices]là một trong những tốt cho việc sử dụng mà không cầnnumpy
Ilya Rusin

16

Sử dụng zip chức năng tích hợp

property_asel = [a for (a, truth) in zip(property_a, good_objects) if truth]

BIÊN TẬP

Chỉ cần nhìn vào các tính năng mới của 2.7. Bây giờ có một chức năng trong mô-đun itertools tương tự như đoạn mã trên.

http://docs.python.org/library/itertools.html#itertools.compress

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F

1
Tôi bị choáng ngợp bởi việc sử dụng itertools.compressở đây. Danh sách sự hiểu biết là xa hơn có thể đọc được, mà không cần phải đào lên những gì mà nén quái gì đang làm.
PaulMcG

5
Hm, tôi thấy mã sử dụng nén dễ đọc hơn nhiều :) Có lẽ tôi thiên vị, bởi vì nó làm chính xác những gì tôi muốn.
fuenfundachtzig

8

Giả sử bạn chỉ có danh sách các mục và danh sách các chỉ số đúng / bắt buộc, thì đây sẽ là cách nhanh nhất:

property_asel = [ property_a[index] for index in good_indices ]

Điều này có nghĩa là lựa chọn thuộc tính sẽ chỉ thực hiện nhiều vòng khi có chỉ số đúng / bắt buộc. Nếu bạn có nhiều danh sách thuộc tính tuân theo các quy tắc của một danh sách thẻ đơn (true / false), bạn có thể tạo danh sách chỉ mục bằng cách sử dụng cùng các nguyên tắc hiểu danh sách:

good_indices = [ index for index, item in enumerate(good_objects) if item ]

Điều này lặp lại từng mục trong good_objects (đồng thời ghi nhớ chỉ mục của nó với liệt kê) và chỉ trả về các chỉ số mà mục đó là true.


Đối với bất kỳ ai không hiểu được danh sách, đây là một phiên bản văn xuôi tiếng Anh với mã được tô đậm:

liệt kê các chỉ số cho mỗi nhóm chỉ số, item đang tồn tại trong một liệt kê các đối tượng tốt , nếu (ở đâu) các mục là True


0

Ngôn ngữ Matlab và Scilab cung cấp một cú pháp đơn giản và thanh lịch hơn Python cho câu hỏi bạn đang hỏi, vì vậy tôi nghĩ tốt nhất bạn có thể làm là bắt chước Matlab / Scilab bằng cách sử dụng gói Numpy trong Python. Bằng cách làm này, giải pháp cho vấn đề của bạn rất ngắn gọn và thanh lịch:

from numpy import *
property_a = array([545., 656., 5.4, 33.])
property_b = array([ 1.2,  1.3, 2.3, 0.3])
good_objects = [True, False, False, True]
good_indices = [0, 3]
property_asel = property_a[good_objects]
property_bsel = property_b[good_indices]

Numpy cố gắng bắt chước Matlab / Scilab nhưng nó phải trả giá đắt: bạn cần phải khai báo mọi danh sách với từ khóa "array", điều gì đó sẽ làm quá tải script của bạn (vấn đề này không tồn tại với Matlab / Scilab). Lưu ý rằng giải pháp này được giới hạn cho các mảng số, đó là trường hợp trong ví dụ của bạn.


3
Không nơi nào trong câu hỏi mà anh ấy đề cập đến NumPy - không cần phải bày tỏ ý kiến ​​của bạn về NumPy vs Matlab. Danh sách Python không giống với mảng NumPy, ngay cả khi cả hai đều gần tương ứng với vectơ. (Danh sách Python giống như mảng ô Matlab - mỗi phần tử có thể có một kiểu dữ liệu khác nhau. Mảng NumPy bị hạn chế hơn để cho phép tối ưu hóa nhất định). Bạn có thể lấy cú pháp tương tự với ví dụ của mình thông qua filterthư viện tích hợp sẵn của Python hoặc thư viện bên ngoài pandas. Nếu bạn định hoán đổi ngôn ngữ, bạn cũng có thể thử R, nhưng đó không phải là những gì câu hỏi đang đặt ra .
Livius
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.