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

178

У меня есть список, как показано ниже, где первый элемент является идентификатором, а другой является строкой:

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

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

[1,2]

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

wasimbhalli
источник

Ответы:

245
>>> a = [(1, u'abc'), (2, u'def')]
>>> [i[0] for i in a]
[1, 2]
Ракеш
источник
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]
WayneSan
источник
для этого нужен отдельный импорт?
JuliandotNut
2
@JuliandotNut Нет, это встроенная функция. (в Python 2.x)
WayneSan
22

ты имеешь в виду что-то подобное?

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

На самом деле у вас есть список tupleобъектов, а не список множеств (как и предполагал ваш первоначальный вопрос). Если это на самом деле список наборов, то первого элемента не существует, поскольку наборы не имеют порядка.

Здесь я создал плоский список, потому что в целом это кажется более полезным, чем создание списка из 1 элемента кортежей. Тем не менее, вы можете легко создать список кортежей из 1 элемента, просто заменив seq[0]на (seq[0],).

mgilson
источник
Я попробовал это. Это дает эту ошибку:int() argument must be a string or a number, not 'QuerySet'
wasimbhalli
4
@wasimbhalli - int()нигде в моем решении, поэтому исключение, которое вы видите, должно появиться позже в коде.
Мгилсон
Я обновил вопрос, мне нужно использовать этот список позже __inдля фильтрации данных
wasimbhalli
что такое __in? - На основе приведенного вами примера ввода будет создан список целых чисел. Однако, если ваш список кортежей не начинается с целых чисел, вы не получите целые числа, и вам нужно будет сделать их целыми числами через intили попытаться выяснить, почему ваш первый элемент не может быть преобразован в целое число.
Мгилсон
Работает ли 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'
ssoler
источник
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')]
bcattle
источник
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>

Я обнаружил, что должен был использовать list ():

>>> 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()
kederrac
источник
0

Это кортежи, а не наборы. Ты можешь сделать это:

l1 = [(1, u'abc'), (2, u'def')]
l2 = [(tup[0],) for tup in l1]
l2
>>> [(1,), (2,)]
Lanaru
источник
2
Не совсем то, о чем спрашивают
Безумный физик
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]
kederrac
источник
0

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

Это было бы решением в numpy:

>>> import numpy as np

>>> a = np.asarray([(1, u'abc'), (2, u'def')])
>>> a[:, 0].astype(int).tolist()
[1, 2]
CodePrinz
источник