sábado, 3 de julio de 2021

Estructuras de datos Python

Listas

Probablemente la estructura de datos más fundamental en Python es la lista. Una lista es simplemente un array ordenado. (Es similar a lo que en otros lenguajes podría llamarse un array, pero con algunas funciones adicionales.)

lista_enteros = [1, 2, 3]

lista_heterogenea = ["cadena", 0.1, True]

lista_de_listas = [lista_enteros, lista_heterogenea,[]]

longitud_lista = len(lista_enteros) # igual a 3

suma_lista = sum(lista_enteros) # igual a 6

Estructuras de datos Python
Simetría

Podemos obtener o establecer el enésimo elemento de una lista con corchetes:

x = range(10) # es la lista [0, 1, ..., 9]

cero = x[0] # igual a 0, las listas son  0-indexadas

uno = x[1] # igual a 1

nueve = x[-1] # igual a 9, lee desde el último elemento

ocho = x[-2] # iguala 8,

x[0] = -1 # ahora x es [-1, 1, 2, 3, ..., 9]

También podemos utilizar corchetes para "dividir" listas:

primeros_tres = x[:3] # [-1, 1, 2]

tres_hasta_final = x[3:] # [3, 4, ..., 9]

uno_al_cuatro = x[1:5] # [1, 2, 3, 4]

ultimos_tres = x[-3:] # [7, 8, 9]

sin_primero_y_ultimo = x[1:-1] # [1, 2, ..., 8]

copia_de_x = x[:] # [-1, 1, 2, ..., 9]

Python tiene un operador in para verificar la pertenencia a la lista:

1 in [1, 2, 3] # True

0 in [1, 2, 3] # False

 

Esta verificación implica examinar los elementos de la lista de uno en uno, lo que significa que probablemente no deberíamos usarlo a menos que sepamos que nuestra lista es bastante pequeña. Es fácil concatenar listas:

x = [1, 2, 3]

x.extend([4, 5, 6]) # x es ahora [1,2,3,4,5,6]

Si no deseamos modificar x, podemos utilizar la adición de lista:

x = [1, 2, 3]

y = x + [4, 5, 6] # y es [1, 2, 3, 4, 5, 6]; x no cambia

Con más frecuencia, agregaremos a las listas un elemento a la vez:

x = [1, 2, 3]

x.append(0) # x es ahora[1, 2, 3, 0]

y = x[-1] # igual a  0

z = len(x) # igual a  4

A menudo es conveniente descomprimir las listas si sabemos cuántos elementos contienen:

x, y = [1, 2] # ahora x es 1, y es 2

Aunque obtendremos un ValueError si no tenemos el mismo número de elementos en ambos lados.

Es común usar un guión bajo para un valor que vamos a desechar:

_, y = [1, 2] # ahora y == 2, no nos importa el primer elemento

Tuplas

Las tuplas son primos de las listas. Prácticamente cualquier cosa que podamos hacer con una lista que no implique modificarla, podremos hacerlo en una tupla. Especificamos una tupla usando paréntesis (o nada) en lugar de corchetes:

mi_lista = [1, 2]

mi_tupla = (1, 2)

otra_tupla = 3, 4

mi_lista[1] = 3 # mi lista es ahora [1, 3]

try:

mi_tupla[1] = 3

except TypeError:

print "no se puede modificar una tupla"

Las tuplas son una forma conveniente de devolver múltiples valores de funciones:

def suma_y_multiplica(x, y):

return (x + y),(x * y)

sp = suma_y_multiplica(2, 3) # igual a (5, 6)

s, p = suma_y_multiplica(5, 10) # s es 15, p es 50

Las tuplas (y listas) también se pueden usar para asignaciones múltiples:

x, y = 1, 2 # ahora x es 1, y es 2

x, y = y, x # intercambiamos variables; ahora x es 2, y es 1

Diccionarios

Una estructura de datos fundamental es un diccionario, que asocia pares clave-valor y nos permite recuperar rápidamente el valor correspondiente a una clave determinada:

dic_vacio = {}

dic_edades = {"Antonio" : 34, "Juan" : 67 }

Podemos buscar el valor de una clave utilizando corchetes:

edad_antonio = dic_edades["Antonio"]  # devuelve 34

Pero obtendrermos un KeyError si intentamos recuperar una clave que no existe en el diccionario:

try:

edad_elena = dic_edades["Elena"]

except KeyError:

print "¡no existe la clave Elena!"

Podemos verificar la existencia de una clave usando:

antonio_existe_en_dic = "Antonio" in dic_edades # True

elena_existe_en_dic = "Elena" in dic_edades # False

Los diccionarios tienen un método get que devuelve un valor predeterminado (en lugar de generar una excepción) cuando buscamos una clave que no está en el diccionario:

edad_antonio = dic_edades.get("Antonio", 0) #devuelve 34

edad_elena = dic_edades.get("Elena", 0) # devuelve 0

 

Asignamos pares clave-valor utilizando los mismos corchetes:

dic_edades ["Juan"] = 68 # reemplaza el valor anterior

dic_edades ["Elena"] = 42 # añade un tercer valor

num_persona = len(dic_edades) # devuelve 3

Con frecuencia utilizaremos los diccionarios como una forma sencilla de representar datos estructurados:

dic_post_python = {

"autor" : "José Pedro",

"texto" : "Entrada de diccionarios con python",

"visitas" : 100,

"hashtags" : ["#python", "#diccionarios", "#datos"]

}

Además de buscar claves específicas, podemos mirarlas todas:

claves_dic = dic_post_python.keys() # lista de claves

valores_dic = dic_post_python.values() # lista de valores

pares_dic = dic_post_python.items() # lista de pares (clave,valor)

 

"autor" in dic_post_python # devuelve True

"José Pedro" in valores_dic # devuelve True

Valor = dic_post_python.get("autor")# devuelve José Pedro

#Para saber si existe una clave en el diccionario

existeClave = dic_post_python.has_key("autor")#devuelve True

Las claves del diccionario deben ser inmutables; en particular, no podemos utilizar listas como claves. Si necesitamos una clave de varias partes, debemos utilizar tuplas o encontrar una manera de convertir la clave en una cadena.

defaultdict

Imaginemos que estamos intentando contar las palabras de un documento. Un enfoque obvio es crear un diccionario en el que las claves sean palabras y los valores sean recuentos. Comprobamos cada palabra, y si ya existe  incrementamos su recuento y si no está la añadimos al diccionario:

contador_palabras = {}

for palabra in documento:

if palabra in contador_palabras:

contador_palabras[palabra] += 1

else:

contador_palabras[palabra] = 1

También podemos utilizar un enfoque diferente y manejar la excepción de intentar buscar una clave faltante:

contador_palabras = {}

for palabra in documento:

try:

contador_palabras [palabra] += 1

except KeyError:

contador_palabras[palabra] = 1

Un tercer enfoque es usar get, que se comporta de forma más elegante con las claves faltantes:

contador_palabras = {}

for palabra in documento:

cuenta_previa = contador_palabras.get(palabra, 0)

contador_palabras[palabra] = cuenta_previa + 1

Defaultdict es útil cuando intentamos buscar una clave, que aún no existe en el diccionario, En ese caso,  primero se agrega con un valor por defecto inicial que se le pasa por argumento al crearla. Para utilizar los defaultdict predeterminados, debemos importarlos desde colecciones:

from collections import defaultdict

contador_palabras = defaultdict(int) # int() produce 0

for palabra in documento:

contador_palabras [palabra] += 1

También pueden ser útiles con list o dict o incluso con nuestras propias funciones:

dd_lista = defaultdict(list) # list() produce una lista vacía

dd_lista[2].append(1) # ahora dd_lista contiene {2: [1]}

dd_dicc = defaultdict(dict) # dict() produce un diccionario vacío

dd_dicc["Jose"]["Ciudad"] = "Madrid" # { "Jose" : { "Ciudad" : Madrid"}}

dd_par = defaultdict(lambda: [0, 0])

dd_par[2][1] = 1 # ahora dd_par contiene {2: [0,1]}

Estos serán útiles cuando utilicemos diccionarios para "recopilar" resultados por alguna clave y no queramos tener que comprobar cada vez para ver si la clave existe aún.

Counter

Un contador convierte una secuencia de valores en claves de mapeo de objetos similares a defaultdict (int) para contar. Lo utilizaremos principalmente para crear histogramas:

from collections import Counter

c = Counter([0, 1, 2, 0]) # c es (basicamente) { 0 : 2, 1 : 1, 2 : 1 }

Esto nos da una forma muy sencilla de resolver nuestro problema de recuentos de palabras:

contador_palabras = Counter(documento)

Una instancia de Counter tiene un método most_common que suele ser útil:

 # imprime las 10 palabras más communes y su conteo

for palabra, conteo in contador_palabras.mas_comun(10):

print palabra, conteo

 

Sets

Se establece otra estructura de datos, que representa una colección de elementos distintos:

s = set()

s.add(1) # s es ahora { 1 }

s.add(2) # s es ahora { 1, 2 }

s.add(2) # s es todavía { 1, 2 }

x = len(s) # igual a 2

y = 2 in s # es True

z = 3 in s # es False

Utilizaremos sets por dos razones principales. La primera es que los sets son muy muy rápidos. Si tenemos una gran cantidad de elementos que queremos utilizar para una prueba, un set es más apropiado que una lista:

lista_palabras = ["a","abaco","abad" … "zurrón","zutano"]

"alguno" in lista_palabras # False, pero chequea cada elemento de la lista

set_palabras = set(lista_palabras)

"alguno" in set_palabras # chequea mucho más rápido

La segunda razón es encontrar los elementos distintos en una colección:

lista_objetos = [1, 2, 3, 1, 2, 3]

num_objetos = len(lista_objetos) # 6

set_objetos = set(lista_objetos) # {1, 2, 3}

num_objetos_distintos = len(set_objetos) # 3

lista_objetos_distintos = list(set_objetos) # [1, 2, 3]

Utilizaremos sets con mucha menos frecuencia que los diccionarios y las listas.


No hay comentarios:

Publicar un comentario