Як отримати перший елемент у списку кортежів?


178

У мене є список, як нижче, де перший елемент - це ідентифікатор, а другий - рядок:

[(1, u'abc'), (2, u'def')]

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

[1,2]

Я буду використовувати цей список, __inтому він повинен бути списком цілих значень.

Відповіді:



68

Використовуйте функцію zip для роз’єднання елементів:

>>> inpt = [(1, u'abc'), (2, u'def')]
>>> unzipped = zip(*inpt)
>>> print unzipped
[(1, 2), (u'abc', u'def')]
>>> print list(unzipped[0])
[1, 2]

Редагувати (@BradSolomon): Наведене вище працює для Python 2.x, де zipповертає список.

У Python 3.x zipповертає ітератор і таке, що еквівалентно вище:

>>> print(list(list(zip(*inpt))[0]))
[1, 2]

чи потрібен цей окремий імпорт?
JuliandotNut

2
@JuliandotNut Ні, це вбудована функція. (у Python 2.x)
WayneSan

22

ти маєш на увазі щось подібне?

new_list = [ seq[0] for seq in yourlist ]

Насправді у вас є список tupleоб'єктів, а не список наборів (як випливало з оригінального запитання). Якщо це насправді список наборів, то першого елемента немає, тому що множини не мають порядку.

Тут я створив плоский список, тому що загалом це здається більш корисним, ніж створення списку з 1 елемента кортезів. Однак ви можете легко створити список кортежів з 1 елемента, просто замінивши seq[0]їх (seq[0],).


Я спробував це. Це дає цю помилку:int() argument must be a string or a number, not 'QuerySet'
wasimbhalli

4
@wasimbhalli - int()ніде в моєму рішенні, тому виняток, який ви бачите, повинен з’явитися згодом у коді.
mgilson

Я оновив питання, мені потрібно використовувати цей список пізніше __inдля фільтрації даних
wasimbhalli

що таке __in? - На основі введеного вами прикладу, це створить список цілих чисел. Однак якщо ваш список кортезів не починається з цілих чисел, ви не отримаєте цілі числа, і вам потрібно буде зробити їх цілими числами через intабо спробувати з'ясувати, чому ваш перший елемент не може бути перетворений на ціле число.
mgilson

Чи new_list = [ seq[0] for seq in yourlist if type(seq[0]) == int]працює?
pR0Ps

11

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

>>> my_list = [(1, u'abc'), (2, u'def')]
>>> my_ids = [idx for idx, val in my_list]
>>> my_ids
[1, 2]

У час ітерації кожен кортеж розпаковується, його значення встановлюються змінними idxта val.

>>> x = (1, u'abc')
>>> idx, val = x
>>> idx
1
>>> val
u'abc'

8

Це для чого operator.itemgetter.

>>> a = [(1, u'abc'), (2, u'def')]
>>> import operator
>>> b = map(operator.itemgetter(0), a)
>>> b
[1, 2]

itemgetterОператор повертає функцію , яка повертає індекс елемента , який ви вкажете. Це точно так само, як писати

>>> b = map(lambda x: x[0], a)

Але я вважаю, що itemgetterце ясніше і явніше .

Це зручно для створення компактних заяв про сортування. Наприклад,

>>> c = sorted(a, key=operator.itemgetter(0), reverse=True)
>>> c
[(2, u'def'), (1, u'abc')]

7

З точки зору продуктивності, в python3.X

  • [i[0] for i in a]і list(zip(*a))[0]є рівнозначними
  • вони швидші, ніж list(map(operator.itemgetter(0), a))

Код

import timeit


iterations = 100000
init_time = timeit.timeit('''a = [(i, u'abc') for i in range(1000)]''', number=iterations)/iterations
print(timeit.timeit('''a = [(i, u'abc') for i in range(1000)]\nb = [i[0] for i in a]''', number=iterations)/iterations - init_time)
print(timeit.timeit('''a = [(i, u'abc') for i in range(1000)]\nb = list(zip(*a))[0]''', number=iterations)/iterations - init_time)

вихід

3.491014136001468e-05

3.422205176000717e-05


6

якщо кортежі унікальні, то це може спрацювати

>>> a = [(1, u'abc'), (2, u'def')]
>>> a
[(1, u'abc'), (2, u'def')]
>>> dict(a).keys()
[1, 2]
>>> dict(a).values()
[u'abc', u'def']
>>> 

4
Це втратить порядок. ordereddictОднак це може працювати .
Тім Тісдалл

якщо 2 або більше кортежів мають один і той же перший елемент, ніж ваше рішення не буде працювати
kederrac

3

коли я бігав (як було запропоновано вище):

>>> a = [(1, u'abc'), (2, u'def')]
>>> import operator
>>> b = map(operator.itemgetter(0), a)
>>> b

замість повернення:

[1, 2]

Я отримав це як повернення:

<map at 0xb387eb8>

Я виявив, що мені потрібно скористатися списком ():

>>> b = list(map(operator.itemgetter(0), a))

щоб успішно повернути список за допомогою цієї пропозиції. Це сказало, що я задоволений цим рішенням, дякую. (тестується / запускається за допомогою Spyder, консолі iPython, Python v3.6)


3

Я думав, що може бути корисним порівняти тривалість виконання різних підходів, тому зробив орієнтир (використовуючи бібліотеку simple_benchmark )

I) Бенчмарк, що містить кортежі з 2 елементами введіть тут опис зображення

Оскільки ви можете розраховувати, що виберіть перший елемент з кортежів за індексом, 0це найшвидше рішення, дуже близьке до розпакування, очікуючи рівно 2 значення

import operator
import random

from simple_benchmark import BenchmarkBuilder

b = BenchmarkBuilder()



@b.add_function()
def rakesh_by_index(l):
    return [i[0] for i in l]


@b.add_function()
def wayneSan_zip(l):
    return list(list(zip(*l))[0])


@b.add_function()
def bcattle_itemgetter(l):
     return list(map(operator.itemgetter(0), l))


@b.add_function()
def ssoler_upacking(l):
    return [idx for idx, val in l]

@b.add_function()
def kederrack_unpacking(l):
    return [f for f, *_ in l]



@b.add_arguments('Number of tuples')
def argument_provider():
    for exp in range(2, 21):
        size = 2**exp
        yield size, [(random.choice(range(100)), random.choice(range(100))) for _ in range(size)]


r = b.run()
r.plot()

II) Бенчмарк, що містить кортежі з 2 або більше елементами введіть тут опис зображення

import operator
import random

from simple_benchmark import BenchmarkBuilder

b = BenchmarkBuilder()

@b.add_function()
def kederrack_unpacking(l):
    return [f for f, *_ in l]


@b.add_function()
def rakesh_by_index(l):
    return [i[0] for i in l]


@b.add_function()
def wayneSan_zip(l):
    return list(list(zip(*l))[0])


@b.add_function()
def bcattle_itemgetter(l):
     return list(map(operator.itemgetter(0), l))


@b.add_arguments('Number of tuples')
def argument_provider():
    for exp in range(2, 21):
        size = 2**exp
        yield size, [tuple(random.choice(range(100)) for _
                     in range(random.choice(range(2, 100)))) for _ in range(size)]

from pylab import rcParams
rcParams['figure.figsize'] = 12, 7

r = b.run()
r.plot()


0

ви можете розпакувати кортежі та отримати лише перший елемент, використовуючи розуміння списку:

l = [(1, u'abc'), (2, u'def')]
[f for f, *_ in l]

вихід:

[1, 2]

це буде працювати незалежно від того, скільки елементів у вас є в кортежі:

l = [(1, u'abc'), (2, u'def', 2, 4, 5, 6, 7)]
[f for f, *_ in l]

вихід:

[1, 2]

0

Мені було цікаво, чому ніхто не пропонував використовувати numpy, але тепер після перевірки я розумію. Це, мабуть, не найкраще для масивів змішаного типу.

Це було б рішення в numpy:

>>> import numpy as np

>>> a = np.asarray([(1, u'abc'), (2, u'def')])
>>> a[:, 0].astype(int).tolist()
[1, 2]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.