Tengo una lista de cadenas. Quiero asignar un número único a cada cadena (el número exacto no es importante) y crear una lista de la misma longitud usando estos números, en orden. A continuación se muestra mi mejor intento, pero no estoy contento por dos razones:
Se supone que los mismos valores están uno al lado del otro.
Tuve que comenzar la lista con un 0 , de lo contrario, la salida sería incorrecta
Mi código:
names = ['ll', 'll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'HL', 'HL'] numbers = [0] num = 0 for item in range(len(names)): if item == len(names) - 1: break elif names[item] == names[item+1]: numbers.append(num) else: num = num + 1 numbers.append(num) print(numbers)Quiero que el código sea más genérico, para que funcione con una lista desconocida. ¿Algunas ideas?
Sin usar una biblioteca externa (consulte EDIT para una solución de Pandas ), puede hacerlo de la siguiente manera:
d = {ni: indi for indi, ni in enumerate(set(names))} numbers = [d[ni] for ni in names]Breve explicacion:
En la primera línea, asignas un número a cada elemento único en tu lista (almacenado en el diccionario d ; puedes crearlo fácilmente usando un diccionario de comprensión; set devuelve los elementos únicos de names ).
Luego, en la segunda línea, realiza una lista de comprensión y almacena los números reales en la lista de numbers .
Un ejemplo para ilustrar que también funciona bien para listas desordenadas:
# 'll' appears all over the place names = ['ll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'll', 'LL', 'HL', 'HL', 'HL', 'll'] Esa es la salida para los numbers :
[1, 1, 3, 3, 3, 2, 2, 1, 2, 0, 0, 0, 1] Como puede ver, el número 1 asociado con ll aparece en los lugares correctos.
EDITAR
Si tiene Pandas disponibles, también puede usar pandas.factorize (que parece ser bastante eficiente para listas enormes y también funciona bien para listas de tuplas como se explica aquí ):
import pandas as pd pd.factorize(names)luego regresará
(array([(array([0, 0, 1, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0]), array(['ll', 'hl', 'LL', 'HL'], dtype=object))Por lo tanto,
numbers = pd.factorize(names)[0]Si la condición es que los números sean únicos y el número exacto no es importante , puede crear una asignación que relacione cada elemento de la lista con un número único sobre la marcha, asignando valores desde un objeto de recuento:
from itertools import count names = ['ll', 'll', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'll'] d = {} c = count() numbers = [d.setdefault(i, next(c)) for i in names] print(numbers) # [0, 0, 2, 2, 4, 4, 4, 7, 0] Puede eliminar los nombres adicionales utilizando el map en la lista y un objeto de conteo, y configurando la función de mapa como {}.setdefault (vea el comentario de @StefanPochmann):
from itertools import count names = ['ll', 'll', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'll'] numbers = map({}.setdefault, names, count()) # call list() on map for Py3 print(numbers) # [0, 0, 2, 2, 4, 4, 4, 7, 0] Como extra, también puedes usar np.unique , en caso de que ya tengas instalado numpy:
import numpy as np _, numbers = np.unique(names, return_inverse=True) print(numbers) # [3 3 2 2 1 1 1 0 3]Si tiene k valores diferentes, esto los asigna a los números enteros 0 a k-1 en orden de aparición:
>>> names = ['b', 'c', 'd', 'c', 'b', 'a', 'b'] >>> tmp = {} >>> [tmp.setdefault(name, len(tmp)) for name in names] [0, 1, 2, 1, 0, 3, 0]