2017-02-01 11 views
2

Есть ли способ удалить дубликат кортежа на основе индекса в кортеже. Скажем, у меня естьудалить дублирующее значение кортежа на основе индекса в кортеже

[(0, 4, 1.0), (1, 4, 1.0), (3, 4, 1.0), (0, 3, 2.0), (1, 3, 2.0), (0, 2, 3.0), (1, 2, 3.0), (2, 4, 4.0), (2, 3, 5.0), (0, 1, inf)] 

Могу ли я случайно хранить один кортеж, где каждый дубликат имеет то же значение при индексе 2?

Итак, существует 3 кортежа, которые имеют значение 1.0 по индексу 2, два кортежа, которые имеют значение 2.0 по индексу 2, значение 3 с индексом 2 и т. Д.

Таким образом, (0, 4, 1.0) случайным образом может быть выбрано из значения 1.0 при индексе 2 и (1, 3, 2.0) может быть случайным образом выбрано из значения 2,0 при индексе 2. Скажем, (1, 2, 3,0) был случайным образом выбран из значения 3.0 с индексом 2. Тогда, мой список будет выглядеть

[(0, 4, 1.0),(1, 3, 2.0), (1, 2, 3.0), (2, 4, 4.0), (2, 3, 5.0), (0, 1, inf)] 

Я никогда не сталкивалась функцию, которая делает это или по крайней мере эффективно.

+0

являются все кортежи с одинаковым значением в индексе 2 рядом друг с другом на входе? Если не правильный порядок вывода? – niemmi

+0

Я могу сортировать их так, как будто они выше –

ответ

4

Вы можете использовать itertools.groupby группу кортежи на основе значения в индексе 2. Затем для каждой группы можно использовать random.choice выбрать кортеж:

>>> from itertools import groupby 
>>> import random 
>>> l = [(0, 4, 1.0), (1, 4, 1.0), (3, 4, 1.0), (0, 3, 2.0), (1, 3, 2.0), (0, 2, 3.0), (1, 2, 3.0), (2, 4, 4.0), (2, 3, 5.0), (0, 1, float('inf'))] 
>>> [random.choice(tuple(g)) for _, g in groupby(l, key=lambda x: x[2])] 
[(1, 4, 1.0), (1, 3, 2.0), (1, 2, 3.0), (2, 4, 4.0), (2, 3, 5.0), (0, 1, inf)] 

В выше groupby возвращает итератор (key, group) кортежей, где ключ значение возвращаемый второго параметра дано groupby и группа итерацию элементов в группе:

>>> [(k, tuple(g)) for k, g in groupby(l, key=lambda x: x[2])] 
[(1.0, ((0, 4, 1.0), (1, 4, 1.0), (3, 4, 1.0))), (2.0, ((0, 3, 2.0), (1, 3, 2.0))), (3.0, ((0, 2, 3.0), (1, 2, 3.0))), (4.0, ((2, 4, 4.0),)), (5.0, ((2, 3, 5.0),)), (inf, ((0, 1, inf),))] 

Поскольку нам не нужен ключ, мы можем отбросить его и превратить группу чтобы последовательность, которая рассчитывает random.choice:

>>> [random.choice(tuple(g)) for _, g in groupby(l, key=lambda x: x[2])] 
[(1, 4, 1.0), (1, 3, 2.0), (0, 2, 3.0), (2, 4, 4.0), (2, 3, 5.0), (0, 1, inf)] 

Следует отметить, что выше, предполагает, что кортежи с одинаковым значением при индексе 2 находятся рядом друг с другом на входе. Если это не так, вы можете отсортировать исходный список, прежде чем передавать его на groupby.

Update В случае, если вы просто хотите, первые три значения результата вы можете использовать выражение генератор вместо списка понимания и вытащить значения оттуда с islice:

>>> from itertools import islice 
>>> gen = (random.choice(tuple(g)) for _, g in groupby(l, key=lambda x: x[2])) 
>>> list(islice(gen, 3)) 
[(0, 4, 1.0), (1, 3, 2.0), (0, 2, 3.0)] 
+0

Примечание: список должен быть предварительно отсортирован на основе индекса 2, прежде чем использовать его с 'groupby' –

+0

Есть ли способ вытащить самые низкие 2 отсортированные значения в отсортированном списке, так что вам не нужно переходить к каждой ценности? –

+0

@MikeElJackson Я не уверен, что понимаю, что вы просите. Вы имеете в виду, что из исходного ввода следует рассматривать только '(0, 4, 1.0), (1, 4, 1.0)' '(==' (3, 4, 1.0) ')?Возможно, вы можете немного изменить исходный вопрос, чтобы привести пример. – niemmi

0

Я бы не сделать это в одном хотя я уверен, что это возможно.

я бы первым сделать один список для каждого значения по индексу 2:

values_at_index_2 = {t[2] for t in data} 
groups_by_value = [[t for t in data if t[2] == v] for v in values_at_index_2] 

Затем выберите один кортеж в каждой из групп:

import random 
new_data = [random.choice(group) for group in groups_by_value]