Удаление дубликатов в списке списков на основе третьего элемента в каждом подсписке

#python #duplicate-removal #nested-lists

#python #дубликат-удаление #вложенные списки

Вопрос:

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

 c = [['470', '4189.0', 'asdfgw', 'fds'],
     ['470', '4189.0', 'qwer', 'fds'],
     ['470', '4189.0', 'qwer', 'dsfs fdv'] 
      ...]
 

c имеет около 30 000 внутренних списков. Что я хотел бы сделать, так это устранить дубликаты на основе 4-го элемента в каждом внутреннем списке. Таким образом, приведенный выше список списков будет выглядеть следующим образом:

 c = [['470', '4189.0', 'asdfgw', 'fds'],['470', '4189.0', 'qwer', 'dsfs fdv'] ...]
 

Вот что у меня есть до сих пор:

 d = [] #list that will contain condensed c
d.append(c[0]) #append first element, so I can compare lists
for bact in c: #c is my list of lists with 30,000 interior list
    for items in d:
        if bact[3] != items[3]:
            d.append(bact)  
 

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

У меня такое чувство, что создание совершенно нового списка списков довольно глупо. Любая помощь будет высоко оценена, и, пожалуйста, не стесняйтесь придираться, пока я учусь. Также, пожалуйста, исправьте мой словарь, если он неверен.

Комментарии:

1. Как вы узнаете, какой из дубликатов необходимо удалить?

2. Рассматривали ли вы отдельный set из четвертых элементов, уже имеющихся в выходных данных? Это значительно ускорит поиск членства.

3. Вы правы. Думаю, в настоящее время мне не нужна другая информация. Можете ли вы напрямую создать «набор», или мне выполнить итерацию по моему списку списков, создав список, а затем вызвать функцию set?

4. @user3754225 вы можете add изменять набор по мере продвижения, не повторяйте c дважды!

5. Вы должны проверить pandas. Если (как я предполагаю) это не будет последней табличной операцией, которую вы выполняете с этими данными, pandas будет очень хорошей инвестицией. И ваша операция в pandas просто df.drop_duplicates('col_4')

Ответ №1:

Я бы сделал это так:

 seen = set()
cond = [x for x in c if x[3] not in seen and not seen.add(x[3])]
 

Объяснение:

seen это набор, который отслеживает уже встреченные четвертые элементы каждого подсписка. cond это сжатый список. В случае x[3] , если (где x находится подсписок в c ) не находится в seen , x будет добавлен cond и x[3] будет добавлен seen .

seen.add(x[3]) вернется None , так not seen.add(x[3]) будет всегда True , но эта часть будет оцениваться только в том случае, если x[3] not in seen есть True , поскольку Python использует оценку короткого замыкания. Если выполняется второе условие, оно всегда будет возвращаться True и будет иметь побочный эффект добавления x[3] seen . Вот еще один пример того, что происходит ( print возвращает None и имеет «побочный эффект» печати чего-либо):

 >>> False and not print('hi')
False
>>> True and not print('hi')
hi
True
 

Комментарии:

1. Я смущен «и не». это логический эквивалент если бла, то бла? или это что-то совсем другое?

2. @user3754225 Я немного расширил объяснение

Ответ №2:

Используйте pandas. Я предполагаю, что у вас также есть лучшие имена столбцов.

 c = [['470', '4189.0', 'asdfgw', 'fds'],
     ['470', '4189.0', 'qwer', 'fds'],
     ['470', '4189.0', 'qwer', 'dsfs fdv']]
import pandas as pd
df = pd.DataFrame(c, columns=['col_1', 'col_2', 'col_3', 'col_4'])
df.drop_duplicates('col_4', inplace=True)
print df
 

   col_1   col_2   col_3     col_4
0   470  4189.0  asdfgw       fds
2   470  4189.0    qwer  dsfs fdv
 

Ответ №3:

У вас есть существенный логический недостаток в вашем текущем коде:

 for items in d:
    if bact[3] != items[3]:
        d.append(bact)  
 

это добавляет bact один d раз для каждого элемента, d который не совпадает. Для минимального исправления вам необходимо переключиться на:

 for items in d:
    if bact[3] == items[3]:
        break
else:
    d.append(bact)  
 

добавить bact один раз, если все элементы в d не совпадают. Я подозреваю, что это будет означать, что ваш код выполняется в более разумное время.


Кроме того, одним из очевидных улучшений производительности (повышение скорости, хотя и за счет использования памяти) было бы сохранение set четвертого элемента, который вы видели до сих пор. При поиске в наборе используются хэши, поэтому проверка членства (выделено) будет намного быстрее.

 d = []
seen = set()
for bact in c:
    if bact[3] not in seen: # membership test
        seen.add(bact[3])
        d.append(bact)