2016-09-01 2 views
4

Что такое лучший способ классифицировать список в python?Список категорий в Python

, например:

totalist is below 

totalist[1] = ['A','B','C','D','E'] 
totalist[2] = ['A','B','X','Y','Z'] 
totalist[3] = ['A','F','T','U','V'] 
totalist[4] = ['A','F','M','N','O'] 

Скажем, я хочу, чтобы получить список, где первые два пункта ['A','B'], в основном list[1] и list[2]. Есть ли простой способ получить их без повторения одного элемента за раз? Что-то вроде этого?

if ['A','B'] in totalist 

Я знаю, что не работает.

+3

Что это код, который вы пытались? –

+1

Невозможно решить эту проблему, не повторяя ее в какой-то форме, как предлагают ответы. Вы должны переделать, как вы представляете данные, если проверка этого эффективно является большим приоритетом; жертвуйте некоторой космической сложностью для улучшения скорости. Например, при создании списка можно указать, какие строки удовлетворяют свойству. – gowrath

ответ

3

Вы можете проверить первые два элемента каждого списка.

for totalist in all_lists: 
    if totalist[:2] == ['A', 'B']: 
     # Do something. 

Примечание: В Однострочник решения, предложенные Kasramvd довольно хорошо тоже. Я нашел мое решение более удобочитаемым. Хотя я должен сказать, что понимание немного быстрее обычного для циклов. (Что я испытал сам.)

+0

Это должно отлично работать, не зная, почему у вас есть нисходящий канал, но я ответил ему. – bravosierra99

+0

@ bravosierra99 Спасибо, OP попросил решение, если есть тот, который вам не нужно перебирать через элементы, возможно, именно поэтому я получил downvote. Но я не вижу способа, которым мы можем достичь этого, не перебирая все списки. – Rockybilly

+0

Я предполагаю, что он имел в виду, не зацикливая каждый отдельный элемент в каждом списке. Вы должны проходить через списки или не можете проверить каждый из них. – bravosierra99

1

В принципе, вы не можете сделать это в python с вложенным списком. Но если вы ищете оптимизированного подхода здесь несколько способов:

Используйте простой список понимания, сравнивая предполагаемый список только с первыми двумя пунктами суб списка:

>>> [sub for sub in totalist if sub[:2] == ['A', 'B']] 
[['A', 'B', 'C', 'D', 'E'], ['A', 'B', 'X', 'Y', 'Z']] 

Если вы хотите индексы использовать enumerate:

>>> [ind for ind, sub in enumerate(totalist) if sub[:2] == ['A', 'B']] 
[0, 1] 

А вот подход в Numpy, который в значительной степени оптимизированы, когда вы имеете дело с большими наборами данных:

>>> import numpy as np  
>>> 
>>> totalist = np.array([['A','B','C','D','E'], 
...      ['A','B','X','Y','Z'], 
...      ['A','F','T','U','V'], 
...      ['A','F','M','N','O']]) 

>>> totalist[(totalist[:,:2]==['A', 'B']).all(axis=1)] 
array([['A', 'B', 'C', 'D', 'E'], 
     ['A', 'B', 'X', 'Y', 'Z']], 
     dtype='|S1') 

Также в качестве альтернативы списка понимания в Python, если вы не хотите использовать цикл, и вы ищете функциональный способ, вы можете использовать filter функции, которая не как оптимизированная как список понимание:

>>> list(filter(lambda x: x[:2]==['A', 'B'], totalist)) 
[['A', 'B', 'C', 'D', 'E'], ['A', 'B', 'X', 'Y', 'Z']] 
+0

FYI, 'filter' отлично оптимизирован _если ваша предикатная функция встроена в C_ (и ваш вход достаточно длинный); в этом случае он обычно работает быстрее, чем эквивалент genexpr/listcomps. Если ему нужен lambda, который может генерировать генxpr/listcomp, он определенно будет медленнее, и если он использует функцию 'def', которую нельзя избежать в genexpr/listcomp, она обычно похожа на производительность (часто немного медленнее , но только просто). 'filter' продвинут, и совершенно нормально использовать ничего, кроме genexpr/listcomp, но вы можете использовать его для ускорения, если вы это понимаете. – ShadowRanger

+0

@ShadowRanger Да, я знаю это, но спасибо за комментарий. – Kasramvd

1

Вы могли бы это сделать.

>>> for i in totalist: 
...  if ['A','B']==i[:2]: 
...    print i 
+0

Я написал ответ, а потом увидел, что он написал то же самое. :-) –

+0

Случается для всех нас! –

0

Вы подразумеваете, что вас беспокоит производительность (стоимость). Если вам нужно это сделать, и если вас беспокоит производительность, вам нужна другая структура данных. Это добавит немного «стоимости» при создании списков, но сэкономит ваше время при их фильтрации.

Если необходимо отфильтровать на основе первых двух элементов (он не обобщается на первые n элементов), я бы добавил списки, как они сделаны, в dict, где ключ является кортежем из первых двух элементов, а элемент - список списков.

, тогда вы просто получаете свой список, выполняя поиск по типу. Это легко сделать и принесет потенциально большие ускорения, практически без затрат в памяти и времени при составлении списков.

+0

это не всегда будет первый два элемента, он будет продолжать увеличиваться по мере прохождения списка. В основном список - это пути и его классификация путей на ветку, вроде дерева. Я буду использовать приведенное выше утверждение if. – user1179317

2

Просто для удовольствия, itertools решения выдвинуть за поэлементную работу к слою C:

from future_builtins import map # Py2 only; not needed on Py3 
from itertools import compress 
from operator import itemgetter 

# Generator 
prefixes = map(itemgetter(slice(2)), totalist) 
selectors = map(['A','B'].__eq__, prefixes) 

# If you need them one at a time, just skip list wrapping and iterate 
# compress output directly 
matches = list(compress(totalist, selectors)) 

все это может быть один подкладка, чтобы:

matches = list(compress(totalist, map(['A','B'].__eq__, map(itemgetter(slice(2)), totalist)))) 

, но я бы не рекомендовал Это. Кстати, если totalist может быть генератором, а не повторно итерацию последовательность, вы хотите использовать itertools.tee, чтобы удвоить его, добавив:

totalist, forselection = itertools.tee(totalist, 2) 

и изменить определение prefixes к map над forselection, не totalist; поскольку compress повторяет итераторы параллельно, tee не будет иметь значительных накладных расходов памяти.

Конечно, как отмечали другие, даже переходя к C, это линейный алгоритм. В идеале вы должны использовать что-то вроде collections.defaultdict(list) для сопоставления двух префиксов элементов каждого из list (преобразованных в tuple, чтобы сделать их законными dict ключей) до list всего list с этим префиксом. Затем вместо линейного поиска по N list s, чтобы найти те, у которых есть соответствующие префиксы, вы просто делаете totaldict['A', 'B'], и вы получаете результаты с O(1) lookup (и менее фиксированная работа тоже, без постоянной нарезки).

Пример предвычисления работы:

from collections import defaultdict 

totaldict = defaultdict(list) 
for x in totalist: 
    totaldict[tuple(x[:2])].append(x) 

# Optionally, to prevent autovivification later: 
totaldict = dict(totaldict) 

Тогда вы можете получить matches эффективно мгновенно для любого два элемента префикса с просто:

matches = totaldict['A', 'B'] 
+1

Действительно, какая забава! Вы получаете голосование за забавный фактор, но, пожалуйста, не позволяйте этому быть принятым ответом! :) –

+0

@ RolfofSaxony: :-) Мне нравится 'itertools'; это почти наверняка не уместно здесь, но общий шаблон на самом деле является достойной иллюстрацией того, как использовать его хорошо для других целей. В случае с OP я почти наверняка перешел на маршрут 'defaultdict (list). – ShadowRanger

+0

@ RolfofSaxony Нет необходимости веселиться, объединять некоторые функции вместе и делать ненужные операции - это не правильный путь, особенно для такой простой задачи, которую можно выполнить с помощью более простых и быстрых подходов. – Kasramvd