Python cuenta con múltiples estructuras de datos nativas del lenguaje (listas, diccionarios, tuplas y conjuntos) que son fundamentales para los desarrolladores. Aunque, para ciertas tareas complejas, pueden no ser suficiente. En estos casos, se puede recurrir al módulo collections
. Este módulo proporciona estructuras de datos avanzadas que se puede usar para simplificar el procesado de datos en Python. En esta entrada, se analizarán cinco de estas estructuras: Counter
, defaultdict
, OrderedDict
, namedtuple
y deque
.
Las colecciones de collections
En esta entrada se verá cómo usar las siguientes colecciones del módulo collections
:
-
Counter
es una subclase de diccionario especializada en contar elementos hashables. Es útil para llevar un registro de la frecuencia de los elementos en una colección, cómo contar caracteres en una cadena o palabras en una lista. -
defaultdict
es una subclase de diccionario que proporciona un valor por defecto para claves inexistentes. Eliminando la necesidad de verificar la existencia de una clave antes de modificar su valor, lo que simplifica el código y mejora la legibilidad. -
OrderedDict
es una subclase de diccionario que mantiene el orden de inserción de los elementos. Dado que los diccionarios son elementos "desordenados", es necesario recurrir a esta colección cuando se necesita garantizar el orden en estos objetos. Algo crucial cuando el orden de los datos afecta el resultado, como en el procesamiento de secuencias o la generación de informes. -
namedtuple
es una función para crear tuplas con nombre, es decir, tuplas cuyos elementos pueden ser accedidos mediante nombres en lugar de índices. Esto mejora la legibilidad del código y facilita la gestión de datos estructurados. -
deque
(double-ended queue) es una estructura de datos que permite añadir y quitar elementos por ambos extremos de manera eficiente. Es ideal para implementar colas y pilas, donde las operaciones de inserción y eliminación son frecuentes en ambos extremos.
Una vez visto qué hace cada una de las colecciones, se puede comprender mejor su funcionamiento a través de diferentes ejemplos prácticos.
Counter
es una herramienta del módulo collections
que se utiliza para contar elementos hashables de manera eficiente. Siendo especialmente útil cuando se necesita llevar un registro de la frecuencia de elementos en una secuencia. Lo que se puede ver en el siguiente código:
from collections import Counter # Contar caracteres en una cadena cadena = "abracadabra" contador = Counter(cadena) print(contador) # Contar palabras en una lista palabras = ["apple", "banana", "apple", "orange", "banana", "banana"] contador_palabras = Counter(palabras) print(contador_palabras) # Métodos útiles de Counter print(contador.most_common(2)) # Los dos elementos más comunes print(list(contador.elements())) # Lista de elementos expandida
Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
Counter({'banana': 3, 'apple': 2, 'orange': 1})
[('a', 5), ('b', 2)]
['a', 'a', 'a', 'a', 'a', 'b', 'b', 'r', 'r', 'c', 'd']
En este código, Counter
no solo cuenta elementos, sino que también proporciona métodos útiles como most_common()
, que devuelve una lista de los elementos más comunes, y elements()
, que devuelve un iterador sobre los elementos. Estos métodos permiten analizar y manipular los datos contados de manera eficiente.
2. defaultdict
defaultdict
es una subclase de diccionario que devuelve un valor por defecto si la clave no existe. Este valor por defecto se define cuando se crea el defaultdict
y puede ser cualquier tipo de objeto o función que se pueda llamar. Lo que se puede ver en el siguiente código:
from collections import defaultdict # Definir un defaultdict con valor por defecto como lista default_dict = defaultdict(list) # Añadir elementos a listas dentro del defaultdict default_dict["fruits"].append("apple") default_dict["fruits"].append("banana") default_dict["vegetables"].append("carrot") print(default_dict) # Definir un defaultdict con valor por defecto como entero count_dict = defaultdict(int) items = ["apple", "banana", "apple", "orange"] for item in items: count_dict[item] += 1 print(count_dict)
defaultdict(<class 'list'>, {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']})
defaultdict(<class 'int'>, {'apple': 2, 'banana': 1, 'orange': 1})
En este código, defaultdict
simplifica el código al eliminar la necesidad de inicializar manualmente los valores de las claves. En el primer ejemplo, cada nueva clave tiene como valor por defecto una lista vacía, permitiendo agregar elementos directamente. En el segundo ejemplo, cada nueva clave tiene un valor por defecto de 0, facilitando el conteo de elementos.
3. OrderedDict
OrderedDict
es una subclase de diccionario que mantiene el orden de inserción de los elementos. A diferencia de los diccionarios normales (en versiones de Python anteriores a 3.7), OrderedDict
recuerda el orden en que se insertaron los elementos, lo cual es útil en muchas aplicaciones donde el orden de los datos es importante. Lo que se puede ver en el siguiente código:
from collections import OrderedDict # Crear un OrderedDict ordered_dict = OrderedDict() ordered_dict["apple"] = 3 ordered_dict["banana"] = 2 ordered_dict["orange"] = 1 print(ordered_dict) # Iterar sobre un OrderedDict for key, value in ordered_dict.items(): print(key, value) # Mover un elemento al final o al principio ordered_dict.move_to_end("banana") print(ordered_dict) ordered_dict.move_to_end("banana", last=False) print(ordered_dict)
OrderedDict([('apple', 3), ('banana', 2), ('orange', 1)])
apple 3
banana 2
orange 1
OrderedDict([('apple', 3), ('orange', 1), ('banana', 2)])
OrderedDict([('banana', 2), ('apple', 3), ('orange', 1)])
En este ejemplo, OrderedDict
se usa para preservar el orden de inserción de los elementos. Esto puede ser crucial en aplicaciones donde el orden de los datos afecta el resultado, como en el procesamiento de secuencias o en la generación de informes. Además, métodos como move_to_end()
permiten reordenar elementos de manera eficiente.
4. namedtuple
namedtuple
es una función del módulo collections
que permite crear tuplas con nombre. Esto significa que los elementos de la tupla pueden ser accedidos mediante nombres en lugar de índices, mejorando la legibilidad y manejabilidad del código. Lo que se puede ver en el siguiente código:
from collections import namedtuple # Definir un namedtuple Persona = namedtuple("Persona", ["nombre", "edad", "ocupacion"]) # Crear una instancia de namedtuple persona1 = Persona("Juan", 28, "Ingeniero") print(persona1) # Acceder a los elementos por nombre print(persona1.nombre) print(persona1.edad) print(persona1.ocupacion) # Convertir namedtuple a diccionario print(persona1._asdict())
Persona(nombre='Juan', edad=28, ocupacion='Ingeniero')
Juan
28
Ingeniero
{'nombre': 'Juan', 'edad': 28, 'ocupacion': 'Ingeniero'}
Como se puede ver en el código anterior, namedtuple
mejora la legibilidad al permitir el acceso a los elementos por nombre en lugar de por índice. Esto es particularmente útil en aplicaciones que manejan datos estructurados, como registros de base de datos o respuestas de API. Además, namedtuple
incluye métodos adicionales como _asdict()
, que convierte la tupla en un diccionario, facilitando su manipulación.
deque
( double-ended queue) es una estructura de datos que permite añadir y quitar elementos por ambos extremos de manera eficiente. A diferencia de las listas, donde las operaciones de inserción y eliminación en ambos extremos pueden ser costosas en términos de tiempo, deque
está optimizado para estas operaciones. El uso de esta componente se puede ver en el siguiente ejemplo:
from collections import deque # Crear una deque dq = deque() # Añadir elementos al final y al principio dq.append("a") dq.append("b") dq.appendleft("c") print(dq) # Quitar elementos del final y del principio dq.pop() dq.popleft() print(dq) # Rotar elementos dq.extend(["d", "e", "f"]) print(dq) dq.rotate(1) # Rotar a la derecha print(dq) dq.rotate(-1) # Rotar a la izquierda print(dq)
deque(['c', 'a', 'b'])
deque(['a'])
deque(['a', 'd', 'e', 'f'])
deque(['f', 'a', 'd', 'e'])
deque(['a', 'd', 'e', 'f'])
Como se puede ver en el ejemplo, deque
es extremadamente eficiente para operaciones que añaden o quitan elementos de ambos extremos. Métodos como append()
, appendleft()
, pop()
y popleft()
permiten manipular la estructura de datos fácilmente. Además, deque
proporciona operaciones avanzadas como rotate()
, que permite rotar los elementos, facilitando la implementación de algoritmos más complejos.
Conclusiones
El módulo collections
de Python es un módulo que se debe conocer cualquier desarrollador. Al proporcionar estructuras de datos avanzadas que simplifican la gestión y manipulación de datos, se puede conseguir un código mas eficiencia y legible. Ya sea contando elementos con Counter
, manejando valores por defecto con defaultdict
, preservando el orden de inserción con OrderedDict
, creando estructuras de datos legibles con namedtuple
o manipulando secuencias de manera eficiente con deque
, collections
ofrece soluciones elegantes, fáciles de implementar y eficaces para resolver muchos problemas comunes.