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


99

У меня есть несколько списков, содержащих одинаковое количество записей (каждый из которых указывает свойство объекта):

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]

Ответы:


128

Вы можете просто использовать понимание списка :

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 05

Хорошо, тогда я должен придерживаться вашего 2-го предложения, потому что оно составляет центральную часть моего кода.
fuenfundachtzig

4
@ 85: почему ты беспокоишься о производительности? Напишите, что вам нужно сделать, если это медленно, а затем проверьте, чтобы найти узкие места.
Гэри Керр

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 - хорошее предложение, поскольку OP, похоже, хочет хранить числа в списках. Двумерный массив был бы еще лучше.
Филипп

Это также хорошее предложение, потому что это будет очень знакомый синтаксис пользователям R, где этот вид выбора очень эффективен, особенно когда он вложен и / или многомерен.
Томас Браун

1
[property_b[i] for i in good_indices]хорош для использования безnumpy
Илья Русин

15

Используйте встроенную функцию 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 05

5
Хм, я считаю, что код с использованием compress намного более читабелен :) Возможно, я предвзято, потому что он делает именно то, что я хочу.
fuenfundachtzig 09

Почему бы вам не предоставить пример itertools.compressвместо того, чтобы скопировать и вставить пример документации?
Николя Жерве,

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) и возвращает только те индексы, в которых элемент является истинным.


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

перечисляет индекс для каждой группы индекса, элемент , который существует в качестве перечисления из хороших объектов , если (когда) элемент Правды


-1

Языки 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, но за это приходится платить: вам нужно объявить каждый список с ключевым словом «array», что приведет к перегрузке вашего скрипта (этой проблемы не существует с Matlab / Scilab). Обратите внимание, что это решение ограничено массивами чисел, как в вашем примере.


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