Python: виберіть підмножину зі списку на основі набору індексів


98

У мене є кілька списків, що мають однакову кількість записів (кожен із зазначенням властивості об’єкта):

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, property_bsel..., які містять лише значення, зазначені або Trueзаписами, або індексами?

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

Відповіді:


126

Ви можете просто використати розуміння списку :

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

або

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

Останнє швидше, оскільки їх менше, good_indicesніж довжина property_a, припускаючи, що good_indicesвони попередньо обчислюються, а не генеруються на льоту.


Редагувати : перший варіант еквівалентний itertools.compressдоступному з Python 2.7 / 3.1. Дивіться відповідь @Gary Kerr .

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

1
@fuen: Так. Викликає багато на Python 2 ( замість цього використовуйте itertools.izip ), не стільки на Python 3. Це тому, що zipв Python 2 створить новий список, але на Python 3 він просто поверне (ледачий) генератор.
kennytm

Добре, тому я повинен дотримуватися вашої другої пропозиції, тому що це становить центральну частину мого коду.
fuenfundachtzig

4
@ 85: чому ви турбуєтесь про продуктивність? Напишіть, що вам потрібно зробити, якщо це повільно, то протестуйте, щоб знайти вузькі місця.
Gary Kerr

1
@PreludeAndFugue: Якщо є два еквівалентні варіанти, добре знати, який з них швидший, і скористатися цим одразу.
fuenfundachtzig

1
Ви можете просто використовувати from itertools import izipі використовувати це, а не zipв першому прикладі. Це створює ітератор, такий самий, як Python 3.
Кріс Б.

28

Я бачу 2 варіанти.

  1. Використання 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. Використовуючи розуміння списку та заархівуйте його:

    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
Використання Numpy - хороша порада, оскільки ОП, схоже, хоче зберігати номери у списках. Двовимірний масив був би ще кращим.
Філіпп

Це також гарна порада, оскільки це буде дуже знайомий синтаксис для користувачів R, де такий вид виділення є дуже потужним, особливо коли вкладений та / або багатовимірний.
Томас Браун,

[property_b[i] for i in good_indices]хороший для використання безnumpy
Ілля Русін

16

Використовуйте вбудовану функцію zip

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

РЕДАГУВАТИ

Просто дивлячись на нові функції 2.7. Зараз у модулі itertools є функція, яка схожа на наведений вище код.

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

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

1
Я вражений використанням itertools.compressтут. Розуміння списку стає набагато читабельнішим, без необхідності розкопувати те, що робить компромісний компрес.
PaulMcG

5
Хм, я вважаю, що код за допомогою компресу є набагато читабельнішим :) Можливо, я упереджений, тому що робить саме те, що я хочу.
fuenfundachtzig

8

Якщо припустити, що у вас є лише список елементів і список справжніх / необхідних індексів, це повинно бути найшвидшим:

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

Це означає, що при виборі властивостей буде здійснено стільки раундів, скільки є справжніх / необхідних індексів. Якщо у вас багато списків властивостей, які відповідають правилам одного списку тегів (true / false), ви можете створити список індексів, використовуючи ті самі принципи розуміння списку:

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

Це перебирає кожен елемент у good_objects (при запам'ятовуванні його індексу з enumerate) і повертає лише ті індекси, де елемент справжній.


Для тих, хто не зрозумів списку, ось англійська прозова версія з кодом, виділеним жирним шрифтом:

перераховує індекс для кожної групи індексу, елемент , який існує в якості перерахування з хороших об'єктів , якщо (коли) елемент Правди


0

Мови Matlab та Scilab пропонують простіший та вишуканіший синтаксис, ніж Python, для вашого запитання, тому я думаю, що найкраще, що ви можете зробити, це імітувати Matlab / Scilab за допомогою пакету Numpy у Python. Таким чином, вирішення вашої проблеми є дуже стислим та елегантним:

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 намагається імітувати Matlab / Scilab, але це має певну ціну: вам потрібно оголосити кожен список ключовим словом "масив", що призведе до перевантаження вашого сценарію (цієї проблеми у Matlab / Scilab не існує). Зверніть увагу, що це рішення обмежене масивами чисел, як це має місце у вашому прикладі.


3
Ніде в питанні він не згадує NumPy - немає потреби висловлювати свою думку щодо NumPy проти Matlab. Списки Python - це не те саме, що масиви NumPy, навіть якщо вони обидва приблизно відповідають векторам. (Списки Python схожі на масиви комірок Matlab - кожен елемент може мати різний тип даних. Масиви NumPy є більш обмеженими, щоб забезпечити певні оптимізації). Ви можете отримати синтаксис, подібний до вашого прикладу, за допомогою вбудованого Python filterабо зовнішньої бібліотеки pandas. Якщо ви збираєтеся поміняти місцями мови, ви також можете спробувати R, але це не те, що задає питання .
Лівій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.