Проверьте, идентичны ли все элементы в списке
Мне нужна следующая функция:
Вход: a list
Выход:
-
True
, если все элементы в списке ввода оцениваются как равные друг другу с помощью стандартного оператора равенства;
-
False
в противном случае.
Производительность: конечно, я предпочитаю не налагать лишних накладных расходов.
Я считаю, что было бы лучше:
- итерация по списку
- сравнить соседние элементы
- и
AND
все полученные булевы значения
Но я не уверен, какой самый питонический способ сделать это.
ИЗМЕНИТЬ
Спасибо за все замечательные ответы. Я оценил несколько, и было очень сложно выбирать между решениями @KennyTM и @Ivo van der Wijk.
Недостаток функции короткого замыкания болит только на длинном входе (более ~ 50 элементов), которые на ранних этапах имеют неравные элементы. Если это происходит достаточно часто (как часто зависит от того, как долго могут быть списки), требуется короткое замыкание. Лучшим алгоритмом короткого замыкания является @KennyTM checkEqual1
. Тем не менее, это требует значительных затрат:
- до 20x в производительности почти идентичные списки
- до 2,5x в производительности в коротких списках
Если длинные входы с ранними неравными элементами не происходят (или происходят достаточно редко), короткое замыкание не требуется. Тогда самым быстрым решением является @Ivo van der Wijk.
Ответы
Ответ 1
Общий метод:
def checkEqual1(iterator):
iterator = iter(iterator)
try:
first = next(iterator)
except StopIteration:
return True
return all(first == rest for rest in iterator)
Один лайнер:
def checkEqual2(iterator):
return len(set(iterator)) <= 1
Также однострочный:
def checkEqual3(lst):
return lst[1:] == lst[:-1]
Разница между тремя версиями заключается в следующем:
- В
checkEqual2
содержимое должно быть хешируемым. -
checkEqual1
и checkEqual2
могут использовать любые итераторы, но checkEqual3
должен принимать ввод последовательности, обычно конкретные контейнеры, такие как список или кортеж. -
checkEqual1
останавливается, как только обнаруживается разница. - Так как
checkEqual1
содержит больше кода Python, он менее эффективен, когда многие из элементов равны в начале. - Поскольку
checkEqual2
и checkEqual3
всегда выполняют операции копирования O (N), они будут занимать больше времени, если большая часть вашего ввода вернет False. - Для
checkEqual2
и checkEqual3
сложнее адаптировать сравнение от a == b
к a is b
.
timeit
для Python 2.7 и (только s1, s4, s7, s9 должны возвращать True)
s1 = [1] * 5000
s2 = [1] * 4999 + [2]
s3 = [2] + [1]*4999
s4 = [set([9])] * 5000
s5 = [set([9])] * 4999 + [set([10])]
s6 = [set([10])] + [set([9])] * 4999
s7 = [1,1]
s8 = [1,2]
s9 = []
мы получаем
| checkEqual1 | checkEqual2 | checkEqual3 | checkEqualIvo | checkEqual6502 |
|-----|-------------|-------------|--------------|---------------|----------------|
| s1 | 1.19 msec | 348 usec | 183 usec | 51.6 usec | 121 usec |
| s2 | 1.17 msec | 376 usec | 185 usec | 50.9 usec | 118 usec |
| s3 | 4.17 usec | 348 usec | 120 usec | 264 usec | 61.3 usec |
| | | | | | |
| s4 | 1.73 msec | | 182 usec | 50.5 usec | 121 usec |
| s5 | 1.71 msec | | 181 usec | 50.6 usec | 125 usec |
| s6 | 4.29 usec | | 122 usec | 423 usec | 61.1 usec |
| | | | | | |
| s7 | 3.1 usec | 1.4 usec | 1.24 usec | 0.932 usec | 1.92 usec |
| s8 | 4.07 usec | 1.54 usec | 1.28 usec | 0.997 usec | 1.79 usec |
| s9 | 5.91 usec | 1.25 usec | 0.749 usec | 0.407 usec | 0.386 usec |
Заметка:
# http://stackoverflow.com/q/3844948/
def checkEqualIvo(lst):
return not lst or lst.count(lst[0]) == len(lst)
# http://stackoverflow.com/q/3844931/
def checkEqual6502(lst):
return not lst or [lst[0]]*len(lst) == lst
Ответ 2
Решение быстрее, чем использование set(), которое работает с последовательностями (а не итерами), состоит в простом подсчете первого элемента. Это предполагает, что список не пуст (но это тривиально, чтобы проверить и решить, какой результат должен быть в пустом списке)
x.count(x[0]) == len(x)
несколько простых тестов:
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*5000', number=10000)
1.4383411407470703
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*4999+[2]', number=10000)
1.4765670299530029
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*5000', number=10000)
0.26274609565734863
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*4999+[2]', number=10000)
0.25654196739196777
Ответ 3
Самый простой и самый элегантный способ заключается в следующем:
all(x==myList[0] for x in myList)
(Да, это работает даже с пустым списком! Это потому, что это один из немногих случаев, когда у python ленивая семантика.)
Что касается производительности, то это не удастся в самое ближайшее время, поэтому она асимптотически оптимальна.
Ответ 4
Сложная сравнительная работа:
len(set(the_list)) == 1
Использование set
удаляет все повторяющиеся элементы.
Ответ 5
Вы можете преобразовать список в набор. У набора не может быть дубликатов. Поэтому, если все элементы в исходном списке идентичны, набор будет иметь только один элемент.
if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
Ответ 6
Для чего это стоит, недавно появилось в список рассылки python-идей. Оказывается, что для этого уже существует itertools recipe: 1
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
Предположительно, он работает очень хорошо и имеет несколько приятных свойств.
- Короткие замыкания: он перестанет потреблять элементы из итерируемого, как только он найдет первый неравный элемент.
- Не требует, чтобы элементы были хешируемыми.
- Он ленив и требует только O (1) дополнительной памяти для проверки.
1 Другими словами, я не могу взять на себя ответственность за то, чтобы придумать решение - и я не могу взять на себя ответственность за его поиск.
Ответ 7
Вот два простых способа сделать это
используя set()
При преобразовании списка в набор удаляются повторяющиеся элементы. Поэтому, если длина преобразованного набора равна 1, это означает, что все элементы одинаковы.
len(set(input_list))==1
Вот пример
>>> a = ['not', 'the', 'same']
>>> b = ['same', 'same', 'same']
>>> len(set(a))==1 # == 3
False
>>> len(set(b))==1 # == 1
True
используя все()
Это будет сравнивать (эквивалентность) первый элемент входного списка с каждым другим элементом в списке. Если все эквиваленты True будут возвращены, в противном случае возвращается False.
all(element==input_list[0] for element in input_list)
Вот пример
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 1, 1, 1, 1]
>>> all(number==a[0] for number in a)
False
>>> all(number==b[0] for number in b)
True
PS Если вы проверяете, соответствует ли весь список определенному значению, вы можете suibstitue значение для input_list [0].
Ответ 8
Это еще один вариант, более быстрый, чем len(set(x))==1
для длинных списков (используется короткое замыкание)
def constantList(x):
return x and [x[0]]*len(x) == x
Ответ 9
Это простой способ сделать это:
result = mylist and all(mylist[0] == elem for elem in mylist)
Это немного сложнее, оно навлекает на вызов функции, но семантика более четко прописана:
def all_identical(seq):
if not seq:
# empty list is False.
return False
first = seq[0]
return all(first == elem for elem in seq)
Ответ 10
Убедитесь, что все элементы равны первому.
np.allclose(array, array[0])
Ответ 11
Сомнительно, что это "самый Pythonic", но что-то вроде:
>>> falseList = [1,2,3,4]
>>> trueList = [1, 1, 1]
>>>
>>> def testList(list):
... for item in list[1:]:
... if item != list[0]:
... return False
... return True
...
>>> testList(falseList)
False
>>> testList(trueList)
True
сделал бы трюк.
Ответ 12
Если вам интересно что-то более читаемое (но, конечно, не так эффективно), вы можете попробовать:
def compare_lists(list1, list2):
if len(list1) != len(list2): # Weed out unequal length lists.
return False
for item in list1:
if item not in list2:
return False
return True
a_list_1 = ['apple', 'orange', 'grape', 'pear']
a_list_2 = ['pear', 'orange', 'grape', 'apple']
b_list_1 = ['apple', 'orange', 'grape', 'pear']
b_list_2 = ['apple', 'orange', 'banana', 'pear']
c_list_1 = ['apple', 'orange', 'grape']
c_list_2 = ['grape', 'orange']
print compare_lists(a_list_1, a_list_2) # Returns True
print compare_lists(b_list_1, b_list_2) # Returns False
print compare_lists(c_list_1, c_list_2) # Returns False
Ответ 13
Я бы сделал:
not any((x[i] != x[i+1] for i in range(0, len(x)-1)))
as any
останавливает поиск итерации, как только находит условие True
.
Ответ 14
Относительно использования reduce()
с lambda
. Вот рабочий код, который я лично считаю более приятным, чем некоторые другие ответы.
reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))
Возвращает truple, где первое значение является логическим, если все элементы одинаковы или нет.
Ответ 15
Преобразуйте список в набор, а затем найдите количество элементов в наборе. Если результат равен 1, он имеет одинаковые элементы, а если нет, то элементы в списке не идентичны.
list1 = [1,1,1]
len(set(list1))
>1
list1 = [1,2,3]
len(set(list1)
>3
Ответ 16
>>> a = [1, 2, 3, 4, 5, 6]
>>> z = [(a[x], a[x+1]) for x in range(0, len(a)-1)]
>>> z
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
# Replacing it with the test
>>> z = [(a[x] == a[x+1]) for x in range(0, len(a)-1)]
>>> z
[False, False, False, False, False]
>>> if False in z : Print "All elements are not equal"
Ответ 17
def allTheSame(i):
j = itertools.groupby(i)
for k in j: break
for k in j: return False
return True
Работает в Python 2.4, у которого нет "всех".
Ответ 18
Можно использовать карту и лямбда
lst = [1,1,1,1,1,1,1,1,1]
print all(map(lambda x: x == lst[0], lst[1:]))
Ответ 19
Или используйте метод diff
numpy:
import numpy as np
def allthesame(l):
return np.all(np.diff(l)==0)
И позвонить:
print(allthesame([1,1,1]))
Выход:
True
Ответ 20
Или используйте метод сравнения numpy:
import numpy as np
def allthesame(l):
return np.unique(l).shape[0]<=1
И позвонить:
print(allthesame([1,1,1]))
Выход:
Правда
Ответ 21
Вы можете сделать:
reduce(and_, (x==yourList[0] for x in yourList), True)
Довольно раздражает, что python заставляет вас импортировать операторы типа operator.and_
. Начиная с python3 вам также потребуется импортировать functools.reduce
.
(Вы не должны использовать этот метод, потому что он не будет ломаться, если он найдет не равные значения, но продолжит рассмотрение всего списка. Он просто включен здесь в качестве ответа для полноты.)
Ответ 22
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]
Следующее короткое замыкание:
all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))
Ответ 23
Измените список на набор. Тогда, если размер набора равен только 1, они должны быть одинаковыми.
if len(set(my_list)) == 1:
Ответ 24
Существует также рекурсивная опция чистого Python:
def checkEqual(lst):
if len(lst)==2 :
return lst[0]==lst[1]
else:
return lst[0]==lst[1] and checkEqual(lst[1:])
Однако по некоторым причинам он в некоторых случаях на два порядка медленнее, чем другие варианты. Исходя из менталитета языка Си, я ожидал, что это будет быстрее, но это не так!
Другим недостатком является то, что в Python есть предел рекурсии, который в этом случае необходимо отрегулировать. Например, используя это.
Ответ 25
Вы можете использовать .nunique()
чтобы найти количество уникальных элементов в списке.
def identical_elements(list):
series = pd.Series(list)
if series.nunique() == 1: identical = True
else: identical = False
return identical
identical_elements(['a', 'a'])
Out[427]: True
identical_elements(['a', 'b'])
Out[428]: False
Ответ 26
Вы можете использовать set
это сделает набор и удалит повторяющиеся элементы. затем проверьте, имеет ли он 1 элемент.
if set(your_list) == 1:
print('all ements are equal')