Поиск дубликатов в списке списков
Я использую Python 2.7 и пытаюсь удалить дубликаты списка списков и объединить значения дубликатов.
Сейчас у меня есть:
original_list = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
Я хочу сопоставить первый элемент каждого вложенного списка, а затем добавить значения второго элемента. Я хочу закончить с этим (порядок окончательного списка не имеет значения):
ideal_output = [['a', 2], ['b', 7], ['c', 2]]
До сих пор у меня есть код, который найдет мне повторяющиеся значения, основанные на первом элементе каждого вложенного списка:
for item in original_list:
matches = -1
for x in original_list:
if (item[0] == x[0]):
matches += 1
if matches >= 1:
if item[0] not in duplicates_list:
duplicates_list.append(item[0])
Отсюда мне нужно найти все элементы duplicates_list, которые находятся в original_list, и добавить значения, но я не уверен, что лучший способ сделать это.
Ответы
Ответ 1
Много хороших ответов, но все они используют скорее больше кода, чем я для этого, поэтому здесь я беру, для чего это стоит:
totals = {}
for k,v in original_list:
totals[k] = totals.get(k,0) + v
# totals = {'a': 2, 'c': 2, 'b': 7}
Как только у вас есть такой диктофон, из любого из этих ответов вы можете использовать items
для получения списка кортежей:
totals.items()
# => [('a', 2), ('c', 2), ('b', 7)]
И сопоставьте list
по кортежам, чтобы получить список списков:
map(list, totals.items())
# => [['a', 2], ['c', 2], ['b', 7]]
И сортируйте, если вы хотите их в порядке:
sorted(map(list, totals.items()))
# => [['a', 2], ['b', 7], ['c', 2]]
Ответ 2
>>> from collections import Counter
>>> lst = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
>>> c = Counter(x for x, c in lst for _ in xrange(c))
Counter({'b': 7, 'a': 2, 'c': 2})
>>> map(list, c.iteritems())
[['a', 2], ['c', 2], ['b', 7]]
Или, альтернативно, не повторяя каждый элемент (a, b)
b раз (@hcwhsa):
>>> from collections import Counter
>>> lst = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
>>> c = sum((Counter(**{k:v}) for k, v in lst), Counter())
Counter({'b': 7, 'a': 2, 'c': 2})
>>> map(list, c.iteritems())
[['a', 2], ['c', 2], ['b', 7]]
Ответ 3
Решение
Используйте collections.Counter
:
from collections import Counter
original_list = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
result = Counter()
for k, v in original_list:
result.update({k:v})
map(list, result.items())
# [['a', 2], ['c', 2], ['b', 7]]
НАХОДКИ
Итак, много ответов, просмотров и опросов. Я даже заработал свой первый Nice answer
из ничего (за последние 2 дня я дал много ответов, заслуживающих больше исследований и усилий). В связи с этим я решил сделать хотя бы некоторые исследования и испытания решений с помощью простого script, написанного с нуля. Не включайте код непосредственно в ответ ради размера.
Каждая функция называется для нее автором, которую легко найти в вопросе. Решение thefourtheye
теперь равно одному из Mark Reed и оценивается в оригинальной форме, а fourtheye2 - для решения на основе itertools.groupby
.
Каждый из них тестировался несколько раз (образцы), каждый образец, в свою очередь, вызывал несколько итераций функций. Я оценил минимальное, максимальное и стандартное отклонение для выборок.
Здесь мы идем, запуская пробный тест в 10 раз.
testing: thefourtheye, kroolik2, void, kroolik, alko, reed, visser
10 samples
10 iterations each
author min avg max stddev
reed 0.00000 0.00000 0.00000 0.00000
visser 0.00000 0.00150 0.01500 0.00450
thefourtheye 0.00000 0.00160 0.01600 0.00480
thefourtheye2 0.00000 0.00310 0.01600 0.00620
alko 0.00000 0.00630 0.01600 0.00772
void 0.01500 0.01540 0.01600 0.00049
kroolik2 0.04700 0.06430 0.07800 0.00831
kroolik 0.32800 0.34380 0.37500 0.01716
Посмотрите внизу две строки: в этот момент решения kroolik были дисквалифицированы, так как с ним любое разумное количество образцов * итерации будут выполняться в течение нескольких часов. Здесь проходят финальные тесты. Я вручную добавил количество upvotes для ouptut:
testing: thefourtheye, kroolik2, void, kroolik, alko, reed, visser
100 samples
1000 iterations each
author upvotes min avg max stddev
reed [20] 0.06200 0.08174 0.15600 0.01841
thefourtheye [5] 0.06200 0.09971 0.20300 0.01911
visser [6] 0.10900 0.12392 0.23500 0.02263
thefourtheye2 0.25000 0.29674 0.89000 0.07183
alko [11] 0.56200 0.62309 1.04700 0.08438
void [3] 1.50000 1.65480 2.39100 0.18721
kroolik [14] [DSQ]
Ответ 4
Если заказ не имеет значения, вы можете использовать этот
original_list = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
myDict = {}
for first, second in original_list:
myDict[first] = myDict.get(first, 0) + second
result = [[key, value] for key, value in myDict.items()]
print result
Или вы можете использовать groupby, и код становится oneliner
original_list = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
from itertools import groupby
print [[key, sum(item[1] for item in list(group))]
for key, group in groupby(sorted(original_list), lambda x:x[0])]
Выход
[['a', 2], ['b', 7], ['c', 2]]
Ответ 5
Вы можете использовать collections.defaultdict
:
original_list = [['a', 1], ['b', 1], ['a', 1], ['b', 1], ['b', 2], ['c', 2], ['b', 3]]
import collections
data = collections.defaultdict(list)
for item in original_list:
data[item[0]].append(item[1])
output = {key: sum(values) for key, values in data.items()}
print output
# gives: {'a': 2, 'c': 2, 'b': 7}
Ответ 6
Я знаю, что это уродливо, но я немного повеселился, пытаясь реализовать его в 1 лайнере:
map(list, set(([(x[0], sum([i[1] for i in original_list if i[0]==x[0]])) for x in original_list])))
выход:
[['a', 2], ['b', 7], ['c', 2]]
Ответ 7
Возможно, вы тоже можете попробовать это,
>>> x = [[1,1],[2,2],[1,1],[2,2],[3,3],[4,4],[4,4]]
>>> z = []
>>> for i in x:
>>> if i not in z:
>>> z.append(i)
>>>
>>> z
[[1, 1], [2, 2], [3, 3], [4, 4]]