Antes de empezar
Varios ejercicios usan funciones y variables de la teoría: promedio, limpiar, contar, la_cuenta, leer_columna, palabras, conteo, la clase Contador y el archivo notas.csv. Abre un cuaderno nuevo en Colab, pega y ejecuta en orden los bloques de la ficha Código y después trabaja debajo. Intenta cada ejercicio antes de abrir la respuesta: equivocarse primero y corregir después es como se aprende de verdad.
Ejercicios
1. Predecir antes de ejecutar
Sin ejecutarlo, escribe en un papel qué imprime este programa. Después compruébalo.
def area(base, altura=2):
return base * altura
def mostrar_area(base):
print("El área es", area(base))
x = area(3)
y = area(3, 5)
z = mostrar_area(4)
print(x, y, z)
Respuesta
El área es 8
6 15 None
area(3)usa la altura por defecto, 2: da 6.area(3, 5)da 15.mostrar_area(4)imprime «El área es 8» en el momento en que se llama, por eso esa línea sale primero. Como no tienereturn, devuelveNone, y eso es lo que queda guardado enz.
2. Una función para normalizar texto
Escribe una función normalizar(texto) que devuelva el texto en minúsculas, sin tildes (á → a, é → e, í → i, ó → o, ú → u; deja la ñ) y con un solo espacio entre palabras, sin espacios al principio ni al final. Pruébala con " ¡Qué RICO el Pan! ", que tiene que dar "¡que rico el pan!".
Pista: un diccionario sirve para decir qué letra cambia por cuál, y " ".join(lista) une una lista de palabras con un espacio entre cada una.
Respuesta
def normalizar(texto):
"""Minúsculas, sin tildes y con un solo espacio entre palabras."""
sin_tilde = {"á": "a", "é": "e", "í": "i", "ó": "o", "ú": "u"}
resultado = ""
for c in texto.lower():
resultado += sin_tilde.get(c, c)
# split() sin argumentos corta en cualquier cantidad de espacios
return " ".join(resultado.split())
print(repr(normalizar(" ¡Qué RICO el Pan! ")))
print(repr(normalizar("Canción de CUNA")))
'¡que rico el pan!'
'cancion de cuna'
sin_tilde.get(c, c)busca la letra en el diccionario; si no está, devuelve la misma letra. Así, la ñ y todo lo demás quedan igual.reprmuestra el texto con comillas, para que se vea que no quedaron espacios en los bordes.
3. La mediana a mano
La mediana de una lista de números es el valor del medio cuando se ordenan. Si la cantidad es par, es el promedio de los dos del medio. Escribe mediana(valores) sin usar ningún módulo y pruébala con [7, 1, 5] (da 5) y con [8, 3, 1, 10] (da 5,5). Después calcula la mediana de las notas de matemática de notas.csv y compárala con su promedio.
Respuesta
def mediana(valores):
"""El valor del medio de una lista de números."""
ordenados = sorted(valores)
n = len(ordenados)
medio = n // 2
if n % 2 == 1:
return ordenados[medio]
return (ordenados[medio - 1] + ordenados[medio]) / 2
print(mediana([7, 1, 5]), mediana([8, 3, 1, 10]))
matematica = leer_columna("notas.csv", "matematica")
print("Mediana:", mediana(matematica),
" Promedio:", round(promedio(matematica), 1))
5 5.5
Mediana: 72.5 Promedio: 69.5
- Con 4 valores,
medioes 2 y los del medio están en las posiciones 1 y 2 (recuerda que se cuenta desde 0). - Se usa
sorted, que devuelve una lista nueva, y novalores.sort(), que ordenaría la lista original de quien llamó la función (mira el ejercicio 5). - La mediana (72,5) queda arriba del promedio (69,5) porque el 40 de Diego tira el promedio hacia abajo, pero a la mediana casi no la mueve.
4. Transformar listas con comprensiones
Tienes las temperaturas máximas de una semana, en grados Celsius:
semana = [18.5, 21.0, 23.4, 19.8, 25.1, 22.7, 17.9]
Con una comprensión de lista en cada caso (y la función celsius_a_fahrenheit de la teoría):
a) Crea la lista de las temperaturas en Fahrenheit, redondeadas a un decimal.
b) Crea la lista de las temperaturas mayores a 22 °C.
c) Crea la lista de los números de día (empezando en 1) en que la temperatura pasó de 22 °C. Pista: enumerate(semana, start=1) da pares (día, temperatura).
Respuesta
fahrenheit = [round(celsius_a_fahrenheit(t), 1) for t in semana]
calurosos = [t for t in semana if t > 22]
dias = [d for d, t in enumerate(semana, start=1) if t > 22]
print(fahrenheit)
print(calurosos)
print(dias)
[65.3, 69.8, 74.1, 67.6, 77.2, 72.9, 64.2]
[23.4, 25.1, 22.7]
[3, 5, 6]
En c), cada elemento de enumerate es una tupla (día, temperatura) que se desempaqueta en d, t; se guarda d solo si t pasa de 22.
5. La función que cambió mi lista
Este programa quiere sumar 5 puntos de bono a una copia de las notas y dejar las originales intactas. Ejecútalo. ¿Qué pasó con originales? ¿Por qué?
def con_bono(lista):
for i in range(len(lista)):
lista[i] = lista[i] + 5
return lista
originales = [70, 45, 88]
nuevas = con_bono(originales)
print("Nuevas: ", nuevas)
print("Originales:", originales)
Nuevas: [75, 50, 93]
Originales: [75, 50, 93]
Y este otro, que quiere ordenar las notas, ¿por qué imprime None?
notas_curso = [70, 45, 88]
ordenadas = notas_curso.sort()
print(ordenadas)
None
Corrige los dos programas.
Respuesta
El primero. Al llamar con_bono(originales), el parámetro lista no recibe una copia: recibe la misma lista, con otro nombre. Cambiar lista[i] adentro de la función cambia la lista de afuera. nuevas y originales son dos nombres para una sola lista. La solución es crear una lista nueva, por ejemplo con una comprensión:
def con_bono(lista):
return [n + 5 for n in lista]
originales = [70, 45, 88]
nuevas = con_bono(originales)
print("Nuevas: ", nuevas)
print("Originales:", originales)
Nuevas: [75, 50, 93]
Originales: [70, 45, 88]
Otra forma es copiar al principio de la función con lista = lista.copy().
El segundo. El método sort ordena la lista en su lugar (la cambia) y devuelve None. Si quieres una lista nueva ordenada, usa sorted; si te da igual cambiar la original, llama sort sin asignar:
notas_curso = [70, 45, 88]
ordenadas = sorted(notas_curso)
print(ordenadas, notas_curso)
notas_curso.sort()
print(notas_curso)
[45, 70, 88] [70, 45, 88]
[45, 70, 88]
Regla práctica: una función que recibe una lista no debería cambiarla, salvo que ese sea justamente su trabajo y lo diga su nombre.
6. Contar votos con un diccionario
En un curso votaron por la fruta para la merienda:
votos = ["mango", "plátano", "mango", "uva", "piña", "mango",
"uva", "plátano", "mango", "uva", "piña", "uva", "mango"]
a) Usa la función contar de la teoría para obtener cuántos votos tuvo cada fruta.
b) Imprime las frutas de la más votada a la menos votada, una por línea, con su cantidad y su porcentaje.
c) ¿Cuántas letras distintas se usaron en total en los votos? Usa un conjunto.
Respuesta
resultado = contar(votos)
print(resultado)
total = len(votos)
for fruta, n in sorted(resultado.items(), key=la_cuenta, reverse=True):
print(f"{fruta:<8} {n} votos ({n / total:.0%})")
letras = set()
for fruta in votos:
for letra in fruta:
letras.add(letra)
print(len(letras), "letras distintas:", "".join(sorted(letras)))
{'mango': 5, 'plátano': 2, 'uva': 4, 'piña': 2}
mango 5 votos (38%)
uva 4 votos (31%)
plátano 2 votos (15%)
piña 2 votos (15%)
13 letras distintas: agilmnoptuváñ
{fruta:<8}rellena el nombre con espacios hasta 8 caracteres, para que las columnas queden alineadas;{n / total:.0%}escribe una proporción como porcentaje sin decimales.- Para la «á» de plátano, el conjunto la cuenta como una letra distinta de la «a». Si quisieras contarlas juntas, primero pasarías cada voto por
normalizardel ejercicio 2.
7. Palabras en común
Con limpiar y conjuntos, encuentra las palabras que aparecen en las dos frases y las que aparecen solo en la primera. Los operadores de conjuntos son a & b (las que están en los dos) y a - b (las que están en a pero no en b).
frase_1 = "El perro de Ana corre en el parque."
frase_2 = "Ana corre todas las mañanas en el parque del barrio."
Respuesta
a = set(limpiar(frase_1).split())
b = set(limpiar(frase_2).split())
print("En las dos: ", sorted(a & b))
print("Solo en la 1.ª: ", sorted(a - b))
En las dos: ['ana', 'corre', 'el', 'en', 'parque']
Solo en la 1.ª: ['de', 'perro']
Sin limpiar, «parque.» (con punto) y «parque» serían palabras distintas y no aparecerían en común; «El» y «el», tampoco.
8. Las ventas de una tienda
Este código crea un CSV de ejemplo con las ventas de una semana en una tienda pequeña:
ventas = [
["dia", "producto", "cantidad"],
["lunes", "pan", 40], ["lunes", "leche", 12], ["lunes", "queso", 3],
["martes", "pan", 35], ["martes", "leche", 15],
["miercoles", "pan", 42], ["miercoles", "queso", 5],
["jueves", "pan", 38], ["jueves", "leche", 10], ["jueves", "queso", 4],
["viernes", "pan", 50], ["viernes", "leche", 18],
]
with open("ventas.csv", "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerows(ventas)
a) Léelo solo con open (sin el módulo csv) y cuenta cuántas filas de datos tiene, sin contar la de los nombres de las columnas.
b) Léelo con csv.DictReader y calcula, con un diccionario, el total vendido de cada producto.
Respuesta
# a) Solo con open: la primera línea son los nombres
with open("ventas.csv", encoding="utf-8") as f:
lineas = f.readlines()
print(lineas[0].strip())
print(len(lineas) - 1, "filas de datos")
# b) Con csv.DictReader y un diccionario producto → total
totales = {}
with open("ventas.csv", encoding="utf-8") as f:
for fila in csv.DictReader(f):
producto = fila["producto"]
cantidad = int(fila["cantidad"])
totales[producto] = totales.get(producto, 0) + cantidad
print(totales)
dia,producto,cantidad
12 filas de datos
{'pan': 205, 'leche': 55, 'queso': 12}
f.readlines()lee todo el archivo de una vez y devuelve una lista con una línea por elemento.- La línea clave de b) es la misma que la de contar palabras, pero sumando
cantidaden lugar de 1. Un diccionario que acumula por clave es uno de los patrones más útiles de todo el análisis de datos. - Sin
int(...),totales.get(producto, 0) + cantidadfallaría con unTypeError, porquecantidadllegaría como texto.
9. Instalar un paquete
a) En Colab quieres usar el paquete wordcloud (dibuja «nubes de palabras») y al escribir import wordcloud aparece un error. ¿Qué error esperas ver y qué escribes en una celda para arreglarlo?
b) Quieres instalar el paquete que trae LinearRegression. Su nombre para instalar es scikit-learn. ¿Con qué nombre lo importas?
c) ¿Cuál es la diferencia entre import random y pip install?
Respuesta
a) Un ModuleNotFoundError: No module named 'wordcloud'. Se arregla con una celda que diga:
!pip install wordcloud
Después de instalarlo, vuelve a ejecutar la celda del import.
b) Con import sklearn (o, más común, from sklearn.linear_model import LinearRegression). El nombre para instalar y el nombre para importar no siempre coinciden.
c) pip install descarga e instala un paquete en la computadora: se hace una vez. import carga en tu programa un módulo que ya está instalado: se hace en cada programa que lo use. random viene con Python, así que nunca hace falta instalarlo.
10. Mejorar la clase Contador
Agrega a la clase Contador de la teoría dos métodos:
total(), que devuelve cuántas cosas se agregaron en total;proporcion(cosa), que devuelve qué fracción del total es esa cosa (0 si nunca se agregó).
Pruébala con las palabras del texto de la panadería: ¿qué proporción de las palabras es «pan»?
Respuesta
class Contador:
"""Cuenta cuántas veces aparece cada cosa."""
def __init__(self):
self.conteo = {}
def agregar(self, cosa):
self.conteo[cosa] = self.conteo.get(cosa, 0) + 1
def agregar_todas(self, cosas):
for cosa in cosas:
self.agregar(cosa)
def mas_comunes(self, k):
pares = sorted(self.conteo.items(), key=lambda par: par[1],
reverse=True)
return pares[:k]
def total(self):
return sum(self.conteo.values())
def proporcion(self, cosa):
return self.conteo.get(cosa, 0) / self.total()
c = Contador()
c.agregar_todas(palabras)
print(c.total(), "palabras")
print(f"pan: {c.proporcion('pan'):.1%} queso: {c.proporcion('queso')}")
63 palabras
pan: 6.3% queso: 0.0
self.conteo.values() da solo los valores del diccionario (las cuentas), y sum los suma. Los métodos nuevos usan self.conteo, el mismo dato que llenó agregar_todas: eso es lo que hace útil a un objeto.
Mini-proyecto: el reporte de notas de un curso
Vas a escribir un programa que lee las notas de un curso desde un CSV e imprime un reporte. Los datos son de ejemplo, inventados para este ejercicio: diez estudiantes con las notas de tres trimestres, sobre 100.
curso = [
["nombre", "trimestre_1", "trimestre_2", "trimestre_3"],
["Andrea", 72, 78, 85],
["Benjamín", 45, 52, 58],
["Camila", 88, 91, 87],
["Daniel", 60, 55, 49],
["Fernanda", 93, 95, 98],
["Gabriel", 50, 61, 70],
["Isabel", 77, 74, 80],
["Joaquín", 38, 47, 55],
["Lucía", 81, 79, 76],
["Mateo", 66, 70, 73],
]
with open("curso.csv", "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerows(curso)
Escribe tu programa en funciones, y que al final imprima:
- Cada estudiante con su promedio, ordenados del mejor al peor promedio.
- Cuántos estudiantes aprueban, si se aprueba con un promedio de 51 o más, y quiénes no.
- El promedio del curso en cada trimestre.
- Quién mejoró más del primer al tercer trimestre, y en cuántos puntos.
Una forma de organizarlo: una función leer_notas(archivo) que devuelva un diccionario nombre → lista de tres notas; las funciones promedio de la teoría y mediana del ejercicio 3 para los cálculos; y una función reporte(notas) que imprima todo.
Una solución
def leer_notas(archivo):
"""Diccionario nombre → lista de notas (números)."""
notas = {}
with open(archivo, encoding="utf-8") as f:
for fila in csv.DictReader(f):
nombre = fila["nombre"]
notas[nombre] = [float(fila[f"trimestre_{t}"])
for t in (1, 2, 3)]
return notas
def reporte(notas, minimo=51):
"""Imprime el reporte del curso."""
promedios = {n: promedio(v) for n, v in notas.items()}
print("1. Promedios, del mejor al peor")
for nombre, p in sorted(promedios.items(), key=la_cuenta,
reverse=True):
print(f" {nombre:<10} {p:5.1f}")
reprobados = [n for n, p in promedios.items() if p < minimo]
print(f"2. Aprueban {len(notas) - len(reprobados)} de {len(notas)}."
f" No aprueban: {', '.join(reprobados)}")
print("3. Promedio del curso por trimestre")
for t in range(3):
del_trimestre = [v[t] for v in notas.values()]
print(f" Trimestre {t + 1}: {promedio(del_trimestre):.1f}"
f" (mediana {mediana(del_trimestre):.1f})")
mejoras = {n: v[2] - v[0] for n, v in notas.items()}
mejor = max(mejoras, key=mejoras.get)
print(f"4. Mejoró más: {mejor}, {mejoras[mejor]:+.0f} puntos")
reporte(leer_notas("curso.csv"))
1. Promedios, del mejor al peor
Fernanda 95.3
Camila 88.7
Lucía 78.7
Andrea 78.3
Isabel 77.0
Mateo 69.7
Gabriel 60.3
Daniel 54.7
Benjamín 51.7
Joaquín 46.7
2. Aprueban 9 de 10. No aprueban: Joaquín
3. Promedio del curso por trimestre
Trimestre 1: 67.0 (mediana 69.0)
Trimestre 2: 70.2 (mediana 72.0)
Trimestre 3: 73.1 (mediana 74.5)
4. Mejoró más: Gabriel, +20 puntos
leer_notasarma cada lista con una comprensión que lee las columnastrimestre_1,trimestre_2ytrimestre_3.max(mejoras, key=mejoras.get)recorre las claves del diccionario y se queda con la que tiene el valor más grande: la misma idea dekeyque ensorted.- El promedio del curso sube trimestre a trimestre, pero no todos mejoraron: Daniel y Lucía bajaron. Un promedio general esconde a las personas; por eso conviene mirar también cada caso.
- Para ir más allá: guarda el reporte en un archivo
reporte.txt(abre con"w"y usaf.write(...)en lugar deprint), o grafica los promedios conplt.barh, como las palabras de la teoría.
Errores comunes
- Olvidar el
return. Una función que solo haceprintdevuelveNone; si guardas su resultado, guardas nada. - Usar fuera una variable que se creó dentro de una función. Es local y no existe afuera (
NameError). Devuélvela conreturn. - Cambiar sin querer la lista que recibió una función. El parámetro es la misma lista, no una copia. Crea una lista nueva o usa
.copy(). - Escribir
lista = lista.sort().sortordena en su lugar y devuelveNone. Usasorted(lista)si quieres una lista nueva. - Contar desde 1. El primer elemento es
lista[0]y el último eslista[-1]olista[len(lista) - 1].lista[len(lista)]daIndexError. - Buscar una clave que no está.
d["clave"]daKeyErrorsi no existe; usa"clave" in dpara preguntar od.get("clave", 0)para tener un valor por defecto. - No limpiar el texto antes de contar. «Pan», «pan,» y «pan» son tres claves distintas para la computadora.
- Hacer cuentas con lo que se leyó de un archivo sin convertirlo. De un CSV todo llega como texto:
int(...)ofloat(...)antes de sumar. - Pasar
key=la_cuenta()con paréntesis.keyrecibe la función, no su resultado:key=la_cuenta. - Escribir
pip installdentro del código Python. En Colab va en su propia celda con!adelante; en tu computadora, en la terminal.
¿Lo lograste?
Al terminar este tema deberías poder hacer lo siguiente. Al lado, dónde lo practicaste.
- Escribir funciones con parámetros y
return(ejercicios 1, 2 y 3, y el mini-proyecto). - Recorrer y transformar listas, también con comprensiones, sin cambiar sin querer la original (ejercicios 4 y 5).
- Contar frecuencias con un diccionario y ordenarlas con
sortedykey(ejercicios 6 y 7). - Leer un CSV con
openy con el módulocsv(ejercicio 8 y el mini-proyecto). - Instalar un paquete con
pipy cargarlo conimport(ejercicio 9). - Reconocer una clase, un objeto y sus métodos (ejercicio 10).