FUNCIONES
1. Funciones sin parámetros; son las que no se especifican parámetros de uso
Formato estándar: def <nombre>():
<instrucciones>
Ejemplo :
def promedio():
calculo = (10+9+7)/3
print(calculo)
2. Funciones con parámetros
Formato estándar:
def <nombre>(<param_1>, <param_2>, ..., <param_n>):
<instrucciones>
Ejemplo: def promedio(nota_1,nota_2,nota_3):
calculo = (nota_1+nota_2+nota_3)/3
print(calculo)
3. Funciones que retornan valores
Formato estándar: def <nombre>(<param_1>, <param_2>, ..., <param_n>):
<instrucciones>
return resultado
Ejemplo: # Notas del estudiante
notas = [8.5, 9.0, 6.0, 10.0]
def promedio(lista):
resultado = sum(lista)/len(lista)
return resultado
Uso del return: return es una palabra clave que se usa dentro de una función para devolver un valor al
lugar donde se llamó esa función.
Cuando Python encuentra un return, termina la ejecución de la función en ese punto. Todo lo que venga
después no se ejecuta.
Ejemplo: def promedio(lista):
resultado = sum(lista)/len(lista)
return resultado
Devuelve una sola variable o valor llamado resultado desde la función hacia el lugar donde fue llamada y
sirve para ocupar esta variable fuera de la función.
Ejemplo:
def boletin(lista):
resultado = sum(lista)/len(lista)
if resultado >= 7:
situacion ='Aprobado(a)' # almacena en la variable si esta aprobado o reprobado
else:
situacion ='Reprobado(a)'
return (resultado,situacion)
Este return está devolviendo dos valores a la vez, empaquetados en una tupla.
En el return podemos colocar lo mismo que se pide al print.
Ejemplo:
def boletin(lista):
resultado = sum(lista)/len(lista)
if resultado >= 7:
situacion ='Aprobado(a)'
else:
situacion ='Reprobado(a)'
return f'El/la estudiante obtuvo un puntaje de: {resultado} y su situación es: {situacion}.'
Está devolviendo una cadena de texto (un string) ya formateada, que incluye los valores de resultado y
situacion.
4. Funciones lambda: Son funciones que no necesitan ser definidas, o sea, no poseen un nombre, y describen
en una única fila los comandos que deseamos aplicar.
Formato estándar: lambda <variable>: <expresion>
Ejemplo 1: nota_ = float(input('Digite una nota: '))
cualitativa = lambda x: x + 0.5
cualitativa(nota_)
Ejemplo 2: n1 = float(input('Digite la primera nota: '))
n2 = float(input('Digite la segunda nota: '))
n3 = float(input('Digite la tercera nota: '))
ponderado = lambda x,y,z: (x*3+y*2+z*5)/10
nota_final = ponderado(n1,n2,n3)
Uso de map: En Python, la función map nos permite aplicar una función sobre los ítems de un objeto iterable
(lista, tupla, etc...) y devuelve un nuevo iterable (listas, tuplas, etc). Esto se usa para iterar sobre cada elemento
de la lista y lugo devuelve un objeto iterable.
Formato estándar: map(<lambda function>, <iterador>)
Ejemplo : en esta función sin uso de map
def cuadrado(numero):
return numero * numero
lista = [1,2,3,4,5]
resultado = map(cuadrado, lista)
lista_resultado = list(resultado)
print(lista_resultado)
Con uso de map: lista = [1,2,3,4,5]
resultado = map(lambda numero: numero * numero , lista)
lista_resultado = list(resultado)
print(lista_resultado)
Otro ejemplo: def media(lista: list) -> float:
calculo = sum(lista) / len(lista)
return calculo
temp_celsius = [0, 25, 37, 78, 100]
temp_fahrenheit = list(map(lambda x: (x * 9/5) + 32, temp_celsius))
temp_fahrenheit
ESTRUCTURAS DE DATOS COMPUESTAS
Estructuras anidadas.
1. Lista de listas: Una lista y cada elemento es otra lista.
Formato estándar: [[a1, a2,...,an], [b1, b2,...,bn], ..., [n1, n2,...,nn]]
Ejemplo:
notas_grupo = ['Juan', 8.0, 9.0, 10.0, 'Maria', 9.0, 7.0, 6.0, 'José', 3.4, 7.0, 7.0, 'Claudia', 5.5, 6.6, 8.0, 'Ana', 6.0, 10.0, 9.5]
nombres = []
notas = []
for i in range(len(notas_grupo)):
if i % 4 == 0:
[Link](notas_grupo[i])
else:
[Link](notas_grupo[i])
nombres
Salida: ['Juan', 'Maria', 'José', 'Claudia', 'Ana']
notas_separadas = []
for i in range(0, len(notas),3):
notas_separadas.append([notas[i],notas[i+1], notas[i+2]])
notas_separadas
Salida: [[8.0, 9.0, 10.0],
[9.0, 7.0, 6.0],
[3.4, 7.0, 7.0],
[5.5, 6.6, 8.0],
[6.0, 10.0, 9.5]]
• Las listas de lista permiten almacenar datos de diferentes tipos y formatos, haciendo que el código sea
mas flexible y adaptable a diferentes escenarios de análisis de datos.
• Permiten alcanzar datos en forma organizada, como en tablas y matrices.
• Permiten almacenar en una estructura jerárquica, lo cual puede ser útil en situaciones en las que los
datos tienen varias dimensiones.
2. Lista de tuplas
Formato estándar: [(a1, a2,...,an), (b1, b2,...,bn), ..., (n1, n2,...,nn)]
Ejemplo:
['Juan', 'Maria', 'José', 'Claudia', 'Ana']
from random import randint
def genera_numero():
return randint(0,999)
genera_numero()
codigo_estudiantes = []
for i in range(len(nombres)):
codigo_estudiantes.append((nombres[i],nombres[i][0]+str(genera_numero())))
codigo_estudiantes
Salida:
[('Juan', 'J63'),
('Maria', 'M469'),
('José', 'J985'),
('Claudia', 'C546'),
('Ana', 'A272')]
Tuplas: Las tuplas son estructuras de datos inmutables en el lenguaje Python que se utilizan para almacenar
conjuntos de múltiples elementos y a menudo se aplican para agrupar datos que no deben modificarse. Es decir,
no es posible agregar, cambiar o eliminar sus elementos después de creadas. Vamos a explorar un poco
más este tipo de estructura enfocada en la aplicación en ciencia de datos.
Las tuplas son especialmente útiles en situaciones en las que necesitamos garantizar que los datos no se
modifiquen accidental o intencionalmente.
Por ejemplo, en un conjunto de datos que representa el registro de estudiantes, podemos utilizar una tupla para
representar a ese estudiante en particular y mantenerlo en la base de datos de una institución educativa.
De esta manera, aseguramos que la información de cada estudiante no se modifique inadvertidamente.
Para crear una tupla, simplemente separamos sus elementos por comas y los envolvemos entre paréntesis.
Por ejemplo, podemos crear una tupla con un registro de una estudiante de la siguiente manera:
registro = ("Julia", 23, "CDMX", "EM", "Python para DS 1")
Para acceder a los elementos de una tupla, podemos usar el índice entre corchetes.
Por ejemplo: print(registro[0]) # imprime Julia
print(registro[-1]) # imprime Python para DS 1
Además, al ser un iterable, podemos desempaquetar los datos de una tupla pasando cada valor a una
variable.
Por ejemplo: nombre, edad, ciudad, estado, curso = registro
Y mostrar los datos del registro de la estudiante:
print(f'La estudiante {nombre} tiene {edad} años y vive en {ciudad}-{estado}. Ella está matriculada en el curso
de {curso}.')
Salida:
La estudiante Julia tiene 23 años y vive en CDMX-EM. Ella está matriculada en el curso de Python para DS 1.
3. List comprehension: Es una forma simple y concisa de crear una lista.
Podemos aplicar condicionales y lazos para crear diversos tipos de listas a partir de patrones que
deseamos para nuestra estructura de datos.
Formato estándar: [expresion for item in lista]
nombres = [('Juan', 'J63'), ('Maria', 'M469'), ('José', 'J985'), ('Claudia', 'C546'), ('Ana', 'A272')]
promedios = [9.0, 7.3, 5.8, 6.7, 8.5]
# Se elige solo el nombre de la lista de nombres y códigos
nombres = [nombre[0] for nombre in nombres]
nombres
# con zip forma tuplas en una lista.
estudiantes = list(zip(nombres,promedios))
estudiantes
candidatos = [estudiante[0] for estudiante in estudiantes if estudiante[1] >= 8] # List comprehesion
candidatos
Función zip()
La función zip() es una función incorporada de Python que toma uno o más iterables (lista, cadena,
diccionario, etc.) y los devuelve como un iterador de tuplas donde cada elemento de los iterables está
emparejado. Es útil para realizar iteraciones simultáneas en varias listas.
La función zip() se puede utilizar junto con otras funciones de Python, como map() y filter(), para crear
soluciones elegantes y concisas para ciertos problemas.
Realicemos una prueba simple para verificar este comportamiento:
objeto_zip = zip([1, 2, 3])
objeto_zip
Salida: <zip at 0x7f28fc5c0040>
Notamos que zip() creó un objeto zip en la memoria, que sería nuestro iterable. Coloquemos el resultado
en una lista para verificar la salida:
list(objeto_zip)
Salida: [(1,), (2,), (3,)]
Observa que, con solo un iterable, se generó una lista de tuplas, donde cada tupla tiene, como uno de los
pares, los elementos provenientes de la lista [1, 2, 3] y la otra parte de los pares está vacía. Como solo
usamos un iterable, cada tupla está vacía en el segundo elemento, ya que zip() actúa para crear pares de
iterables.
Pero lo más interesante es trabajar con dos o más iterables en los que podemos emparejarlos.
Por ejemplo, si queremos crear una lista de tuplas con la asignación de las regiones de Brasil con sus
respectivos identificadores:
id = [1, 2, 3, 4, 5]
region = ["Norte", "Oriente", "Sudeste", "Centro", "Sur"]
mapa = list(zip(id, region))
mapa
Salida: [(1, 'Norte'), (2, Oriente), (3, 'Sudeste'), (4, 'Centro'), (5, 'Sur')]
Para un científico de datos, esta función puede ayudar a emparejar dos listas diferentes en un único
objeto zip, que se puede transformar en una lista de tuplas (formato ideal para generar un índice de más
de un nivel que se explorará en algunos de los cursos de la formación) o en un diccionario pasando el
objeto zip a la función dict().
Ahora, si las listas de entrada tienen longitudes diferentes, la salida contendrá el mismo número de tuplas
que la lista de menor longitud y los elementos restantes de los otros iterables se ignorarán.
Por ejemplo:
codigos = ["1000", "1001", "1002", "1003", "1004", "1005"]
frutas = ["manzana", "uva", "banana", "naranja"]
mercancia = list(zip(codigos, frutas))
mercancia
Salida: [('1000', 'manzana'), ('1001', 'uva'), ('1002', 'banana'), ('1003', 'naranja')]
Para realizar el proceso contrario, de transformar una tupla iterable en listas, basta con colocar el
operador asterisco (*) al lado izquierdo del nombre de la tupla iterable que se desea extraer los datos,
transmitiendo cada tupla a una variable.
tupla_iterable = [('J392', 'Juan'), ('M890', 'Maria'), ('J681', 'José'), ('C325', 'Claudia'), ('A49', 'Ana')]
ids, nombres = zip(*tupla_iterable)
ids = list(ids)
nombres = list(nombres)
print("IDs = ", ids) # Con el asterisco se separa la tupla en dos listas
print("Nombres = ", nombres)
Salida: IDs = ['J392', 'M890', 'J681', 'C325', 'A49']
Nombres = ['Juan', 'Maria', 'José', 'Claudia', 'Ana']
La idea de realizar un "desempaquetado inverso" es útil cuando queremos extraer claves o valores por
separado o generar una lista de tuplas separadas, con el conjunto de claves y valores, cada uno
representado en una tupla.
List Comprehension con if y else.
Para la lista de las situaciones utiliza el formato:
[resultado_if if condicion else resultado_else for item in lista]
nombres = [('Juan', 'J63'), ('Maria', 'M469'), ('José', 'J985'), ('Claudia', 'C546'), ('Ana', 'A272')]
notas = [[8.0, 9.0, 10.0], [9.0, 7.0, 6.0], [3.4, 7.0, 7.0], [5.5, 6.6, 8.0], [6.0, 10.0, 9.5]]
promedios = [9.0, 7.3, 5.8, 6.7, 8.5]
situacion = ['Aprobado(a)' if promedio >= 7 else 'Reprobado(a)' for promedio in promedios]
situación
#Lista de listas
registros = [x for x in [nombres, notas, promedios, situacion]]
registros
Salida:
[[('Juan', 'J63'),
('Maria', 'M469'),
('José', 'J985'),
('Claudia', 'C546'),
('Ana', 'A272')],
[[8.0, 9.0, 10.0],
[9.0, 7.0, 6.0],
[3.4, 7.0, 7.0],
[5.5, 6.6, 8.0],
[6.0, 10.0, 9.5]],
[9.0, 7.3, 5.8, 6.7, 8.5],
['Aprobado(a)', 'Aprobado(a)', 'Reprobado(a)', 'Reprobado(a)', 'Aprobado(a)']]
Podemos acudir a la forma más simple de generación de lista de listas con el uso directo de los corchetes
sin la necesidad de utilizar las expresiones y el lazo for que se emplea en la comprensión de listas.
lista_completa = [nombres,notas,promedios,situacion]
lista_completa
salida:
[[('Juan', 'J63'),
('Maria', 'M469'),
('José', 'J985'),
('Claudia', 'C546'),
('Ana', 'A272')],
[[8.0, 9.0, 10.0],
[9.0, 7.0, 6.0],
[3.4, 7.0, 7.0],
[5.5, 6.6, 8.0],
[6.0, 10.0, 9.5]],
[9.0, 7.3, 5.8, 6.7, 8.5],
['Aprobado(a)', 'Aprobado(a)', 'Reprobado(a)', 'Reprobado(a)', 'Aprobado(a)']]
4. Dict comprehension
Es una forma simple y concisa de crear o modificar un diccionario.
Podemos aplicar condicionales y lazos para crear diversos tipos de diccionarios a partir de los patrones que
buscamos para nuestra estructura de datos y con el soporte de iterables como listas o sets.
Formato estándar: {clave: valor for item in lista}
Ahora, nuestra demanda consiste en generar un diccionario a partir de la lista de listas que creamos en la
Situación 10 para entregar a la persona responsable por construir las tablas para el análisis de los datos.
Las llaves de nuestro diccionario serán las columnas identificando el tipo de dato
Los valores serán las listas con los datos correspondientes a aquella llave.
Para facilitar nuestra comprensión del proceso vamos a trabajar con un grupo de 5 estudiantes.
Tip: Utiliza el formato: {llave: valor for item in lista}
lista_completa = [[('Juan', 'J63'), ('Maria', 'M469'), ('José', 'J985'), ('Claudia', 'C546'), ('Ana', 'A272')],
[[8.0, 9.0, 10.0], [9.0, 7.0, 6.0], [3.4, 7.0, 7.0], [5.5, 6.6, 8.0], [6.0, 10.0, 9.5]],
[9.0, 7.3, 5.8, 6.7, 8.5],
['Aprobado(a)', 'Aprobado(a)', 'Reprobado(a)', 'Reprobado(a)', 'Aprobado(a)']]
columnas = ['Notas','Promedio Final','Situación']
# creación del dict comprehension
registro = {columna[i]:lista_completa[i+1] for i in range(len(columnas))}
registro
registro['Estudiante'] = [lista_completa[0][i][0] for i in range(len(lista_completa[0]))]
registro
Salida: es un diccionario,
{'Notas': [[8.0, 9.0, 10.0],
[9.0, 7.0, 6.0],
[3.4, 7.0, 7.0],
[5.5, 6.6, 8.0],
[6.0, 10.0, 9.5]],
'Promedio Final': [9.0, 7.3, 5.8, 6.7, 8.5],
'Situación': ['Aprobado(a)',
'Aprobado(a)',
'Reprobado(a)',
'Reprobado(a)',
'Aprobado(a)'],
'Estudiante': ['Juan', 'Maria', 'José', 'Claudia', 'Ana']}
Excepciones.
En Python, básicamente existen dos formas distintas de errores: los de sintaxis y las excepciones. Las
excepciones son una manera de manejar errores y situaciones inesperadas en el código, asegurando un
flujo de ejecución más controlado.
Como científico de datos, deberás prestar atención a situaciones como estas para evitar errores o problemas
en tus códigos y análisis que puedan afectar tanto la experiencia del usuario como la eficiencia de tu análisis.
Tipos de Excepciones
SyntaxError: Ocurre cuando el analizador detecta un error en la descripción del código. Normalmente, una
flecha señala la parte del código que generó el error, como una especie de pista sobre dónde puede haber
ocurrido el error.
print(10 / 2
Salida: File "<ipython-input-16-2db3afa07d68>", line 1
print(10/2
^
SyntaxError: unexpected EOF while parsing
Observa que olvidamos cerrar el paréntesis y, por lo tanto, se presentó un error de sintaxis, es decir, de
escritura de código.
NameError: Excepción lanzada cuando intentamos utilizar un nombre de algún elemento que no está
presente en nuestro código.
raiz = sqrt(100)
Salida:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-17-2e14e900fb9f> in <module>
----> 1 raiz = sqrt(100)
NameError: name 'sqrt' is not defined
En este caso, el intérprete no puede aplicar el método de la raíz cuadrada porque no se ha importado junto
con la biblioteca math.
IndexError: Excepción lanzada cuando intentamos indexar alguna estructura de datos como lista, tupla o
incluso una cadena más allá de sus límites.
lista = [1, 2, 3]
lista[4]
Salida:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-18-f5fe6d922eea> in <module>
1 lista = [1, 2, 3]
----> 2 lista[4]
IndexError: list index out of range
Para esta situación, solo tenemos 3 elementos en la lista y tratamos de leer el elemento en la posición 4,
que no existe. Recibimos el mensaje de que el índice está fuera de rango.
TypeError: Excepción lanzada cuando un operador o función se aplican a un objeto cuyo tipo es
inapropiado.
"1" + 1
Salida:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-20-ec358fc6499a> in <module>
----> 1 "1" + 1
TypeError: can only concatenate str (not "int") to str
Observa que intentamos "sumar" una cadena con un número entero y esto generó una excepción en nuestro
código. Esto ocurrió por 2 razones: el operador de suma se consideró como concatenación porque
comenzamos usando una cadena (en este caso, el signo de suma se utiliza para concatenar cadenas), y un
valor de tipo entero no se puede concatenar en este tipo de operación.
KeyError
Excepción lanzada cuando intentamos acceder a una clave que no está en el diccionario presente en nuestro
código.
estados = {'EM': 1, 'JC': 2, 'OA': 3}
estados["MI"]
Salida:
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-22-45729db26889> in <module>
1 estados = {'EM': 1, 'JC': 2, 'OA': 3}
----> 2 estados["MI"]
KeyError: 'MI'
Intentamos acceder a los datos del Estado MI (Michoacán), que no está presente en el diccionario, lanzando
así la excepción.
Warning
Excepción lanzada en situaciones en las que necesitamos alertar al usuario sobre algunas condiciones del
código. Estas condiciones no necesariamente interrumpen la ejecución del programa, pero pueden lanzar
advertencias sobre el uso de módulos obsoletos, o que pueden ser obsoletos en futuras actualizaciones, o
también para cambios que pueden repercutir en alguna parte del código.
Es importante recordar que, en el caso de los Warnings, pueden ser ignorados o tratados como excepciones.
import numpy as np
a = [Link](5)
a/a # presenta una advertencia
Salida:
<ipython-input-23-93a37b275923>:4: RuntimeWarning: invalid value encountered in true_divide
a / a # presenta una advertencia
array([nan, 1., 1., 1., 1.])
Intentamos dividir cero por cero. En un array Numpy, que es esta estructura en la salida de la consola, este
resultado genera un valor nan (Not a Number).
Es decir, puedes seguir con la ejecución del programa, pero es probable que necesites procesar los datos
para poder utilizar este array en alguna operación más adelante.
Tratando las Excepciones
El tratamiento de las excepciones contribuye a establecer un flujo alternativo para la ejecución del código evitando
la interrupción de los procesos inesperadamente.
Existe una serie de excepciones, y a partir del comportamiento que queremos, y de los errores que queremos
tratar, es posible construir un camino para el usuario, o también, proveer más detalles sobre aquella excepción.
Try ... Except
try: # código que será ejecutado. En caso de que surja una excepción, para inmediatamente
except <nombre_de_la_excepcion as e>: # Si surje una excepción en el try, ejecuta este código, si no, salta
esta etapa.
Ejemplo:
try:
nombre = input('Digitar el nombre del/de la estudiante: ')
resultado = notas[nombre]
except Exception as e:
print(type(e), e)
salida: Digitar el nombre del/de la estudiante: Michael
<class 'KeyError'> 'Michael'
try:
nombre = input('Digitar el nombre del/de la estudiante: ')
resultado = notas[nombre]
except KeyError:
print('Estudiante no matriculado(a) en el grupo.')
Salida: Digitar el nombre del/de la estudiante: Michael
Estudiante no matriculado(a) en el grupo.
Agregando la cláusula Else
try: # código que será ejecutado. En caso de que surja una excepción, para inmediatamente
except: # Si surje una excepción en el try, ejecuta este código, si no, salta esta etapa
else: # Si no surjen excepciones, ejecuta esta parte del código
Ejemplo:
try:
nombre = input('Digitar el nombre del/de la estudiante: ')
resultado = notas[nombre]
except KeyError:
print('Estudiante no matriculado(a) en el grupo.')
else:
print(resultado)
Salida: Digitar el nombre del/de la estudiante: Michael
Estudiante no matriculado(a) en el grupo.
try:
nombre = input('Digitar el nombre del/de la estudiante: ')
resultado = notas[nombre]
except KeyError:
print('Estudiante no matriculado(a) en el grupo.')
else:
print(f'Las notas del/de la estudiante son: {resultado}.')
Salida: Digitar el nombre del/de la estudiante: Maria
Las notas del/de la estudiante son: [9.0, 7.0, 6.0].
Añadiendo la cláusula finally
try: # código que será ejecutado. En caso de que surja una excepción, para inmediatamente
except: # Si surje una excepción en el try, ejecuta este código, si no, salta esta etapa
else: # Si no surgen excepciones, ejecuta esta parte del código
finally: # Ejecuta este trecho (con o sin excepción)
Ejemplo:
try:
nombre = input('Digitar el nombre del/de la estudiante: ')
resultado = notas[nombre]
except KeyError:
print('Estudiante no matriculado(a) en el grupo.')
else:
print(f'Las notas del/de la estudiante son: {resultado}.')
finally:
print('La consulta ha concluido.')
Salida: Digitar el nombre del/de la estudiante: Michael
Estudiante no matriculado(a) en el grupo.
La consulta ha concluido.
Digitar el nombre del/de la estudiante: Claudia
Las notas del/de la estudiante son: [5.5, 6.6, 8.0].
La consulta ha concluido.
Raise.
Otra forma de trabajar con las excepciones en tu código, es generando tus propias excepciones para
determinados comportamientos que deseas en tu código.
Para ello, utilizamos la palabra clave raise junto al tipo de excepción que se desea mostrar y el mensaje que será
exhibido.
Formato: raise NombreDelError("Mensaje deseado.")