Сравните различное количество списков в Python

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

Будет несколько списков (около 20 или более, но я сократил их до трех, например, и целей тестирования), все с одинаковым количеством целых элементов в определенном порядке. Я хочу сравнивать элементы в одной позиции в каждом списке, чтобы найти различия. Для определенного количества списков это легко:

a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
b = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 4, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
c = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

for x,y,z in zip(a,b,c):
    if x != y != z:
        print x, y, z

Я пробовал обертывать этот цикл в функции, поэтому количество аргументов может меняться, но там я застрял.

def compare(*args):
    for x in zip(args):
        ???

В финальном скрипте у меня не будет нескольких отдельных списков, но все вместе в одном списке списка. Это поможет? Если я прокручу список списков, я не получу каждый список сразу ...

Forget the function, it's not really useful anyway as it will be part of a bigger script and it's too difficult defining the different arguments. I'm now comparing two lists at a time, saving those that are identical. That way, I can later easily remove all those from my whole list and keep only the unique ones.

l_o_l = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 4, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

for i in range(0, (len(l_o_l)-1)):
    for j in range((i+1), len(l_o_l)):
        if l_o_l[i] == l_o_l[j]:
            duplicates.append(key_list[i])
            duplicates.append(key_list[j])
dup = list(set(duplicates))
uniques = [x for x in key_list if x not in dup]

где key_list содержит, из словаря, идентификаторы для моих списков.

Любые предложения по улучшению?

1
В чем смысл l_o_l ? Просто любопытный : P
добавлено автор Oscar Mederos, источник
В чем смысл l_o_l ? Просто любопытный : P
добавлено автор Oscar Mederos, источник
как насчет распаковки кортежа в список? для x в zip (args): lst = list (x) , а затем , если len (set (lst))! = 1: print lst .
добавлено автор rxdazn, источник
как насчет распаковки кортежа в список? для x в zip (args): lst = list (x) , а затем , если len (set (lst))! = 1: print lst .
добавлено автор rxdazn, источник
@OscarMederos list_of_lists . ;-) Я знаю, что это немного вводит в заблуждение, но я не мог придумать ничего лучшего.
добавлено автор Lilith-Elina, источник
@OscarMederos list_of_lists . ;-) Я знаю, что это немного вводит в заблуждение, но я не мог придумать ничего лучшего.
добавлено автор Lilith-Elina, источник

10 ответы

Может быть, что-то вроде этого

def compare(*args):
    for things in zip(*args):
        yield all(x == things[0] for x in things)

Затем вы можете использовать его так

a = range(10)
b = range(10)
c = range(10)
d = range(11, 20)

for match in compare(a,b,c):
    print match

for match in compare(a,b,c,d):
    print match

Вот демонстрационный пример, использующий ваш пример (его генератор, поэтому вам нужно выполнить его или вывести его с помощью list ))

a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
b = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 4, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
c = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

print list(compare(a,b,c))
2
добавлено
Ах, пошли. Спасибо за дополнительный пример! В моем случае было бы достаточно знать, отличаются ли списки. Результаты сравнения всех значений представляют лишь незначительный интерес.
добавлено автор Lilith-Elina, источник
Это только печатает «True» для моего примера, и я не понимаю, почему.
добавлено автор Lilith-Elina, источник

Может быть, что-то вроде этого

def compare(*args):
    for things in zip(*args):
        yield all(x == things[0] for x in things)

Затем вы можете использовать его так

a = range(10)
b = range(10)
c = range(10)
d = range(11, 20)

for match in compare(a,b,c):
    print match

for match in compare(a,b,c,d):
    print match

Вот демонстрационный пример, использующий ваш пример (его генератор, поэтому вам нужно выполнить его или вывести его с помощью list ))

a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
b = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 4, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
c = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

print list(compare(a,b,c))
2
добавлено
Ах, пошли. Спасибо за дополнительный пример! В моем случае было бы достаточно знать, отличаются ли списки. Результаты сравнения всех значений представляют лишь незначительный интерес.
добавлено автор Lilith-Elina, источник
Это только печатает «True» для моего примера, и я не понимаю, почему.
добавлено автор Lilith-Elina, источник
def compare(*args):
    for x in zip(args):
        values_list = list(x[0]) # x[0] because x is a tuple
        different_values = set(values_list) # a set does not contain identical values
        if len(different_values) != 1: # if you have more than 1 value you have different values in your list
            print 'different values', values_list

дает тебе

a = [0, 0, 1]
b = [0, 1, 1]
c = [1, 1, 1]
compare(a, b, c)
>>> different values [0, 0, 1]
>>> different values [0, 1, 1]
1
добавлено
def compare(*args):
    for x in zip(args):
        values_list = list(x[0]) # x[0] because x is a tuple
        different_values = set(values_list) # a set does not contain identical values
        if len(different_values) != 1: # if you have more than 1 value you have different values in your list
            print 'different values', values_list

дает тебе

a = [0, 0, 1]
b = [0, 1, 1]
c = [1, 1, 1]
compare(a, b, c)
>>> different values [0, 0, 1]
>>> different values [0, 1, 1]
1
добавлено

Я думаю, что попытка усердно с * args и zip просто путает проблему. Я бы написал это примерно так:

def compare(list_of_lists):
    # assuming not an empty data set
    inner_len = len(list_of_lists[0])
    for index in range(inner_len):
        expected = list_of_lists[0][index]
        for inner_list in list_of_lists:
            if inner_list[index] != expected:
                # report difference at this index
1
добавлено
Вот почему перечисляет существует.
добавлено автор Bakuriu, источник
Почему вы выполняете итерацию в диапазоне (inner_len) ? Вы можете просто сделать для ожидаемого в list_of_lists [0]: . Итерация по индексам не является идиоматической и обычно намного медленнее обычной итерации.
добавлено автор Bakuriu, источник
Нет, это не совсем так, потому что нам нужен индекс, чтобы проконсультироваться с правильным элементом в других списках. Его можно было изменить, чтобы использовать что-то вроде для индекса, ожидаемое в перечислении (list_of_lists [0]): , но я лично считаю, что я написал это выше, чтобы быть более читаемым.
добавлено автор GrandOpener, источник

Я думаю, что попытка усердно с * args и zip просто путает проблему. Я бы написал это примерно так:

def compare(list_of_lists):
    # assuming not an empty data set
    inner_len = len(list_of_lists[0])
    for index in range(inner_len):
        expected = list_of_lists[0][index]
        for inner_list in list_of_lists:
            if inner_list[index] != expected:
                # report difference at this index
1
добавлено
Вот почему перечисляет существует.
добавлено автор Bakuriu, источник
Почему вы выполняете итерацию в диапазоне (inner_len) ? Вы можете просто сделать для ожидаемого в list_of_lists [0]: . Итерация по индексам не является идиоматической и обычно намного медленнее обычной итерации.
добавлено автор Bakuriu, источник
Нет, это не совсем так, потому что нам нужен индекс, чтобы проконсультироваться с правильным элементом в других списках. Его можно было изменить, чтобы использовать что-то вроде для индекса, ожидаемое в перечислении (list_of_lists [0]): , но я лично считаю, что я написал это выше, чтобы быть более читаемым.
добавлено автор GrandOpener, источник
def compare(elements):
    return len(set(elements)) == bool(elements)

Если вы хотите знать, все ли списки одинаковы, вы можете просто сделать:

all(compare(elements) for elements in zip(the_lists))

Альтернативой может быть преобразование list s в tuple s и использование set там:

len(set(tuple(the_list) for the_list in the_lists) == bool(the_lists)

Если вы просто хотите удалить дубликаты, это должно быть быстрее:

the_lists = [list(elem) for elem in set(tuple(the_list) for the_list in the_lists)]

Пример использования:

>>> a = range(100)
>>> b = range(100, 200)
>>> c = range(200, 300)
>>> d = a[:]
>>> e = b[:]
>>> the_lists = [a,b,c,d,e]
>>> the_lists2 = [list(elem) for elem in set(tuple(the_list) for the_list in the_lists)]
>>> [a,b,c] == sorted(the_lists2)  #order is not maintained by set
True

Это выглядит довольно быстро:

>>> timeit.timeit('[list(elem) for elem in set(tuple(the_list) for the_list in the_lists)]', 'from __main__ import the_lists', number=1000000)
7.949447154998779

Менее 8 секунд для выполнения 1 миллион раз. (Где the_lists тот же самый использованный ранее).

Edit: If you want to remove only the duplicated list then the simplest algorithm I can think of is sorting the list-of-lists and using itertools.groupby:

>>> a = range(100)
>>> b = range(100,200)
>>> c = range(200,300)
>>> d = a[:]
>>> e = b[:]
>>> the_lists = [a,b,c,d,e]
>>> the_lists.sort()
>>> import itertools as it
>>> for key, group in it.groupby(the_lists):
...     if len(list(group)) == 1:
...             print key
... 
[200, 201, 202, ..., 297, 298, 299]
0
добавлено
@ Лилит-Элина. Я обновил свой ответ. Кстати, решения , основанные на сравнении , - это Ω (nlogn), потому что проблема «эквивалентна» проблеме нахождения, если последовательность содержит два равных элемента, которые «эквивалентны» сортировке. Если вам нужно более быстрое решение, вы должны придумать что-то, что не сравнивает списки.
добавлено автор Bakuriu, источник
Нет, я не хочу удалять дубликаты, я хочу найти уникальные списки (или вы могли бы назвать их шаблонами) в коллекции списков. Однако у вас много хороших идей.
добавлено автор Lilith-Elina, источник
def compare(elements):
    return len(set(elements)) == bool(elements)

Если вы хотите знать, все ли списки одинаковы, вы можете просто сделать:

all(compare(elements) for elements in zip(the_lists))

Альтернативой может быть преобразование list s в tuple s и использование set там:

len(set(tuple(the_list) for the_list in the_lists) == bool(the_lists)

Если вы просто хотите удалить дубликаты, это должно быть быстрее:

the_lists = [list(elem) for elem in set(tuple(the_list) for the_list in the_lists)]

Пример использования:

>>> a = range(100)
>>> b = range(100, 200)
>>> c = range(200, 300)
>>> d = a[:]
>>> e = b[:]
>>> the_lists = [a,b,c,d,e]
>>> the_lists2 = [list(elem) for elem in set(tuple(the_list) for the_list in the_lists)]
>>> [a,b,c] == sorted(the_lists2)  #order is not maintained by set
True

Это выглядит довольно быстро:

>>> timeit.timeit('[list(elem) for elem in set(tuple(the_list) for the_list in the_lists)]', 'from __main__ import the_lists', number=1000000)
7.949447154998779

Менее 8 секунд для выполнения 1 миллион раз. (Где the_lists тот же самый использованный ранее).

Edit: If you want to remove only the duplicated list then the simplest algorithm I can think of is sorting the list-of-lists and using itertools.groupby:

>>> a = range(100)
>>> b = range(100,200)
>>> c = range(200,300)
>>> d = a[:]
>>> e = b[:]
>>> the_lists = [a,b,c,d,e]
>>> the_lists.sort()
>>> import itertools as it
>>> for key, group in it.groupby(the_lists):
...     if len(list(group)) == 1:
...             print key
... 
[200, 201, 202, ..., 297, 298, 299]
0
добавлено
@ Лилит-Элина. Я обновил свой ответ. Кстати, решения , основанные на сравнении , - это Ω (nlogn), потому что проблема «эквивалентна» проблеме нахождения, если последовательность содержит два равных элемента, которые «эквивалентны» сортировке. Если вам нужно более быстрое решение, вы должны придумать что-то, что не сравнивает списки.
добавлено автор Bakuriu, источник
Нет, я не хочу удалять дубликаты, я хочу найти уникальные списки (или вы могли бы назвать их шаблонами) в коллекции списков. Однако у вас много хороших идей.
добавлено автор Lilith-Elina, источник

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

def compare(*args):
    for x in zip(args):
        if min(x) != max(x):
             print x
0
добавлено

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

def compare(*args):
    for x in zip(args):
        if min(x) != max(x):
             print x
0
добавлено
Python
Python
7 654 участник(ов)

Уютный чат для профессионалов, занимающихся поиском питоньих мудростей. Как не получить бан: https://t.me/ru_python/577926

Python beginners
Python beginners
4 449 участник(ов)

Вопросы про Python для чайников. Cпам и троллинг неприемлем. Не злоупотребляйте стикерами. Частозадаваемые вопросы: https://github.com/ru-python-beginners/faq/blob/master/README.md Статистика тут: https://grstats.me/chat/x4qym2k5uvfkr3al6at7

Верстка сайтов HTML/CSS/JS/PHP
Верстка сайтов HTML/CSS/JS/PHP
3 439 участник(ов)

Правила группы: напишите !rules в чате. Группа Вк: vk.com/web_structure Freelancer: @web_fl Веб Дизайн: @dev_design Маркетолог: @topmarkening Автор: @M_Boroda

CSS — русскоговорящее сообщество
CSS — русскоговорящее сообщество
1 502 участник(ов)

Сообщество любителей CSS Возникли проблемы с CSS? – пиши сюда, обсудим и предложим самое лучшее решение Работа: @css_ru_jobs Правила: https://teletype.in/@css_ru/r1EWtQ2w7 Приходите в наши чаты @javascript_ru и @frontend_ru Флуд: @css_flood

pro.python
pro.python
1 090 участник(ов)

Сообщество разработчиков под Python Создатель: @rodgelius

Чат — Типичный Верстальщик
Чат — Типичный Верстальщик
1 080 участник(ов)

Основной канал: @tpverstak Обратная связь: @annblok Все ссылки на соц.сети проекта: http://taplink.cc/tpverstak ПРАВИЛА ЧАТА — https://teletype.in/@annblok/BygPgC3E7

Rude Python
Rude Python
971 участник(ов)

Python без „девочек”, здесь матерятся и унижают Django. Not gay friendly. Правила: t.me/rudepython/114107 @rudepython | t.me/rudepython

rupython
rupython
509 участник(ов)

Группа создана с целью оперативного получения ответов на возникающие вопросы по разработке на яп python, смежные темы, а также человеческого общения. Приветствую!

Python-programming
Python-programming
266 участник(ов)

Чат группы вконтакте https://vk.com/python_community

Веб-Технологи: UI/UX, Вёрстка, Фронтенд
Веб-Технологи: UI/UX, Вёрстка, Фронтенд
167 участник(ов)

Всё про веб-дизайн и вёрстку. А также: HTML, CSS, флекс и бутстрапы, шаблонизаторы, препроцессоры, методологии, аглифаеры, улучшаторы и обфускаторы. Обсуждаем темы юзабилити, устраиваем А/В тесты лендингов, и проводим аудит.

DTP :: @DTPublish
DTP :: @DTPublish
147 участник(ов)

Обсуждаемые темы: полиграфия, препресс, верстка, дизайн, иллюстрации, скрипты, плагины. Канал - @DTPublishing

css_jobs
css_jobs
26 участник(ов)

Чат для вопросов по css и html: @css_ru Флуд: @css_flood Канал с вакансиями и резюме: @css_jobs_feed

css_флуд
css_флуд
10 участник(ов)