python - sacar - Compruebe si todos los elementos en una lista son idénticos
llenar una lista en python (22)
Necesito la siguiente función:
Entrada : una list
Salida :
-
True
si todos los elementos de la lista de entrada se evalúan como iguales entre sí utilizando el operador de igualdad estándar; -
False
contrario.
Rendimiento : por supuesto, prefiero no incurrir en gastos generales innecesarios.
Siento que sería mejor:
- iterar a través de la lista
- comparar elementos adyacentes
- y
AND
todos los valores booleanos resultantes
Pero no estoy seguro de cuál es la forma más pitónica de hacerlo.
EDITAR :
Gracias por todas las grandes respuestas. Clasifiqué varios, y fue realmente difícil elegir entre las soluciones @KennyTM y @Ivo van der Wijk.
La falta de la función de cortocircuito solo duele en una entrada larga (más de ~ 50 elementos) que tienen elementos desiguales desde el principio. Si esto ocurre con la frecuencia suficiente (la frecuencia depende de la duración de las listas), se requiere el cortocircuito. El mejor algoritmo de cortocircuito parece ser @KennyTM checkEqual1
. Paga, sin embargo, un costo significativo para esto:
- Hasta 20x en listas casi idénticas de rendimiento
- Hasta 2.5 veces en el rendimiento en listas cortas
Si las entradas largas con elementos desiguales tempranos no ocurren (o ocurren lo suficientemente raramente), no se requiere cortocircuito. Entonces, con mucho, el más rápido es la solución de @Ivo van der Wijk.
Aquí hay dos formas simples de hacer esto
usando set ()
Al convertir la lista en un conjunto, se eliminan los elementos duplicados. Entonces, si la longitud del conjunto convertido es 1, esto implica que todos los elementos son iguales.
len(set(input_list))==1
Aquí hay un ejemplo
>>> a = [''not'', ''the'', ''same'']
>>> b = [''same'', ''same'', ''same'']
>>> len(set(a))==1 # == 3
False
>>> len(set(b))==1 # == 1
True
usando todos ()
Esto comparará (equivalencia) el primer elemento de la lista de entrada con cada otro elemento de la lista. Si todos son equivalentes, se devolverá True, de lo contrario se devolverá False.
all(element==input_list[0] for element in input_list)
Aquí hay un ejemplo
>>> 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 Si está verificando si toda la lista es equivalente a un cierto valor, puede sustituir el valor para input_list [0].
Cambia la lista a un conjunto. Entonces, si el tamaño del conjunto es solo 1, deben haber sido el mismo.
if len(set(my_list)) == 1:
Compruebe si todos los elementos son iguales al primero.
np.allclose(array, array[0])
Convierta la lista en el conjunto y luego encuentre el número de elementos en el conjunto. Si el resultado es 1, tiene elementos idénticos y, si no, los elementos de la lista no son idénticos.
list1 = [1,1,1]
len(set(list1))
>1
list1 = [1,2,3]
len(set(list1)
>3
Dudo que este sea el "más pitónico", pero algo como:
>>> 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
Haría el truco.
En cuanto al uso de reduce()
con lambda
. Aquí hay un código de trabajo que personalmente creo que es mucho mejor que algunas de las otras respuestas.
reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))
Devuelve un valor truple donde el primer valor es el booleano si todos los elementos son iguales o no.
Esta es otra opción, más rápida que len(set(x))==1
para listas largas (usa cortocircuito)
def constantList(x):
return x and [x[0]]*len(x) == x
Esta es una forma simple de hacerlo:
result = mylist and all(mylist[0] == elem for elem in mylist)
Esto es un poco más complicado, incurre en sobrecarga de llamada de función, pero la semántica se explica más claramente:
def all_identical(seq):
if not seq:
# empty list is False.
return False
first = seq[0]
return all(first == elem for elem in seq)
La forma más sencilla y elegante es la siguiente:
all(x==myList[0] for x in myList)
(Sí, ¡esto incluso funciona con la lista nula! Esto se debe a que este es uno de los pocos casos en los que Python tiene una semántica perezosa).
Con respecto al rendimiento, esto fallará lo antes posible, por lo que es asintóticamente óptimo.
Lo haría:
not any((x[i] != x[i+1] for i in range(0, len(x)-1)))
como any
deja de buscar el iterable tan pronto como encuentra una condición True
.
Método general:
def checkEqual1(iterator):
iterator = iter(iterator)
try:
first = next(iterator)
except StopIteration:
return True
return all(first == rest for rest in iterator)
Un trazador de líneas:
def checkEqual2(iterator):
return len(set(iterator)) <= 1
También una sola línea:
def checkEqual3(lst):
return lst[1:] == lst[:-1]
La diferencia entre las 3 versiones es que:
- En
checkEqual2
el contenido debe ser hashable. -
checkEqual1
ycheckEqual2
pueden usar cualquier iterador, perocheckEqual3
debe tomar una entrada de secuencia, generalmente contenedores concretos como una lista o tupla. -
checkEqual1
detiene tan pronto como se encuentra una diferencia. - Dado que
checkEqual1
contiene más código Python, es menos eficiente cuando muchos de los elementos son iguales al principio. - Dado que
checkEqual2
ycheckEqual3
siempre realizan operaciones de copia O (N), demorarán más si la mayoría de su entrada devolverá False. - Para
checkEqual2
ycheckEqual3
es más difícil adaptar la comparación dea == b
aa is b
.
resultado de timeit
, para Python 2.7 y (solo s1, s4, s7, s9 deben devolver 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 = []
obtenemos
| 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 |
Nota:
# http://.com/q/3844948/
def checkEqualIvo(lst):
return not lst or lst.count(lst[0]) == len(lst)
# http://.com/q/3844931/
def checkEqual6502(lst):
return not lst or [lst[0]]*len(lst) == lst
Para lo que vale, esto apareció recientemente en la lista de correo de python-ideas . Resulta que ya hay una receta de itertools para hacer esto: 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)
Supuestamente se desempeña muy bien y tiene algunas propiedades agradables.
- Cortocircuitos: dejará de consumir elementos de la iterable tan pronto como encuentre el primer elemento no igual.
- No requiere que los elementos sean hashable.
- Es perezoso y solo requiere O (1) memoria adicional para realizar la comprobación.
1 En otras palabras, no puedo tomar el crédito por encontrar la solución, ni tampoco puedo tomar crédito por encontrarla .
Puede convertir la lista a un conjunto. Un conjunto no puede tener duplicados. Entonces, si todos los elementos en la lista original son idénticos, el conjunto tendrá solo un elemento.
if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
Puede usar mapa y lambda
lst = [1,1,1,1,1,1,1,1,1]
print all(map(lambda x: x == lst[0], lst[1:]))
Si estás interesado en algo un poco más legible (pero, por supuesto, no tan eficiente), puedes probar:
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
También hay una opción recursiva de Python pura:
def checkEqual(lst):
if len(lst)==2 :
return lst[0]==lst[1]
else:
return lst[0]==lst[1] and checkEqual(lst[1:])
Sin embargo, por alguna razón, en algunos casos es dos órdenes de magnitud más lento que otras opciones. Viniendo de la mentalidad del lenguaje C, esperaba que esto fuera más rápido, ¡pero no lo es!
La otra desventaja es que hay un límite de recursión en Python que debe ajustarse en este caso. Por ejemplo usando this .
Tu puedes hacer:
reduce(and_, (x==yourList[0] for x in yourList), True)
Es bastante molesto que Python te haga importar los operadores como operator.and_
. A partir de python3, también deberá importar functools.reduce
.
(No debe usar este método porque no se romperá si encuentra valores no iguales, pero continuará examinando la lista completa. Se incluye aquí como una respuesta completa).
Un conjunto de trabajo de comparación:
len(set(the_list)) == 1
El uso de set
elimina todos los elementos duplicados.
Una solución más rápida que usar set () que funciona en secuencias (no iterables) es simplemente contar el primer elemento. Esto supone que la lista no está vacía (pero es trivial de verificar y decidir cuál será el resultado en una lista vacía)
x.count(x[0]) == len(x)
algunos puntos de referencia simples:
>>> 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
>>> 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"
def allTheSame(i):
j = itertools.groupby(i)
for k in j: break
for k in j: return False
return True
Funciona en Python 2.4, que no tiene "todos".
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]
El siguiente será cortocircuito corto:
all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))