Как извлечь n-й элемент из списка кортежей?

112

Я пытаюсь получить n-е элементы из списка кортежей.

У меня что-то вроде:

elements = [(1,1,1),(2,3,7),(3,5,10)]

Я хочу извлечь в список только вторые элементы каждого кортежа:

seconds = [1, 3, 5]

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

pleasedontbelong
источник

Ответы:

34

Это тоже работает:

zip(*elements)[1]

(В основном я публикую это, чтобы доказать себе, что я проглотил zip...)

Посмотрите это в действии:

>>> help(zip)

Справка по встроенной функции zip во встроенном модуле :

zip (...)

zip (seq1 [, seq2 [...]]) -> [(seq1 [0], seq2 [0] ...), (...)]

Вернуть список кортежей, где каждый кортеж содержит i-й элемент из каждой из последовательностей аргументов. Возвращенный список усекается по длине до длины самой короткой последовательности аргументов.

>>> elements = [(1,1,1),(2,3,7),(3,5,10)]
>>> zip(*elements)
[(1, 2, 3), (1, 3, 5), (1, 7, 10)]
>>> zip(*elements)[1]
(1, 3, 5)
>>>

Изящная вещь, которую я узнал сегодня: использовать *listв аргументах для создания списка параметров функции ...

Примечание . В Python3 zipвозвращает итератор, поэтому вместо этого используйте list(zip(*elements))для возврата списка кортежей.

Дарен Томас
источник
2
и используйте **dictдля создания аргументов ключевого слова: def test(foo=3, bar=3): return foo*barзатемd = {'bar': 9, 'foo'=12}; print test(**d)
Уэйн Вернер
@ Уэйн Вернер: Ага. Все это было всего лишь пассивным знанием (я не часто им пользуюсь) - но хорошо, когда вам напоминают время от времени, чтобы вы знали, где / что искать ...
Дарен Томас
1
Быль - Я считаю , что в чем - либо я использую достаточно часто (Python, ВИМ), я , как правило, нужно напоминания о аккуратной / прохлады функции , которые я забыл , потому что я не использую их , что часто.
Уэйн Вернер,
синтаксис * list очень полезен. есть идеи, где это описано в официальной документации Python?
user1748155
Я нашел его только в учебнике: docs.python.org/2/tutorial/…
Дарен Томас,
30

Я знаю, что это можно сделать с помощью FOR, но я хотел знать, есть ли другой способ

Есть другой способ. Вы также можете сделать это с помощью map и itemgetter :

>>> from operator import itemgetter
>>> map(itemgetter(1), elements)

Это по-прежнему выполняет цикл внутри, и это немного медленнее, чем понимание списка:

setup = 'elements = [(1,1,1) for _ in range(100000)];from operator import itemgetter'
method1 = '[x[1] for x in elements]'
method2 = 'map(itemgetter(1), elements)'

import timeit
t = timeit.Timer(method1, setup)
print('Method 1: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup)
print('Method 2: ' + str(t.timeit(100)))

Полученные результаты:

Метод 1: 1.25699996948
Метод 2: 1.46600008011

Если вам нужно перебрать список, можно использовать a for.

Марк Байерс
источник
2
Небольшое дополнение: в python-3.x тест покажет, что карта занимает всего долю миллисекунды. Это потому, что он вернет итератор. method2 = 'list (map (itemgetter (1), elements))' отображает старое поведение.
Maik Beckmann
12

Нашел это, когда искал, какой способ быстрее всего вытащить второй элемент списка из двух кортежей. Не то, что я хотел, но провел тот же тест, что и третий метод, плюс протестировал метод zip

setup = 'elements = [(1,1) for _ in range(100000)];from operator import itemgetter'
method1 = '[x[1] for x in elements]'
method2 = 'map(itemgetter(1), elements)'
method3 = 'dict(elements).values()'
method4 = 'zip(*elements)[1]'

import timeit
t = timeit.Timer(method1, setup)
print('Method 1: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup)
print('Method 2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup)
print('Method 3: ' + str(t.timeit(100)))
t = timeit.Timer(method4, setup)
print('Method 4: ' + str(t.timeit(100)))

Method 1: 0.618785858154
Method 2: 0.711684942245
Method 3: 0.298138141632
Method 4: 1.32586884499

Таким образом, более чем в два раза быстрее, если у вас есть пара кортежей из 2, чтобы просто преобразовать в dict и принять значения.

Грэм Геллатли
источник
Это, вероятно, очевидно, но я бы упомянул, что dict(elements).values()это приведет к одноэлементному dict, а не к составлению списка или сопоставлению. Это именно то, что я хотел (меня интересовали уникальные элементы) (+1 и большое спасибо за публикацию), но другие могут задаться вопросом, почему dict работает быстрее - он не выделяет память, а только проверяет существующий элемент.
Greg0ry
6

Сроки для Python 3.6 для извлечения второго элемента из списка из двух кортежей.

Кроме того, добавлен numpyметод массива, который проще читать (но, возможно, проще, чем понимание списка).

from operator import itemgetter
elements = [(1,1) for _ in range(100000)]

%timeit second = [x[1] for x in elements]
%timeit second = list(map(itemgetter(1), elements))
%timeit second = dict(elements).values()
%timeit second = list(zip(*elements))[1]
%timeit second = np.array(elements)[:,1]

и время:

list comprehension:  4.73 ms ± 206 µs per loop
list(map):           5.3 ms ± 167 µs per loop
dict:                2.25 ms ± 103 µs per loop
list(zip)            5.2 ms ± 252 µs per loop
numpy array:        28.7 ms ± 1.88 ms per loop

Обратите внимание, что map()и zip()больше не возвращают список, отсюда явное преобразование.

Олег
источник
3
map (lambda x:(x[1]),elements)
Трас
источник
9
Попробуйте добавить некоторые пояснения.
fedorqui «SO, перестань причинять вред»
1

Использование isliceи chain.from_iterable:

>>> from itertools import chain, islice
>>> elements = [(1,1,1),(2,3,7),(3,5,10)]
>>> list(chain.from_iterable(islice(item, 1, 2) for item in elements))
[1, 3, 5]

Это может быть полезно, когда вам нужно более одного элемента:

>>> elements = [(0, 1, 2, 3, 4, 5), 
                (10, 11, 12, 13, 14, 15), 
                (20, 21, 22, 23, 24, 25)]
>>> list(chain.from_iterable(islice(tuple_, 2, 5) for tuple_ in elements))
[2, 3, 4, 12, 13, 14, 22, 23, 24]
Георгий
источник