¿Hay una función incorporada que elimina los duplicados de la lista en Python, al tiempo que conserva el orden? Sé que puedo usar un conjunto para eliminar duplicados, pero eso destruye el orden original. También sé que puedo rodar el mío así:
def uniq(input):
output = []
for x in input:
if x not in output:
output.append(x)
return output
(Gracias por relajarse para ese ejemplo de código ).
Pero me gustaría aprovechar un modismo incorporado o un lenguaje más pitón si es posible.
Pregunta relacionada: en Python, ¿cuál es el algoritmo más rápido para eliminar duplicados de una lista para que todos los elementos sean únicos y se mantenga el orden ?
fuente
seen.add
podría haber cambiado entre iteraciones, y el tiempo de ejecución no es lo suficientemente inteligente como para descartarlo. Para jugar con seguridad, tiene que verificar el objeto cada vez. - Si observa el bytecode condis.dis(f)
, puede ver que se ejecutaLOAD_ATTR
para eladd
miembro en cada iteración. ideone.com/tz1Tllseen_add
es una mejora, pero los tiempos pueden verse afectados por los recursos del sistema en ese momento.seen_add = seen.add
rendimientos solo aumentan un 1% la velocidad. Apenas es significativo.Editar 2016
Como señaló Raymond , en Python 3.5+ donde
OrderedDict
se implementa en C, el enfoque de comprensión de la lista será más lento queOrderedDict
(a menos que realmente necesite la lista al final, e incluso entonces, solo si la entrada es muy corta). Entonces, la mejor solución para 3.5+ esOrderedDict
.Edición importante 2015
Como señala @abarnert , la
more_itertools
biblioteca (pip install more_itertools
) contiene unaunique_everseen
función que está diseñada para resolver este problema sin mutaciones ilegibles (not seen.add
) en las comprensiones de listas. Esta también es la solución más rápida:Solo una importación de biblioteca simple y sin hacks. Esto proviene de una implementación de la receta de itertools
unique_everseen
que se ve así:En Python,
2.7+
elidioma común aceptado(que funciona pero no está optimizado para la velocidad, ahora usaríaunique_everseen
) para estos usoscollections.OrderedDict
:Tiempo de ejecución: O (N)
Esto se ve mucho mejor que:
y no utiliza el truco feo :
que se basa en el hecho de que
set.add
es un método in situ que siempre devuelveNone
por lo que senot None
evalúaTrue
.Sin embargo, tenga en cuenta que la solución de pirateo es más rápida en velocidad bruta, aunque tiene la misma complejidad de tiempo de ejecución O (N).
fuente
[seen.add(x) for x in seq if x not in seen]
, o si no le gustan los efectos secundarios de la comprensión, simplemente use unfor
bucle:for x in seq: seen.add(x) if x not in seen else None
(sigue siendo una línea, aunque en este caso creo que una línea es una propiedad tonta para tratar de tener en un solución.seen = set(seq)
.En Python 2.7 , la nueva forma de eliminar duplicados de un iterable mientras se mantiene en el orden original es:
En Python 3.5 , el OrderedDict tiene una implementación en C. Mis tiempos muestran que este es ahora el más rápido y el más corto de los diversos enfoques para Python 3.5.
En Python 3.6 , el dict regular se hizo ordenado y compacto. (Esta característica es válida para CPython y PyPy pero puede no estar presente en otras implementaciones). Eso nos da una nueva forma más rápida de deducir mientras se conserva el orden:
En Python 3.7 , el dict regular está garantizado para ambos ordenados en todas las implementaciones. Entonces, la solución más corta y rápida es:
Respuesta a @max: una vez que te mueves a 3.6 o 3.7 y usas el dict regular en lugar de OrderedDict , no puedes superar el rendimiento de ninguna otra manera. El diccionario es denso y se convierte fácilmente en una lista casi sin sobrecarga. La lista de destino está dimensionada previamente para len (d), lo que guarda todos los cambios de tamaño que se producen en una comprensión de la lista. Además, dado que la lista de claves internas es densa, copiar los punteros es casi tan rápido como una copia de la lista.
fuente
OrderedDict
en una lista al final. Si necesito convertirlo en una lista, para entradas pequeñas, el enfoque de comprensión de la lista es aún más rápido hasta 1,5 veces. Dicho esto, esta solución es mucho más limpia.set()
ayudaría a los usuarios más ingenuos a desarrollar códigos reproducibles.único →
['1', '2', '3', '6', '4', '5']
fuente
n^2
None
referencias en el proceso!)for
bucle en su lugarNo patear a un caballo muerto (esta pregunta es muy antigua y ya tiene muchas buenas respuestas), pero aquí hay una solución con pandas que es bastante rápida en muchas circunstancias y es muy fácil de usar.
fuente
La lista ni siquiera tiene que ser ordenada , la condición suficiente es que los valores iguales se agrupen.
Editar: Supuse que "preservar el orden" implica que la lista está realmente ordenada. Si este no es el caso, entonces la solución de MizardX es la correcta.
Edición comunitaria: sin embargo, esta es la forma más elegante de "comprimir elementos consecutivos duplicados en un solo elemento".
fuente
Creo que si quieres mantener el orden,
puedes probar esto:
O de manera similar, puedes hacer esto:
También puedes hacer esto:
También se puede escribir así:
fuente
En Python 3.7 y superior, se garantiza que los diccionarios recordarán su orden de inserción de claves. La respuesta a esto pregunta resume el estado actual de las cosas.
La
OrderedDict
solución se vuelve obsoleta y sin ninguna declaración de importación simplemente podemos emitir:fuente
Para otra respuesta muy tardía a otra pregunta muy antigua:
Las
itertools
recetas tienen una función que hace esto, usando laseen
técnica de configuración, pero:key
función estándar .seen.add
lugar de buscarlo N veces. (f7
también hace esto, pero algunas versiones no lo hacen)ifilterfalse
, por lo que solo tiene que recorrer los elementos únicos en Python, en lugar de todos ellos. (Todavía iteras sobre todos dentroifilterfalse
, por supuesto, pero eso está en C, y mucho más rápido).¿Es realmente más rápido que
f7
? Depende de sus datos, por lo que tendrá que probarlos y verlos. Si desea una lista al final,f7
use una lista de compilación, y no hay forma de hacerlo aquí. (Puede directamente enappend
lugar deyield
ing, o puede alimentar el generador en lalist
función, pero ninguno puede ser tan rápido como el LIST_APPEND dentro de un listcomp.) En cualquier caso, por lo general, exprimir algunos microsegundos no será tan rápido importante como tener una función fácil de entender, reutilizable y ya escrita que no requiere DSU cuando se desea decorar.Como con todas las recetas, también está disponible en
more-iterools
.Si solo quiere el no-
key
case, puede simplificarlo como:fuente
more-itertools
esta es claramente la mejor respuesta. Un simplefrom more_itertools import unique_everseen
list(unique_everseen(items))
Un enfoque mucho más rápido que el mío y mucho mejor que la respuesta aceptada, creo que la descarga de la biblioteca vale la pena. Voy a la comunidad wiki mi respuesta ySólo para añadir otra aplicación (de buen calidad) de funcionalidad, una de un módulo externo 1 :
iteration_utilities.unique_everseen
:Tiempos
Hice algunos tiempos (Python 3.6) y estos muestran que es más rápido que todas las otras alternativas que probé, incluyendo
OrderedDict.fromkeys
,f7
ymore_itertools.unique_everseen
:Y solo para asegurarme de que también hice una prueba con más duplicados solo para verificar si hay alguna diferencia:
Y uno que contiene solo un valor:
En todos estos casos, la
iteration_utilities.unique_everseen
función es la más rápida (en mi computadora).Esta
iteration_utilities.unique_everseen
función también puede manejar valores no compartibles en la entrada (sin embargo, con unO(n*n)
rendimiento en lugar delO(n)
rendimiento cuando los valores son hashables).1 Descargo de responsabilidad: soy el autor de ese paquete.
fuente
seen_add = seen.add
- ¿Es esto necesario para los puntos de referencia?dict.fromkeys()
método a su gráfico por favor?ordereddict.fromkeys
?Para tipos no hashaable (por ejemplo, lista de listas), basado en MizardX:
fuente
Tomando prestada la idea recursiva utilizada para definir la
nub
función de Haskell para listas, este sería un enfoque recursivo:p.ej:
Lo probé para aumentar el tamaño de los datos y vi una complejidad de tiempo sub-lineal (no definitiva, pero sugiere que esto debería estar bien para los datos normales).
También creo que es interesante que otras operaciones puedan generalizar fácilmente a la unicidad. Me gusta esto:
Por ejemplo, podría pasar una función que usa la noción de redondeo al mismo número entero como si fuera "igualdad" para propósitos de unicidad, como este:
entonces unique (some_list, test_round) proporcionaría los elementos únicos de la lista donde la unicidad ya no significaba igualdad tradicional (lo que está implícito en el uso de cualquier tipo de enfoque basado en conjuntos o en dict-key para este problema) sino que en su lugar tenía la intención de tomar solo el primer elemento que se redondea a K para cada posible entero K al que los elementos podrían redondear, por ejemplo:
fuente
filter
apenas se beneficiará de la llamada anterior. Pero si el número de elementos únicos es pequeño en relación con el tamaño de la matriz, esto debería funcionar bastante bien.Variante de reducción 5 veces más rápida pero más sofisticada
Explicación:
fuente
Puede hacer referencia a una comprensión de la lista, ya que está siendo construida por el símbolo '_ [1]'.
Por ejemplo, la siguiente función unifica una lista de elementos sin cambiar su orden haciendo referencia a su comprensión de la lista.
Manifestación:
Salida:
fuente
La respuesta de MizardX ofrece una buena colección de múltiples enfoques.
Esto es lo que se me ocurrió mientras pensaba en voz alta:
fuente
O(n)
operación y la realiza en cada elemento, la complejidad resultante de su solución seríaO(n^2)
. Esto es simplemente inaceptable para un problema tan trivial.Aquí hay una manera simple de hacerlo:
eso da la salida:
fuente
Podrías hacer una especie de truco de comprensión de listas feo.
fuente
i,e in enumerate(l)
al[i] for i in range(len(l))
.Enfoque relativamente eficaz con
_sorted_
unasnumpy
matrices:Salidas:
fuente
Una expresión generadora que usa la búsqueda O (1) de un conjunto para determinar si se incluye o no un elemento en la nueva lista.
fuente
extend
con una expresión generadora que depende de la cosa que se está extendiendo (por lo tanto, +1), peroset(n)
se recalcula en cada etapa (que es lineal) y esto hace que el enfoque general sea cuadrático. De hecho, esto es casi peor que simplemente usarloele in n
. Hacer un set para una prueba de membresía no vale la pena el gasto de la creación del set Aún así, es un enfoque interesante.Una solución recursiva simple:
fuente
Elimina los valores duplicados en una secuencia, pero conserva el orden de los elementos restantes. Uso de la función de generador de propósito general.
fuente
Los usuarios de pandas deben consultar
pandas.unique
.La función devuelve una matriz NumPy. Si es necesario, puede convertirlo en una lista con el
tolist
métodofuente
Si necesita un revestimiento, entonces esto podría ayudar:
... debería funcionar pero corrígeme si me equivoco
fuente
Si usa habitualmente
pandas
, y se prefiere la estética sobre el rendimiento, considere la función incorporadapandas.Series.drop_duplicates
:Sincronización:
fuente
esto preservará el orden y se ejecutará en O (n) tiempo. Básicamente, la idea es crear un agujero donde se encuentre un duplicado y hundirlo hasta el fondo. hace uso de un puntero de lectura y escritura. cada vez que se encuentra un duplicado, solo el puntero de lectura avanza y el puntero de escritura permanece en la entrada duplicada para sobrescribirlo.
fuente
Una solución sin usar módulos o conjuntos importados:
Da salida:
fuente
Un método en el lugar
Este método es cuadrático, porque tenemos una búsqueda lineal en la lista para cada elemento de la lista (a eso tenemos que agregar el costo de reorganizar la lista debido a la
del
s).Dicho esto, es posible operar en el lugar si comenzamos desde el final de la lista y procedemos hacia el origen eliminando cada término que está presente en la sublista a su izquierda
Esta idea en código es simplemente
Una prueba simple de la implementación
fuente
l[:] = <one of the the faster methods>
si quisieras una operación in situ, ¿no?a=[1]; b=a; a[:]=[2]
, elb==[2]
valor esTrue
y podemos decir que lo estamos haciendo en el lugar, sin embargo, lo que propone es utilizar un nuevo espacio para tener una nueva lista, reemplazar los datos antiguos con los nuevos y marcar el datos antiguos para la recolección de basura porque ya no se hace referencia a nada, por lo que decir que está funcionando en el lugar es un poco estirar un poco el concepto de lo que he demostrado que es posible ... ¿es ineficiente? Sí, pero ya lo dije de antemano.El enfoque de zmk utiliza la comprensión de listas que es muy rápida, pero mantiene el orden de forma natural. Para aplicar a cadenas sensibles a mayúsculas y minúsculas, se puede modificar fácilmente. Esto también conserva el caso original.
Las funciones estrechamente asociadas son:
fuente
Una lista de comprensión de la lista:
Simplemente agregue un condicional para verificar que el valor no esté en una posición anterior
fuente