List
y Dict
Comprehensions#
Es difícil explicar en abstracto que son las List
y Dict
comprehensions. Por ahora sólo vamos a decir que son una manera muy potente y rápida de generar List
y Dict
a partir de otros List
y Dict
. Veamos un par de ejemplos.
List
Comprehensions#
Construir una List
a partir de otra List
u otra estrucutura de datos.
Ejemplo: Transformar los Elementos de una List
#
Supongamos que tenemos una lista de RUTs. Como es típico, los RUTs vienen con formatos inconsistentes, supongamos que pueden venir con o sin separador de miles y con o sin guión antes del dígito verificador. Por ejemplo:
12.345.678-9
21543879-9
214537689
Obviamente, antes de utilizar esta lista, queremos homologar los formatos. Para homologar un RUT al formato sin separador de miles y con guión, escribimos la siguiente función:
def estandariza_rut(rut):
"""
Estandariza un RUT al siguiente formato XXXXXXXX-DV.
Parameters
----------
rut: str o int
Representa un RUT, puede venir con o sin separador de miles, con o sin guión antes del
dígito verificador y podría ser un `int` o un `str`.
Returns
-------
El RUT en el formato estandarizado como un `str`.
"""
# Antes de comenzar la transformación nos aseguramos que el parámetro rut sea un str.
temp = str(rut)
# Se eliminan eventuales separadores de miles.
temp = temp.replace(".", "")
temp = temp.replace(",", "")
# Se elimina eventual dígito verificador.
temp = temp.replace("-", "")
# Se agrega el dígito verificador y se retorna.
return f'{temp[:-1]}-{temp[-1]}' # slicing
Probemos la función:
ruts = ['12.345.678-9', '21543879-9', 214537689]
for rut in ruts:
print(estandariza_rut(rut))
12345678-9
21543879-9
21453768-9
ruts_ok = []
for rut in ruts:
ruts_ok.append(estandariza_rut(rut))
ruts_ok
['12345678-9', '21543879-9', '21453768-9']
Aplicamos ahora un List
comprehension para transformar la List
ruts
en una List
con RUTs estandarizados.
ruts_ok = [estandariza_rut(rut) for rut in ruts]
ruts_ok
['12345678-9', '21543879-9', '21453768-9']
La mejor manera de pensar y entender esta sintaxis es recordando la notación matemática (del colegio nada complicado) para denotar o definir un conjunto. En este caso el conjunto \(Y\) formado por todos los valores transformados por la función \(f\) de los elementos del conjunto \(X\).
Ejercicio: Capitalizar una Lista de Nombres#
Considerar esta List
de nombres: nombres = ['maría', 'Rosa', 'josé', 'horacio', 'Anacleta']
.
Transformar nombres
en: ['María', 'Rosa', 'José', 'Horacio', 'Anacleta']
.
Tip: ir a Google y buscar capitalize string in python.
Solución:
Show code cell content
# Usando List comprehension. Más elegante y más rápido.
nombres = ['maría', 'Rosa', 'josé', 'horacio', 'Anacleta']
resultado = [x.capitalize() for x in nombres]
print(resultado)
# Forma fea
resultado1 = []
for x in nombres:
resultado1.append(x.capitalize())
print(resultado1)
['María', 'Rosa', 'José', 'Horacio', 'Anacleta']
['María', 'Rosa', 'José', 'Horacio', 'Anacleta']
Ejemplo: Filtrar los Elementos de una List
#
Tenemos ahora una List
de Tuple
donde cada Tuple
tiene el nombre de un producto comestible y un bool
que indica si el producto tiene o no sellos (si es True
entonces tiene sellos).
productos = [
('Super8', True),
('Apio', False),
('Zucaritas', True),
('Té verde', False)
]
Vamos a filtrar los productos sin sellos y almacenarlos en una nueva List
.
productos_con_sellos = [p for p in productos if p[1]]
La expresión if p[1]
es lo mismo que escribir if p[1] == True
, pero es más elegante y conciso. Veamos qué obtuvimos.
productos_con_sellos
[('Super8', True), ('Zucaritas', True)]
También usando la notación matemática para conjuntos, esta sintaxis se puede pensar como:
Aquí, \(\land\) es el símbolo matemático para la condición lógica and
.
Ejercicio: Filtrar Números#
Considerando la siguiente List
rand_nums
de números enteros generados aleatoriamente usando una List
comprehension:
filtrar todos los elementos superiores a 50
generar la
List
con las raíces cuadradas de los elementos derand_nums
.
import random as rnd
# En esta librería está la función sqrt para calcular raíces cuadradas
import math
rand_nums = [rnd.randint(1, 101) for i in range(100)] # Es primera vez que usamos range
Solución:
Show code cell content
gt_50 = [number for number in rand_nums if number > 50]
sqr = [math.sqrt(number) for number in rand_nums]
print(rand_nums)
print()
print(gt_50)
print()
print(sqr)
[96, 33, 51, 15, 27, 24, 97, 53, 48, 10, 15, 93, 72, 36, 3, 17, 78, 1, 29, 21, 17, 20, 89, 49, 42, 96, 28, 38, 95, 63, 39, 46, 28, 14, 75, 50, 57, 37, 51, 57, 6, 41, 96, 11, 42, 94, 79, 64, 65, 61, 24, 30, 71, 69, 68, 11, 38, 94, 48, 91, 50, 36, 98, 69, 89, 69, 24, 42, 48, 36, 11, 20, 36, 100, 54, 79, 76, 90, 90, 57, 22, 90, 6, 42, 1, 97, 78, 14, 1, 47, 70, 4, 93, 74, 52, 70, 6, 2, 88, 85]
[96, 51, 97, 53, 93, 72, 78, 89, 96, 95, 63, 75, 57, 51, 57, 96, 94, 79, 64, 65, 61, 71, 69, 68, 94, 91, 98, 69, 89, 69, 100, 54, 79, 76, 90, 90, 57, 90, 97, 78, 70, 93, 74, 52, 70, 88, 85]
[9.797958971132712, 5.744562646538029, 7.14142842854285, 3.872983346207417, 5.196152422706632, 4.898979485566356, 9.848857801796104, 7.280109889280518, 6.928203230275509, 3.1622776601683795, 3.872983346207417, 9.643650760992955, 8.48528137423857, 6.0, 1.7320508075688772, 4.123105625617661, 8.831760866327848, 1.0, 5.385164807134504, 4.58257569495584, 4.123105625617661, 4.47213595499958, 9.433981132056603, 7.0, 6.48074069840786, 9.797958971132712, 5.291502622129181, 6.164414002968976, 9.746794344808963, 7.937253933193772, 6.244997998398398, 6.782329983125268, 5.291502622129181, 3.7416573867739413, 8.660254037844387, 7.0710678118654755, 7.54983443527075, 6.082762530298219, 7.14142842854285, 7.54983443527075, 2.449489742783178, 6.4031242374328485, 9.797958971132712, 3.3166247903554, 6.48074069840786, 9.695359714832659, 8.888194417315589, 8.0, 8.06225774829855, 7.810249675906654, 4.898979485566356, 5.477225575051661, 8.426149773176359, 8.306623862918075, 8.246211251235321, 3.3166247903554, 6.164414002968976, 9.695359714832659, 6.928203230275509, 9.539392014169456, 7.0710678118654755, 6.0, 9.899494936611665, 8.306623862918075, 9.433981132056603, 8.306623862918075, 4.898979485566356, 6.48074069840786, 6.928203230275509, 6.0, 3.3166247903554, 4.47213595499958, 6.0, 10.0, 7.3484692283495345, 8.888194417315589, 8.717797887081348, 9.486832980505138, 9.486832980505138, 7.54983443527075, 4.69041575982343, 9.486832980505138, 2.449489742783178, 6.48074069840786, 1.0, 9.848857801796104, 8.831760866327848, 3.7416573867739413, 1.0, 6.855654600401044, 8.366600265340756, 2.0, 9.643650760992955, 8.602325267042627, 7.211102550927978, 8.366600265340756, 2.449489742783178, 1.4142135623730951, 9.38083151964686, 9.219544457292887]
Ejemplos de range
#
Números tales que: \(0 \leq n \lt 5\)
for i in range(5):
print(i)
0
1
2
3
4
Números tales que: \(-1\leq n \lt 20\) contando de 2 en 2.
for i in range(-1, 20, 2):
print(i)
-1
1
3
5
7
9
11
13
15
17
19
Documentación de range
print(range.__doc__) # dunder doc
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
datos = ['a', 'b', 'c', 'd', 'e']
for i in range(len(datos)):
print(datos[i])
a
b
c
d
e
Aunque la manera pythonica de hacer lo anterior es:
for d in datos:
print(d)
a
b
c
d
e
Dict
Comprehensions#
Construir un Dict
a partir de otro Dict
, una List
u otra estrucutura de datos.
Reorganizar una List
#
Consideremos la siguiente List
de Tuples
. Cada Tuple
contiene el nombre, edad (años), peso (kilos) y estatura (cm.) de un paciente. Data con esta estructura es la que usualmente se obtiene de la consulta a una base de datos. Sin embargo, si queremos rápidamente acceder a las cifras de un paciente en particular, tener la data almacenada de esta forma, no es lo más conveniente. Si vamos a buscar por nombre, lo más conveniente es usar un Dict
cuyos keys
sea el nombre del paciente y cuyos values
sea la data del paciente.
data = [
('Pedro', 25, 70, 170),
('Juan', 43, 67, 165),
('Diego', 18, 90, 180),
('María', 50, 55, 160),
]
data[0][1:]
(25, 70, 170)
data_dict = {d[0]: d[1:] for d in data}
data_dict
{'Pedro': (25, 70, 170),
'Juan': (43, 67, 165),
'Diego': (18, 90, 180),
'María': (50, 55, 160)}
Ahora, si queremos acceder a los datos de María sólo tenemos que:
data_dict['María']
(50, 55, 160)
Asignar Nombres a los Datos Numéricos#
La estructura anterior es sin duda una mejora. Sin embargo, podríamos confundirnos entre la edad y el peso de un paciente. Por ejemplo, María tiene 50 años y pesa 55 kilos. Para que no exista esa confusión, también la data se almacenará en un Dict
.
data_dict_2 = {d[0]: {'edad': d[1], 'peso': d[2], 'estatura': d[3]} for d in data}
data_dict_2
{'Pedro': {'edad': 25, 'peso': 70, 'estatura': 170},
'Juan': {'edad': 43, 'peso': 67, 'estatura': 165},
'Diego': {'edad': 18, 'peso': 90, 'estatura': 180},
'María': {'edad': 50, 'peso': 55, 'estatura': 160}}
Ahora, si queremos la edad de María hacemos:
data_dict_2['María']['edad']
50
Y su peso …
data_dict_2['María']['peso']
55