Program A Con Python Web
Program A Con Python Web
ISBN: 978-607-2615-62-5
[Link]
Estudiante
Presentación
Para los estudiantes de Ciencia y Tecnología de la UACM, una de las últimas materias a
certificar, a pesar de ser del primer semestre es Introducción a la Programación. Una de
las sugerencias que se han hecho en la discusión de la revisión de los planes de estudio
del Colegio, es que Introducción a la programación se dé en un lenguaje más sencillo e
intuitivo, como Python, para todas las licenciaturas excepto probablemente Ingeniería
de Software.
Python es un lenguaje de programación intuitivo que, entre otras bondades, tiene aso-
ciadas bibliotecas sobre ciertas áreas de las ciencias, para poder usar sus funciones de
manera sencilla. Es gratuito y tiene una amplia distribución, al figurar en la top list de
los lenguajes de programación más utilizados.
Hay materias de matemáticas y de física del Ciclo Básico del Colegio de Ciencia y
Tecnología que usualmente sólo se imparten de manera teórica en el aula. Lo ideal es
acompañar estas materias utilizando el aula de cómputo, como parte de las estrategias
de enseñanza-aprendizaje, para explorar soluciones a problemas cambiando parámetros
y graficando. Python es un lenguaje ideal para este propósito, por su sencillez, y este
libro fomenta su uso.
En el caso del Ciclo Superior, la licenciatura en Modelación Matemática cuenta con ma-
terias que requieren forzosamente del aula de cómputo, como es el caso de las materias
de Programación Avanzada, Modelación Matemática y Computacional I y II, Métodos
Numéricos en Ecuaciones Diferenciales y Álgebra Lineal Numérica, pero también ma-
terias como la de Optimización, Álgebra Lineal Avanzada, Estadística I, entre otras, se
ven beneficiadas por el uso de software dirigido a los temas que se ven en esas materias.
Este libro fomenta el uso de un lenguaje abierto con bibliotecas especializadas para
tratar algunos temas, contra el uso de softwares de paga. En particular, además de la
graficación, en este libro se toca el tema de resolución numérica de Ecuaciones Diferen-
ciales, que se encuentran en los temarios actuales de Métodos Numéricos en Ecuaciones
Diferenciales y Modelación Matemática y Computacional I, el tema de Estadística, que
se encuentra en los temarios de Estadística y probabilidad (Ciclo Básico), Estadística
I y más materias optativas de esa línea, el tema de Regresión Lineal, que se ve en los
temarios de diversas materias (Álgebra Lineal Numérica, Probabilidad II, Estadística
II, laboratorios de Física), y, por supuesto, el tema de Álgebra Lineal, para todas las
materias de esa área. Los temas abarcados en este libro, sin embargo, no son de uso
exclusivo de la licenciatura en Modelación Matemática, pues son lo bastante generales
como para ser de interés también para las ingenierías y algunos de los posgrados.
El lector encontrará, adicionalmente, módulos de introducción a tratamientos de datos
y de machine learning, tan necesarios en la actualidad, que serán de utilidad no sólo
para los estudiantes, sino también para los profesores del Colegio.
No hay, hasta el momento, libros publicados en la UACM en relación con este lenguaje
tan extendido. El abordaje de los temas es con aplicación directa con ejemplos, dejando
ejercicios de práctica para el lector. El lenguaje del libro es sencillo, de fácil acceso para
el usuario.
Índice general
1. Introducción 13
1.1. Instalación de la distribución Anaconda . . . . . . . . . . . . . . . . . . 13
2. Lo básico de Python 17
2.1. Comentarios en el programa . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2. Operaciones con números . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3. Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.3.1. Importación de módulos . . . . . . . . . . . . . . . . . . . . . . 19
2.3.2. El módulo math . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3.3. El módulo random . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4. Cadenas (strings) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.4.1. Concatenación, slicing y len() . . . . . . . . . . . . . . . . . . . 23
2.4.2. Imprimir el resultado de una operación como cadena . . . . . . 24
2.5. Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.5.1. Copia de una lista . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.6. Booleanos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.7. Condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.8. Bucles for (for loops) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.8.1. for i in range(a,b) . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.8.2. for i in A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.8.3. for i,j in enumerate(A) . . . . . . . . . . . . . . . . . . . . . . . 33
2.8.4. for i,j in zip(A,B) . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.8.5. Llenar una lista, lista por comprensión . . . . . . . . . . . . . . 34
2.9. Bucles while (while loops) . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.10. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.10.1. Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.11. Entradas externas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.11.1. Inputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.11.2. Leer archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4. Numpy 57
9
10 ÍNDICE GENERAL
5. Imágenes y gráficas 73
5.1. [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.1.1. Gráficas en dos dimensiones: plot y scatter . . . . . . . . . . . . 74
5.1.2. Subfiguras y gráficas con estilo orientado a objetos . . . . . . . 77
5.1.3. Histogramas, hist . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.1.4. Gráficas de barra: bar y barh . . . . . . . . . . . . . . . . . . . 81
5.2. Imágenes y proyecciones en el plano . . . . . . . . . . . . . . . . . . . . 83
5.2.1. Leer y mostrar imágenes: imread e imshow . . . . . . . . . . . . 83
5.2.2. Heat maps y curvas de nivel: imshow, contour y pcolormesh . . 86
5.3. Gráficas en tres dimensiones . . . . . . . . . . . . . . . . . . . . . . . . 90
5.3.1. Puntos en 3D: scatter3D . . . . . . . . . . . . . . . . . . . . . . 90
5.3.2. Curvas en 3D: plot3D . . . . . . . . . . . . . . . . . . . . . . . 91
5.3.3. Superficies en 3D: plot_surface . . . . . . . . . . . . . . . . . . 91
5.4. Animaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7. Pandas 125
7.1. Lectura y exploración de datos . . . . . . . . . . . . . . . . . . . . . . . 125
7.2. Agregación, filtrado, agrupamiento . . . . . . . . . . . . . . . . . . . . 131
7.2.1. Ejercicio de práctica: coronavirus . . . . . . . . . . . . . . . . . 135
Bibliografía 177
1 Introducción
13
14 CAPÍTULO 1. INTRODUCCIÓN
Figura 1.2: Editor (izquierda), figuras (arriba, derecha) y terminal (abajo, derecha) de
spyder)
En este capítulo damos los elementos básicos para programar en Python. Los objetivos
de aprendizaje son que el lector aprenderá a operar con números, cadenas y booleanos
en este lenguaje. Aplicará estructuras condicionales y ciclos for y while para resolver
problemas. Conocerá y aplicará en problemásticas la estructura de listas. Aprenderá a
definir funciones que son una de las bases fundamentales en programación, al poder ser
llamadas cada vez que se requieran, sin tener que repetir líneas de código. Aprenderá a
importar módulos, que son bibliotecas que contienen funciones para ciertas aplicaciones,
y operará con algunas de ellas. Aprenderá a interaccionar con entradas de usuarios
externos y a leer archivos de texto para procesar la información.
Como ya se mencionó en la Introducción, al trabajar en el editor se deben mandar
imprimir los resultados a la terminal con print(). En este libro, la notación In [n]
se refiere a lo que ingresamos en la computadora, el input, y Out[n] es la respuesta, el
output. Cuando una expresión de código es muy larga como para necesitar más de un
renglón, se usa el salto de línea “\”.
17
18 CAPÍTULO 2. LO BÁSICO DE PYTHON
4 int
1 In [2]:
2 print ( type (3.14) )
3 Out [2]:
4 float
Las operaciones básicas son la suma (+), resta (−), multiplicación (∗), división (/),
división entera (//), potenciación (∗∗) y el módulo o residuo ( %):
1 In [3]:
2 print (4+5 , 4 -3 , 2*4 , 16/3 , 16//3 , 4**3 , 7 %3)
3 Out [3]:
4 9 1 8 5.33 33333333 33333 5 64 1
Como podemos ver de este ejercicio, se pueden realizar varias operaciones en un solo
renglón, separando con comas las operaciones.
En este último ejercicio definimos dos variables en un renglón con un sólo signo de
igual. Podemos hacer lo mismo, en un renglón, dividiendo con un punto y coma cada
definición. Si se definen variables en renglones separados, no es necesario el punto y
coma:
1 In [5]:
2 a = 2; b = 3
3 c = 4
4 print ( a * b * c )
5 Out [5]:
6 24
Ejercicios:
1. Calcula aproximadamente cuántos días han transcurrido desde el Big Bang, si la
edad del Universo, según los científicos, es de 13.8 × 109 años.
2.3. Módulos
2.3.1. Importación de módulos
Hay funciones y operaciones especiales que están guardadas en módulos. El Python
básico tiene instalados unos módulos por default, y hay otros muchos más que vienen
con la versión de Anaconda. Si se requirieran de otros, el usuario tiene la libertad de
instalar más. En Anaconda, la forma usual es ir a la terminal, Anaconda prompt, y
teclear conda install nombre_módulo, aunque, hay siempre excepciones y en esos
casos es mejor buscar en Google la manera correcta de instalar el módulo.
La forma simple de llamar a un módulo es con import nombre_módulo. Para usar
una función, una constante, o un submódulo dentro del módulo, hay que anteponer
el nombre de éste. Sin embargo, hay veces que las funciones dentro del módulo son
empleadas frecuentemente, y es preferible abreviar el nombre de éste. Para los módulos
más usados hay ya convenciones establecidas, por ejemplo, el módulo numpy, que sirve
para operaciones matriciales y vectores en general, se abrevia np. Otro módulo muy
utilizado es el [Link] para graficar, que se abrevia como plt. La forma
más general de llamar a un módulo es entonces, import nombre_módulo as alias.
Por ejemplo, el número e está dentro del módulo math:
1 In [1]:
2 import math
3 print ( math . e )
4 Out [1]:
5 2.71 828182845 9045
En cada módulo, las funciones que empiezan con guión bajo son métodos internos.
Las funciones que vamos a utilizar son el resto, y en lenguaje de programación a estas
funciones se les llama atributos o métodos. En general, los atributos son constantes o
descripciones del objeto con el que estamos trabajando y no llevan paréntesis, mientras
que los métodos son considerados como acciones, y llevan paréntesis, tal cual como
trabajamos con funciones en matemáticas. Por ejemplo:
1 In [2]:
2 print ( math . sin ( math . pi /2) )
3 Out [2]:
4 1.0
Para ver para qué sirven algunas de estas funciones, podemos usar la función help(),
ya sea para todo el módulo, o nada más para la función en la que estamos interesados,
sin los paréntesis de la función al final. Por ejemplo:
1 In [2]:
2 print ( help ( random . sample ) )
3 Out [2]:
4 Help on method sample in module random :
5
6 sample ( population , k ) method of random . Random instance
7 Chooses k unique random elements from a population sequence or set
.
8 Returns a new list containing elements from the population while
leaving the original population unchanged . The resulting list is
in selection order so that all sub - slices will also be valid random
samples . This allows raffle winners ( the sample ) to be
partitioned into grand prize and second place winners ( the
subslices ) .
9 Members of the population need not be hashable or unique . If the
population contains repeats , then each occurrence is a possible
selection in the sample .
10 To choose a sample in a range of integers , use range as an
argument .
11 This is especially fast and space efficient for sampling from a
large population : sample ( range (10000000) , 60)
Los métodos que más suelen usarse son seed, que es para poder reproducir nuestros
cálculos en cada corrida del programa, random, randint, randrange, uniform, gauss,
sample, shuffle, entre otros.
Ejercicios:
1. Checa las diferencias entre [Link], [Link] y [Link],
y selecciona la que más te guste o la que te sirva para seleccionar un número aleatorio
entre el 1 y el 100.
2. Escoge el número 7 como semilla, [Link](7), y después selecciona un núme-
ro de entre la serie (7, 11, 15), [Link]((7,11,15)). Corre el programa varias
veces, ¿qué sucede?
1 In [1]:
2 s = " Hola "
3 print ( dir ( s ) )
4 Out [1]:
5 [ ’ __add__ ’ , ’ __class__ ’ , ’ __contains__ ’ , ’ __delattr__ ’ , ’ __dir__ ’ , ’
__doc__ ’ , ’ __eq__ ’ , ’ __format__ ’ , ’ __ge__ ’ , ’ __getattribute__ ’ , ’
__getitem__ ’ , ’ __getnewargs__ ’ , ’ __getstate__ ’ , ’ __gt__ ’ , ’ __hash__
’ , ’ __init__ ’ , ’ __i nit_subc lass__ ’ , ’ __iter__ ’ , ’ __le__ ’ , ’ __len__ ’
, ’ __lt__ ’ , ’ __mod__ ’ , ’ __mul__ ’ , ’ __ne__ ’ , ’ __new__ ’ , ’ __reduce__ ’
, ’ __reduce_ex__ ’ , ’ __repr__ ’ , ’ __rmod__ ’ , ’ __rmul__ ’ , ’ __setattr__
’ , ’ __sizeof__ ’ , ’ __str__ ’ , ’ __subclasshook__ ’ , ’ capitalize ’ , ’
casefold ’ , ’ center ’ , ’ count ’ , ’ encode ’ , ’ endswith ’ , ’ expandtabs ’ , ’
find ’ , ’ format ’ , ’ format_map ’ , ’ index ’ , ’ isalnum ’ , ’ isalpha ’ , ’
isascii ’ , ’ isdecimal ’ , ’ isdigit ’ , ’ isidentifier ’ , ’ islower ’ , ’
isnumeric ’ , ’ isprintable ’ , ’ isspace ’ , ’ istitle ’ , ’ isupper ’ , ’ join ’ ,
’ ljust ’ , ’ lower ’ , ’ lstrip ’ , ’ maketrans ’ , ’ partition ’ , ’
removeprefix ’ , ’ removesuffix ’ , ’ replace ’ , ’ rfind ’ , ’ rindex ’ , ’ rjust
’ , ’ rpartition ’ , ’ rsplit ’ , ’ rstrip ’ , ’ split ’ , ’ splitlines ’ , ’
startswith ’ , ’ strip ’ , ’ swapcase ’ , ’ title ’ , ’ translate ’ , ’ upper ’ , ’
zfill ’]
e igual que con los módulos, usamos help(str.método), para ver la información sobre
el método:
1 In [6]:
2 print ( help ( s . split ) )
3 Out [6]:
4 Help on built - in function split :
2.4. CADENAS (STRINGS) 23
5
6 split (...) method of builtins . str instance
7 S . split ( sep = None , maxsplit = -1) -> list of strings
8
9 Return a list of the words in S , using sep as the
10 delimiter string . If maxsplit is given , at most maxsplit
11 splits are done . If sep is not specified or is None , any
12 whitespace string is a separator and empty strings are
13 removed from the result .
Ejercicios:
Explora los métodos de las cadenas y responde las siguientes preguntas.
Dada la cadena
s = "tres tristes tigres tragaban trigo, en tres tristes trastos \
sentados en un trigal"
• cuenta el número de letras "t" en la cadena
• encuentra el índice de la sub-cadena "traga"
• reemplaza los espacios sencillos en blanco por tres guiones, "---"
Para saber la longitud de la cadena, existe una función que se usa en general para
cualquier iterable en Python, len(). Pero no es necesario saber la longitud de la cadena
si queremos imprimir un pedazo de la cadena que abarque hasta el final, se puede omitir
el límite superior en este caso:
1 In [3]:
2 print ( s [7: len ( s ) ])
3 print ( s [7:])
4 Out [3]:
24 CAPÍTULO 2. LO BÁSICO DE PYTHON
5 y ficha roja
6 y ficha roja
Podemos también contar para atrás, nada más que en este caso la numeración empieza
desde −1, que es equivalente a len(cadena)-1. En general, el índice -i equivale a
len(cadena)-i,
1 In [4]:
2 print ( s [ -7: -2])
3 Out [4]:
4 ’ ha ro ’
Ejercicios:
1. Dada la cadena s=”ferrocarrilero”, a) selecciona las primeras tres letras, b) se-
lecciona las últimas cuatro letras, c) encuentra cuántas letras tiene la cadena.
1 In [2]:
2 # redondear un resultado a dos cifras :
3 x , y = 35.87 , 24.32
4 print ( " z = xy : " , round ( x *y ,2) )
5 Out [2]:
6 z = xy : 872.36
1 In [3]:
2 print ( " a los " + str (2*3) + " a ñ os se enamor ó , luego a los " + str (6+1) \
3 + " , pues se cas ó , lo que ten í a que pasar pas ó , a los " + str (4*2) + \
4 " a ñ os pap á result ó " )
5 Out [3]:
6 a los 6 a ñ os se enamor ó , luego a los 7 , pues se cas ó , lo que ten í a que
pasar pas ó , a los 8 a ñ os pap á result ó
Podemos hacer uso también del método format de las cadenas. En esencia, lo que
queramos que aparezca en la cadena se encierra entre llaves, y hay opciones para obtener
ciertos formatos, como alinear a la izquierda, derecha, centrado, ocupar un cierto número
de caracteres como cuando se llena una tabla, entre otras.
1 In [4]:
2 import random
3 print ( " tengo {} tomates , {} manzanas y {} \
4 calabazas " . format ( random . randint (1 ,10) , random . randrange (1 ,4) ,\
5 random . choice ((2 ,7 ,8) ) ) )
6 Out [4]:
7 ’ tengo 2 tomates , 2 manzanas y 7 calabazas ’
2.5. Listas
Las listas son el tipo de bases de datos más básico y utilizado en Python. Pueden tener
cualquier tamaño y son mutables, lo que significa que, si aplicamos un método, la lista se
modifica permanentemente. Los elementos de las listas no tienen que ser necesariamente
del mismo tipo, se pueden mezclar números, cadenas, listas dentro de la lista, etc. Se
pueden definir usando corchetes o con la función list()
1 In [1]:
2 A = []
3 B = list ()
1 In [3]:
2 A . append (5)
3 print ( A )
4 Out [3]:
5 [5]
Podemos ordenarla,
1 In [6]:
2 A . sort ()
3 print ( A )
4 Out [6]:
5 [2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 11]
Finalmente, de la misma forma que en las cadenas, además de índices positivos podemos
acceder también a algún elemento empezando a contar desde −1 desde el final de la
lista:
2.5. LISTAS 27
1 In [10]:
2 print ( A [7] , A [5] , A [ -1] , A [ -3])
3 Out [10]:
4 11 8 11 8
Ejercicios:
Dado la lista
A = [11, 7, 5, 3, 9, 17, 8, 4, 2, 12, 13]
agrega el número 27 a la lista
agrega los elementos [16,20,31] a la lista
borra el número 8 de la lista
borra el elemento con índice 5 de la lista
inserta de nuevo el número 8 en el índice 2
encuentra el índice que tiene el número 12 en la lista
ordena la lista
encuentra la longitud total de la lista
Vemos que asignar un valor diferente a alguna entrada no modifica el resto de las listas,
pero el modificar una sublista sí se replica en el resto, así que hay que tener otra forma
de hacer copias. Para ello está la función deepcopy del módulo copy:
1 In [4]:
2 from copy import deepcopy
3 A = [[3 ,5] ,4 ,[8 ,6]]
4 B = deepcopy ( A ) # copiamos
5 A [0] = [48 ,49] # cambiamos una entrada de una matriz
6 B [2]. append (7) # agregamos un elemento a una entrada de una matriz
7 print ( " A : " ,A )
8 print ( " B : " ,B )
9 Out [4]:
10 A : [[48 , 49] , 4 , [8 , 6]]
11 B : [[3 , 5] , 4 , [8 , 6 , 7]]
2.6. Booleanos
El siguiente tipo de variable es el booleano, bool, que admite dos valores, True y
False. Las operaciones con booleanos son and, or y not. Las tablas de verdad son
muy sencillas. La operación and con dos afirmaciones sólo es verdadera cuando las dos
afirmaciones son ciertas, mientras que el resultado con el operador or sólo es falso
cuando las dos afirmaciones son falsas. El operador not, por su parte, cambia el sentido
de una afirmación. Las operaciones se muestran en la tabla 2.1:
2.7. CONDICIONALES 29
P1 P2 P1 and P2 P1 or P2 P not P
True True True True True False
True False False True False True
False True False True
False False False False
Tabla 2.1: Tablas de verdad
Obtenemos variables booleanas, por ejemplo, cuando comparamos objetos. Las compa-
raciones posibles son >, <, >=, <=, ==, != (diferente a):
1 In [2]:
2 print (5 >4 , 5 >=4 , 5 <4 , 5 <=4 , 5==4 , 5!=4)
3 print ( type (5==2) )
4 Out [2]:
5 True True False False False True
6 Bool
Otra forma de obtener una variable booleana es con el operador in, si queremos saber
por ejemplo si un objeto se encuentra dentro de una iterable:
1 In [3]:
2 print ( " orro " in " cotorro " )
3 Out [3]:
4 True
Ejercicios:
¿Cuáles son los resultados de las siguientes comparaciones? Inténtalo primero con lápiz
y papel, y después en Python:
a)("María">"Victoria") or ((3**3<2**5) and (17%5=="almeja".count("a")))
b) la longitud de la palabra “Aguascalientes” es igual que la división de 234 entre 17.
c) (not "Hola!".isalnum()) and "pedro".replace("d","r")=="perro"
2.7. Condicionales
Los condicionales son enunciados que nos permiten arrojar un resultado dependiendo
de la condición dada. En español sería, SI cond1, haz esto, O SI cond2, haz aquello,
etc., hasta llegar a DE OTRA FORMA, haz esto otro.
En Python, se comienza con if para la primera condición, el número necesario de
elif (puede no haber) para otras posibles condiciones, y, finalmente (no obligatorio),
el else para el resto de posibilidades no comprendidas en el if y elif. La notación
30 CAPÍTULO 2. LO BÁSICO DE PYTHON
consiste en escribir la condición seguida de dos puntos (:), y en el siguiente renglón, con
indentación, lo que se va a ejecutar dada la condición. Hay que subrayar que, en Python,
la indentación es muy importante. Después de los dos puntos hay una indentación por
default de cuatro espacios, o un tab, en el siguiente renglón, que se aplica solito en el
editor. No necesariamente debe ser ese número de espacios, aunque una vez seleccionado
el espaciamiento, el resto del cuerpo debe quedar alineado de manera acorde, los if,
elif y else a la misma altura, y las diferentes acciones a la misma altura entre ellas:
1 In [1]:
2 import random
3 # seleccionamos un entero entre el 1 y el 100
4 a = random . randint (1 ,100)
5 if a %3 == 0:
6 print (a , " es divisible entre 3 " )
7 elif a %3==1:
8 print ( " al dividir " ,a , " entre 3 , el residuo es 1 " )
9 else :
10 print ( " el residuo de la divisi ó n de " ,a , " entre 3 , es 2 " )
11 Out [1]:
12 57 es divisible entre 3
Ejercicios:
En una cadena escribe tu nombre (uno solo). Si la longitud de tu nombre es mayor a
7, imprime “tu nombre es algo largo”, más tu nombre; si es menor que 7, imprime “tu
nombre es algo corto”, más tu nombre, y si es igual a 7, imprime “tu nombre tiene la
justa medida”, más tu nombre.
1 In [1]:
2 # range ( inicio , final , paso ) , i es el í ndice :
3 for i in range (2 ,11 ,3) :
4 print ( " soy el n ú mero " ,i , " y mi cuadrado es " , i **2)
5 Out [1]:
6 soy el n ú mero 2 y mi cuadrado es 4
7 soy el n ú mero 5 y mi cuadrado es 25
8 soy el n ú mero 8 y mi cuadrado es 64
1 In [2]:
2 print (6* " ma " + " gui quero " )
3 # range ( s ó lo final ) , no hay necesidad de un i expl í cito , se puede usar
4 # el gui ó n bajo , por convenci ón , pero no es obligatorio !:
5 for _ in range (2) :
6 print ( " mamagui quero " )
7 print ( " comer " )
8 Out [2]:
9 mamamamamamagui quero
10 mamagui quero
11 mamagui quero
12 comer
Entonces:
1 In [3]:
2 k =0 # k es la aproximaci ó n a π ( multiplicada por 4)
3 for n in range (0 ,10) :
4 k = k + ( -1) ** n /(2* n +1)
5 # para imprimir en cada iteraci ó n la aproximaci ó n a π:
6 print (n ,4* k )
7 Out [3]:
8 0 4.0
9 1 2 .6666666 66666667
10 2 3 .4666666 66666667
11 3 2. 89 523 80 95 238 09 56
12 4 3. 33 968 25 39 682 54 03
13 5 2. 97 604 61 76 046 17 65
14 6 3. 28 373 84 83 738 48 44
15 7 3 .0170718 17071818
16 8 3. 25 236 59 34 718 87 67
17 9 3. 04 183 96 18 929 40 32
Ejercicios
1. En el ejemplo de la aproximación a π, ¿qué pasaría si la asignación k = 0 se hiciera
dentro del ciclo for?, ¿qué pasa si no se asigna un valor a k de inicio?, ¿dónde hay
que colocar el print si sólo se quiere imprimir el resultado final (último término de la
32 CAPÍTULO 2. LO BÁSICO DE PYTHON
iteración)? ¿Se puede imprimir la variable n definida para el range fuera del bucle for?
Si la fórmula no fuera una suma de términos sino un producto, iterativo, ¿cuál debe ser
la asignación inicial de k?
2.8.2. for i in A
El otro tipo básico de for es el que itera por elemento en una secuencia. De igual forma,
en for i in A, el elemento i puede tener cualquier nombre.
1 In [1]:
2 A = [ " elefante " , " le ó n " , " tar á ntula " , " escorpi ó n " , " rata " , " gato " , \
3 " perro " , " burro " , " tigre " ]
4 t = " tiene un nombre largo ! "
5 for j in A : # j se refiere a cada palabra de A
6 if len ( j ) >6:
7 if j [ -1]== " a " : # si la palabra termina en a
8 s = " la "
9 else : # de otra forma
10 s = " el "
11 print (s ,j , t )
12 Out [1]:
13 el elefante tiene un nombre largo !
14 la tar á ntula tiene un nombre largo !
15 el escorpi ó n tiene un nombre largo !
Ejercicios:
Dada la siguiente lista de animales:
A = ["canguro", "serpiente", "león", "jaguar", "caballo", "puma",\
"tigre", "venado", "alce", "reno", "gato", "perro", "ardilla",\
"mapache", "zorro", "iguana", "rana", "lobo", "lémur", "leopardo",\
"zopilote", "mariposa", "ballena", "tiburón", "piraña", "coyote",\
"armadillo", "tortuga"]
* ordena la lista en orden alfabético
* encuentra la palabra más larga, rompe empates arbitrariamente
* para cada palabra, encuentra su número de vocales y consonantes
2.8. BUCLES FOR (FOR LOOPS) 33
Ejercicios:
1. Imprime el resultado del ejemplo resuelto usando un for con range.
2. Dada la siguiente lista:
1 A = [ " Beethoven " , " Mozart " , " Tchaikovsky " , " Saint - Saenz " , " Wagner " ,\
2 " Verdi " , " Bizet " , " Vivaldi " , " Rossini " , " Schubert " , " Grieg " , " Bach " ,\
3 " Ravel " , " Mahler " , " Berliotz " , " Mussorgsky " , " Dvorak " , " Borodin " ,\
4 " Gershwin " , " Williams " , " Moncayo " , " Offenbach " , " Strauss " ]
Ejercicios:
1. Dadas las siguientes listas:
1 comp = [ " Beethoven " , " Mozart " , " Tchaikovsky " , " Saint - Saenz " , " Wagner " ,
" Verdi " ]
2 a ñ o = [1770 , 1756 , 1840 , 1835 , 1813 , 1813]
3 pa í s =[ " Alemania " ," Austria " ," Rusia " ," Francia " ," Alemania " ," Italia " ]
4 pieza = [ " Sinfon í a no . 5 " , " Concierto para piano no . 21 " ,\
5 " Obertura 1812 " , " Sinfon í a no . 3 ( con ó rgano ) " ," Tannhauser " ,\
6 " El trovador " ]
manda a imprimir, por cada compositor, un enunciado del tipo, fulanito nació en año
en país y compuso pieza.
pero hay una forma más compacta y rápida que se conoce como list comprehension,
lista por comprensión, donde los corchetes resguardan al elemento de la lista seguido
del for:
1 In [2]:
2 B = [4* i +3 for i in range (10) ]
3 print ( B )
4 Out [2]:
5 [3 , 7 , 11 , 15 , 19 , 23 , 27 , 31 , 35 , 39]
entonces a utilizar el método choice() del módulo random para agregar uno a uno los
símbolos para conformar la cadena larga:
1 In [1]:
2 import random
3 random . seed (5)
4 # escogemos un n ú mero como semilla , seed , en caso de que queramos
5 # reproducir nuestro resultado
6 s = " " ; N = 20000
7 # concatenamos con + para formar la cadena
8 for i in range ( N ) :
9 s = s + random . choice ( " erika " )
Respuesta.
Recorremos la cadena s desde el índice 0, para buscar el nombre, pero paramos algo
antes del final, porque el nombre tiene un cierto tamaño, en nuestro ejemplo, 5. Haciendo
slicing en la cadena, comparamos esa sub-cadena con el nombre. En caso de éxito,
imprimimos el índice:
1 In [2]:
2 for i in range (N -5+1) : # paramos antes del final
3 if s [ i : i +5]== " erika " : # si la subcadena es igual al nombre
4 print ( i ) # imprime el í ndice
5 Out [2]:
6 12558
7 19004
13 4 3
14 5 5
15 6 8
16 7 13
17 8 21
18 9 34
19 10 55
20 11 89
21 12 144
22 13 233
23 14 377
24 15 610
25 16 987
26 17 1597
27 18 2584
28 19 4181
Respuesta
Para encontrar el máximo y el mínimo de una iterable, Python tiene las funciones max()
y min(). Sin embargo, el problema nos pide encontrar dónde se localizan estos números
en la lista A. Chequemos como quiera de una vez cuáles son el máximo y el mínimo:
1 In [1]:
2 print ( max ( A ) , min ( A ) )
3 Out [1]:
4 (996 , 2)
Ahora sí, para resolver este tipo de problema, la forma usual es recorrer la lista com-
parando cada nuevo elemento con el máximo y mínimo que encontramos en los pasos
previos. Para ello, definamos un mínimo y un máximo de inicio, y, cada que encon-
tremos un número más pequeño que nuestro mínimo, o un número más grande que
nuestro máximo, los actualizamos, así como los índices respectivos. Empezamos dando
un número grande al mínimo, fácil de vencer a la primera, y un número pequeño al
máximo:
1 In [2]:
2 maximus , minimus = 0 , 200000
3 indmax , indmin = 0 , 0
4 for ind , i in enumerate ( A ) :
5 if i > maximus : # si i es mayor al valor de maximus actual
6 maximus = i # actualizamos maximus y actualizamos
2.9. BUCLES WHILE (WHILE LOOPS) 37
3 while True :
4 a = random . randint (1 ,10)
5 print ( " yo ten í a " , a , " perritos " )
6 if a ==1:
7 break
8 Out [2]:
9 yo ten í a 8 perritos
10 yo ten í a 3 perritos
11 yo ten í a 7 perritos
12 yo ten í a 6 perritos
13 yo ten í a 5 perritos
14 yo ten í a 3 perritos
15 yo ten í a 6 perritos
16 yo ten í a 10 perritos
17 yo ten í a 7 perritos
18 yo ten í a 7 perritos
19 yo ten í a 3 perritos
20 yo ten í a 1 perritos
Ejercicios:
Dada la lista
In [1]:
import random
[Link](3)
# una forma de tomar 150 números aleatorios del 0 al 499, sin repetir
A = [Link](range(500),150)
guarda en otra lista los primeros 25 números impares y manda a imprimirla.
2.10. Funciones
Las funciones se escriben de manera muy sencilla en Python. La notación es
def nombrefunción(argumentos):
cuerpo de la función
return el resultado deseado
Como en los casos anteriores, la notación es la de 2 puntos y la indentación, preferen-
temente de 4 espacios (un tab). Dependiendo del objetivo de la función, puede existir
el return o no, si es que se quiere devolver un valor.
Definamos por ejemplo una función que encuentre la suma de 2 números:
1 In [1]:
2 def suma (a , b ) :
3 return ( a + b ) # los par é ntesis son opcionales
4 print ( suma (5 ,3) )
5 Out [1]:
6 8
2.10. FUNCIONES 39
Pero si pones el return solito en la función, aunque no lo requiera, no hay error, como
quiera termina el algoritmo.
Podemos también devolver más de un resultado, usando lo que se llama tupla y que
mencionaremos más adelante, pero que básicamente consiste en guardar más de un
objeto entre paréntesis. Por ejemplo, podemos definir una función que encuentre la
suma y la resta de 2 números:
1 In [4]:
2 def sumaresta (a , b ) :
3 return ( a +b , a - b )
4 print ( sumaresta (5 ,3) )
5 Out [4]:
6 (8 , 2)
Si queremos operar con estos resultados, es mejor guardarlos por separado. A eso se le
llama "desempacar":
1 In [5]:
2 suma , resta = sumaresta (7 ,8)
3 print ( " ejercicio : la suma de dos n ú meros es " , suma , " y la resta es " ,\
4 resta , " , cu á les son los n ú meros ? " )
5 Out [5]:
6 ejercicio : la suma de dos n ú meros es 15 y la resta es -1 , cu á les son
los n ú meros ?
Podemos también definir la función con valores de argumentos que vengan por default.
En ese caso y si no se quiere cambiar esos valores, no se requiere poner esos argumentos
cuando se llama a la función. Pero si uno quiere cambiar el valor de alguna variable, se
puede hacer, o poniendo el nombre de la variable y su valor en el argumento, o dando
sólo su valor en el orden que le corresponde, si esto es posible.
40 CAPÍTULO 2. LO BÁSICO DE PYTHON
Por ejemplo, digamos que tenemos la ecuación del gas ideal, P V = nRT , y queremos
calcular el valor del volumen dado el número de moles, suponiendo que tenemos la
presión y temperatura estándar de 1atm = 1.013 × 105 Pa y 20◦ C = 293K. Más aún,
podemos usar las dos unidades usuales, las del SI, usando m3 para el volumen y Pa
para la presión, con la constante del gas ideal R = 8.314 J/(mol K), o la de litro-
atmósfera, con R = 0.08206 l atm/(mol K). Podemos escoger por default las unidades
de litro-atmósfera, y entonces nuestra ecuación queda:
1 In [6]:
2 def volumen (n , P =1 , T =293 , R =0.08206) :
3 return ( n * R * T / P )
4 print ( " V en litros , n =10 , a T y P est á ndar : " )
5 print ( round ( volumen (10) ,3) )
6 print ( " V en litros , n =10 , T =27 C : " )
7 print ( round ( volumen (10 , T =300) ,3) )
8 print ( " V en m ^3 , n =10 , T =14 C , P =77 ,650 Pa ( CdMex d í a nublado ) : " )
9 print ( round ( volumen (10 , T =287 , P =77650 , R =8.314) ,3) )
10 # lo mismo de arriba , pero en litros , las variables se dan en orden
11 print ( " V en litros , n =10 , P =0.767 atm , T =14 C ( igual que arriba ) : " )
12 print ( round ( volumen (10 ,0.767 ,287) ,3) )
13 Out [6]:
14 V en litros , n =10 , a T y P est á ndar :
15 240.436
16 V en litros , n =10 , T =27 C :
17 246.18
18 V en m ^3 , n =10 , T =14 C , P =77 ,650 Pa ( CdMex d í a nublado ) :
19 0.307
20 V en litros , n =10 , P =0.767 atm , T =14 C ( igual que arriba ) :
21 307.056
Ejercicios:
1. Ecuación de segundo grado
Escribe una función que devuelva las soluciones de una ecuación de segundo grado,
dando como argumentos los coeficientes reales de la variable al cuadrado, de la variable
lineal, y la constante, en ese orden. En lugar de usar el módulo math para la raíz
cuadrada, llama al módulo cmath, para manejar también soluciones imaginarias.
2. Productos entre vectores
a) Dados 2 vectores A y B del mismo tamaño, escribe una función que calcule su
producto punto
b) Dados 2 vectores A y B en 3 dimensiones, escribe una función que calcule su producto
cruz
2.10.1. Recursividad
En algunos casos, podemos tener una función que se llame a sí misma. A esto se le llama
recursión. Como el número de llamadas no se puede hacer infinito, tenemos que tener
uno o más casos base para los cuales salgamos de la función y retornemos un resultado.
Como ejemplo, veamos el caso típico del factorial.
2.11. ENTRADAS EXTERNAS 41
Sabemos que n! = n(n − 1)(n − 2) . . . (1) = n(n − 1)!, y que 0! = 1. Podemos definir
una función recursiva de la siguiente forma:
1 In [1]:
2 def factorial ( n ) :
3 if n ==0:
4 return (1)
5 return ( n * factorial (n -1) )
6 print ( factorial (5) )
7 Out [1]:
8 120
En este ejemplo, la función se llama así misma pero con el valor del argumento reducido
en una unidad. Cuando finalmente n = 0, la función ya no se vuelve a llamar a sí misma,
devuelve 1 y se termina el cálculo.
Ejercicios:
Suma de los primeros n números naturales. Define una función que encuentre la
suma de los primeros n números naturales, con la fórmula que encontró Gauss cuando
estaba chiquito. Escribe otra función que encuentre la suma recursivamente. Encuentra
la suma de los primeros 250 números naturales usando las dos funciones.
1 In [2]:
2 b = int ( input ( " dame otro n ú mero " ) )
3 dame otro n ú mero 3
1 In [3]:
2 print ( " la suma de tus n ú meros es " , a + b )
3 Out [3]:
4 la suma de tus n ú meros es 8
Ejercicios:
Escribe un archivo donde la computadora juegue con un usuario piedra, papel o tijeras.
La computadora puede hacer su selección arbitrariamente usando
[Link](secuencia) del módulo random. Trata de convertir el input del usuario
a mayúsculas o minúsculas, con el método lower() o el upper(), para que no importe
cómo ingrese su respuesta el usuario. También, mientras éste dé una mala respuesta,
dile que está tecleando mal y pregúntale de nuevo si ¿piedra, papel o tijeras?
5 print ( B )
6 Out [1]:
7 [ ’ buffer ’ , ’ close ’ , ’ closed ’ , ’ detach ’ , ’ encoding ’ , ’ errors ’ , ’ fileno ’
, ’ flush ’ , ’ isatty ’ , ’ line_buffering ’ , ’ mode ’ , ’ name ’ , ’ newlines ’ ,
’ read ’ , ’ readable ’ , ’ readline ’ , ’ readlines ’ , ’ reconfigure ’ , ’ seek ’ ,
’ seekable ’ , ’ tell ’ , ’ truncate ’ , ’ writable ’ , ’ write ’ , ’
write_through ’ , ’ writelines ’]
Vamos a quedarnos con unas cuantas funciones para leer el archivo. Aunque no venga
en dir(), una de las formas más usuales para procesar el texto es línea por línea con
un for. Adicionalmente, para desplegar de manera correcta los acentos vamos a usar el
parámetro encoding = ”utf8”:
1 In [2]:
2 f = open ( " el gato . txt " , encoding = " utf8 " )
3 A = [ line for line in f ]
4 f . close ()
5 print ( A )
6 Out [2]:
7 [ ’ Ello es que hay animales muy cient í ficos \ n ’ , ’ en curarse con varios
espec í ficos \ n ’ , ’y en conservar su construcci ó n org á nica , \ n ’ , ’
como h á biles que son en la Bot á nica ; \ n ’ , ’ pues conocen las hierbas
diur é ticas ,\ n ’ , ’ cat á rticas , narc ó ticas , em é ticas , \ n ’ , ’ febr í
fugas , est í pticas , prol í ficas , \ n ’ , ’ cef á licas tambi é n y sudor í
ficas . \ n ’ , ’ En esto era gran pr á ctico y te ó rico \ n ’ , ’ un gato ,
pedant í simo ret ó rico ,\ n ’ , ’ que hablaba en un estilo tan enf á tico \ n
’ , ’ como el m á s estirado catedr á tico . \ n ’]
Vemos que se formó una lista de líneas, con cada línea terminando en un "\n" que
indica salto de línea. Si revisamos el archivo de texto directamente, no hay nada como
un “\n”, éste nada más nos dice que la siguiente parte del texto se encuentra en la línea
de abajo. Para quitar el “\n”, usamos el método strip() que elimina los espacios en
blanco y saltos de línea del principio y final de la cadena:
1 In [3]:
2 f = open ( " el gato . txt " , encoding = " utf8 " )
3 A =[ line . strip () for line in f ]
4 f . close ()
5 print ( A )
6 Out [3]:
7 [ ’ Ello es que hay animales muy cient í ficos ’ , ’ en curarse con varios
espec í ficos ’ , ’y en conservar su construcci ó n org á nica , ’ , ’ como h á
biles que son en la Bot á nica ; ’ , ’ pues conocen las hierbas diur é
ticas , ’ , ’ cat á rticas , narc ó ticas , em é ticas , ’ , ’ febr í fugas , est í
pticas , prol í ficas , ’ , ’ cef á licas tambi é n y sudor í ficas . ’ , ’ En esto
era gran pr á ctico y te ó rico ’ , ’ un gato , pedant í simo ret ó rico , ’ , ’
que hablaba en un estilo tan enf á tico ’ , ’ como el m á s estirado
catedr á tico . ’]
Siguiendo, vamos a leer ahora la siguiente parte de la fábula, toda de una vez, con
read()
1 In [4]:
2 f = open ( " el lagarto . txt " , encoding = " utf8 " )
3 a = f . read ()
4 f . close ()
44 CAPÍTULO 2. LO BÁSICO DE PYTHON
5 print ( a )
6 Out [4]:
7 Yendo a caza de plantas salut í feras ,
8 dijo a un lagarto : ¡ Qu é ansias tan mort í feras !
9 Quiero , por mis turgencias semi - hidr ó picas ,
10 chupar el zumo de hojas heliotr ó picas .
11 At ó nito el lagarto con lo ex ó tico
12 de todo aquel pre á mbulo estramb ó tico ,
13 no entendi ó m á s la frase macarr ó nica
14 que si le hablasen lengua babil ó nica ;
15 pero not ó que el charlat á n rid í culo
16 de hojas de girasol llen ó el ventr í culo ,
17 y le dijo : Ya , en fin , se ñ or hidr ó pico ,
18 he entendido lo que es zumo heliotr ó pico .
Sólo nos leyó una línea, la cual contiene el "\n". Para leer todo el archivo tendríamos
que ir línea por línea con este método. Intentemos ahora con readlines()
1 In [7]:
2 f = open ( " el grillo . txt " , encoding = " utf8 " )
3 B = f . readlines ()
4 f . close ()
5 print ( B )
6 Out [7]:
7 [ ’¡ Y no es bueno que un grillo , oyendo el di á logo ,\ n ’ , ’ aunque se fue
en ayunas del cat á logo \ n ’ , ’ de t é rminos tan raros y magn í ficos , \ n
’ , ’ hizo del gato elogios honor í ficos ! \ n ’ , ’S í ; que hay quien
tiene la hinchaz ó n por m é rito , \ n ’ , ’y el hablar liso y llano por
dem é rito .\ n ’ , ’\ n ’ , ’ Mas ya que esos amantes de hiperb ó licas \ n ’ , ’
cl á usulas y met á foras diab ó licas , \ n ’ , ’ de retumbantes voces el dep
2.11. ENTRADAS EXTERNAS 45
Este resultado es más parecido al obtenido con el for. Nada más que, con el for se
pueden procesar las líneas de una vez, aprovechando que estamos en el ciclo.
Por completez, dejamos la fábula completa para el lector.
El gato, el lagarto y el grillo, de Tomás de Iriarte
Por más ridículo que sea el estilo retumbante, siempre habrá necios que le aplaudan,
sólo por la razón de que se quedan sin entenderle
Ello es que hay animales muy científicos
en curarse con varios específicos
y en conservar su construcción orgánica,
como hábiles que son en la Botánica;
pues conocen las hierbas diuréticas,
catárticas, narcóticas, eméticas,
febrífugas, estípticas, prolíficas,
cefálicas también y sudoríficas.
En esto era gran práctico y teórico
un gato, pedantísimo retórico,
que hablaba en un estilo tan enfático
como el más estirado catedrático.
Yendo a caza de plantas salutíferas,
dijo a un lagarto: «¡Qué ansias tan mortíferas!
Quiero, por mis turgencias semi-hidrópicas,
chupar el zumo de hojas heliotrópicas».
Atónito el lagarto con lo exótico
de todo aquel preámbulo estrambótico,
no entendió más la frase macarrónica
que si le hablasen lengua babilónica;
pero notó que el charlatán ridículo
de hojas de girasol llenó el ventrículo,
y le dijo: «Ya, en fin, señor hidrópico,
he entendido lo que es zumo heliotrópico».
¡Y no es bueno que un grillo, oyendo el diálogo,
aunque se fue en ayunas del catálogo
de términos tan raros y magníficos,
hizo del gato elogios honoríficos!
Sí; que hay quien tiene la hinchazón por mérito,
y el hablar liso y llano por demérito.
Mas ya que esos amantes de hiperbólicas
cláusulas y metáforas diabólicas,
de retumbantes voces el depósito
apuran, aunque salga un despropósito,
caiga sobre su estilo problemático
este apólogo esdrújulo-enigmático.
46 CAPÍTULO 2. LO BÁSICO DE PYTHON
Ejercicios:
En un documento de texto (.txt) copia y pega una canción que te guste. Imprime en
la consola las cuatro primeras líneas de la canción, sin el título.
3 Estructuras de datos básicos
3.1. Conjuntos
Los conjuntos en Python operan tal cual como los conjuntos en matemáticas. A dife-
rencia de las listas, los conjuntos no están ordenados, sus elementos están como en una
bolsa. Y, por ser conjuntos, no hay elementos repetidos. Podemos construir un conjunto
a partir de un iterable como una lista o una cadena con set():
1 In [1]:
2 A = set ( " Parangaricutirim í cuaro " )
3 print ( A )
4 Out [1]:
5 { ’P ’ , ’a ’ , ’c ’ , ’g ’ , ’i ’ , ’m ’ , ’n ’ , ’o ’ , ’r ’ , ’t ’ , ’u ’ , ’í ’}
Aunque haya unas llaves resguardando a los elementos, los conjuntos vacíos no se ini-
cializan con llaves, como las listas vacías con los corchetes, pues las llaves se usan
también en otra estructura llamada diccionario. Para tener un conjunto vacío entonces,
lo construimos a partir de una lista o una tupla vacía:
1 In [2]:
2 B = set ([])
3 C = set (() )
4 print ( type ( B ) , type ( C ) )
5 Out [2]:
6 < class ’ set ’ > < class ’ set ’ >
47
48 CAPÍTULO 3. ESTRUCTURAS DE DATOS BÁSICOS
Vemos que, en lugar del método append() para agregar un elemento, como en las listas,
aquí se usa add(). Vamos ahora por ejemplo a quitar la "í" de nuestro conjunto A,
pues ya tenemos como representante a la "i":
1 In [4]:
2 A . remove ( " í " )
3 print ( A )
4 Out [4]:
5 { ’r ’ , ’o ’ , ’m ’ , ’P ’ , ’i ’ , ’t ’ , ’c ’ , ’u ’ , ’a ’ , ’g ’ , ’n ’}
Vamos ahora a formar otros dos conjuntos para poder operar con ellos con las reglas
usuales. Por ejemplo, veamos las diferencias entre las funciones de matemáticas básicas
del módulo math y del módulo cmath, el segundo para trabajar con números complejos:
1 In [5]:
2 import math
3 import cmath
4 A = set ([ i for i in dir ( math ) if i [0]!= " _ " ])
5 B = set ([ i for i in dir ( cmath ) if i [0]!= " _ " ])
6 print ( " A : " , A )
7 print ( " B : " , B )
8 Out [5]:
9 A : { ’ tau ’ , ’ atan2 ’ , ’ erfc ’ , ’ exp2 ’ , ’ atan ’ , ’ asinh ’ , ’ inf ’ , ’ prod ’ , ’
ceil ’ , ’ sqrt ’ , ’ acosh ’ , ’ fmod ’ , ’ pow ’ , ’ fsum ’ , ’ cos ’ , ’ log10 ’ , ’
dist ’ , ’ hypot ’ , ’ expm1 ’ , ’ gamma ’ , ’ erf ’ , ’ cbrt ’ , ’ radians ’ , ’ sin ’ ,
’ isinf ’ , ’ pi ’ , ’ factorial ’ , ’ sinh ’ , ’ isnan ’ , ’ frexp ’ , ’ tan ’ , ’ asin ’
, ’ fabs ’ , ’ degrees ’ , ’ log1p ’ , ’ isqrt ’ , ’ log2 ’ , ’ ulp ’ , ’ gcd ’ , ’ nan ’ ,
’ isclose ’ , ’ lcm ’ , ’ tanh ’ , ’ perm ’ , ’e ’ , ’ log ’ , ’ ldexp ’ , ’ cosh ’ , ’
copysign ’ , ’ comb ’ , ’ floor ’ , ’ exp ’ , ’ lgamma ’ , ’ isfinite ’ , ’ nextafter
’ , ’ modf ’ , ’ remainder ’ , ’ atanh ’ , ’ trunc ’ , ’ acos ’}
10 B : { ’ tau ’ , ’ atan ’ , ’ asinh ’ , ’ inf ’ , ’ sqrt ’ , ’ acosh ’ , ’ cos ’ , ’ log10 ’ , ’
polar ’ , ’ sin ’ , ’ isinf ’ , ’ pi ’ , ’ sinh ’ , ’ isnan ’ , ’ tan ’ , ’ asin ’ , ’
phase ’ , ’ nan ’ , ’ isclose ’ , ’ rect ’ , ’ tanh ’ , ’e ’ , ’ log ’ , ’ cosh ’ , ’ exp ’
, ’ nanj ’ , ’ isfinite ’ , ’ infj ’ , ’ atanh ’ , ’ acos ’}
Aunque muchas de las funciones de los dos conjuntos compartan nombre, ¡las funciones
son diferentes! Si usas las funciones de cmath y tienes puros números reales, vas a estar
cargando la parte compleja por todos lados, +0j, es mejor en esos casos quedarse con
math. Teniendo esto claro, sigamos con el ejemplo y, vamos a ver si, como parece a
primera vista, B es un subconjunto de A:
1 In [6]:
2 print ( B . issubset ( A ) )
3 Out [6]:
4 False
Algunas de estas funciones deben ser para la forma polar de los números complejos.
Además de la diferencia, se pueden sustituir otros métodos por símbolos para las ope-
raciones más usadas en conjuntos, como el & (and) para la intersección, el | (or) para
la unión, y el ^ para los elementos que están en A y en B, quitando su intersección.
Probemos esta última:
1 In [8]:
2 print ( A ^ B )
3 Out [8]:
4 { ’ atan2 ’ , ’ erfc ’ , ’ exp2 ’ , ’ prod ’ , ’ ceil ’ , ’ fmod ’ , ’ pow ’ , ’ fsum ’ , ’ dist
’ , ’ hypot ’ , ’ expm1 ’ , ’ gamma ’ , ’ erf ’ , ’ polar ’ , ’ cbrt ’ , ’ radians ’ , ’
factorial ’ , ’ frexp ’ , ’ fabs ’ , ’ degrees ’ , ’ log1p ’ , ’ phase ’ , ’ isqrt ’ ,
’ log2 ’ , ’ ulp ’ , ’ gcd ’ , ’ lcm ’ , ’ rect ’ , ’ perm ’ , ’ ldexp ’ , ’ copysign ’ , ’
comb ’ , ’ floor ’ , ’ lgamma ’ , ’ nanj ’ , ’ nextafter ’ , ’ modf ’ , ’ infj ’ , ’
remainder ’ , ’ trunc ’}
Ejercicios:
Construye una lista de los números que son múltiplos del 4, del 0 al 200, inclusive, y
una lista con los números múltiplos del 3, en el mismo rango. Después, usando métodos
de conjuntos, encuentra los números comunes a las dos listas, sin importar el orden de
aparición.
3.2. Diccionarios
La siguiente estructura de datos, el diccionario, también conocido en general como hash
table, es tan útil como las listas, es otro caballito de batalla. El diccionario consiste en
guardar valores con ciertas claves dadas. Accedemos fácilmente a un valor sólo dando
la clave.
Inicializamos un diccionario con:
1 In [1]:
2 d = dict ()
y también se puede inicializar un diccionario con d={}. Los métodos del diccionario los
encontramos, como siempre, con dir()
1 In [2]:
2 print ([ i for i in dir ( d ) if i [0]!= " _ " ])
3 Out [2]:
4 [ ’ clear ’ , ’ copy ’ , ’ fromkeys ’ , ’ get ’ , ’ items ’ , ’ keys ’ , ’ pop ’ , ’ popitem ’
, ’ setdefault ’ , ’ update ’ , ’ values ’]
Para guardar un valor value en una clave key, se usa d[key]=value, donde la clave
tiene que ser única, y además tiene que ser un objeto inmutable, como una cadena, no
podemos tener listas u objetos que puedan modificarse permanentemente, esto es, la
clave es segura. El valor puede ser cualquier tipo de objeto.
Para acceder al valor de la clave key sólo se llama a d[key] o a [Link](key), que
también nos puede servir para dar una opción de valor en caso de que key no se
encuentre en el diccionario, [Link](key, default_value). Para remover un elemento
50 CAPÍTULO 3. ESTRUCTURAS DE DATOS BÁSICOS
de un diccionario, se usa [Link](key). Para acceder tanto a las claves como a los valores,
se usa [Link](). Por ejemplo:
1 In [3]:
2 d [ " a " ]=1 # asignamos valores
3 d [ " b " ]=2
4 for key , value in d . items () :
5 print ( key , value )
6 Out [3]:
7 b 2
8 a 1
1 In [4]:
2 d [ " b " ]=3 # cambiamos el valor de " b "
3 d . pop ( " a " ) # eliminamos a " a "
4 print ( " d = " ,d ) # imprimimos el diccionario
5 # si no estamos seguros de que una clave pertenezca al diccionario , le
# damos un valor por default
6 print ( " d [ c ]: " ,d . get ( " c " ,0) )
7 # pero si estamos seguros que un elemento est á en el diccionario , lo
8 # llamamos f á cilmente
9 print ( " d [ b ]: " ,d [ " b " ])
10 Out [4]:
11 d = { ’b ’: 3}
12 d [ c ]: 0
13 d [ b ]: 3
El diccionario también puede tener elementos desde el principio, sin que todo lo tenga-
mos que llenar con d[key]=value. La notación, en ese caso, es que los elementos están
separados por coma, y la clave está separada del valor por dos puntos:
1 In [5]:
2 pintar_animal = { " le ó n " : " amarillo " , " tigre " : " naranja " , " elefante " : \
3 " gris " , " pollito " : " amarillo " , " cotorro " : " verde " }
4 print ( pintar_animal [ " elefante " ])
5 Out [5]:
6 ’ gris ’
Respuesta:
Leemos el archivo y tenemos cuidado de no agregar en el diccionario la primera fila con
los títulos. Recordamos que usamos strip() para quitar espacios en blanco al principio
y final de la línea, y los saltos de línea, y split(símbolo) para dividir una cadena en una
lista, con el símbolo de división siendo en este caso una tabulación o tab, "\t". El
primer elemento de cada lista es el nombre del estudiante, y el resto de la lista tiene
sus cosas favoritas:
1 In [1]:
2 g = open ( " favoritos . txt " )
3 g . readline () # nos saltamos la primera l í nea de t í tulos
4 d = dict ()
5 for line in g : # a partir de la segunda l í nea
6 A = line . strip () . split ( " \ t " ) # una lista A por estudiante
7 d [ A [0]] = A [1:] # d [ nombre ]= [ lista de favoritos ]
8 g . close ()
9 for a , b in d . items () : # checamos que le í mos bien el archivo
10 print (a , b )
11 Out [1]:
12 Luis [ ’ verde militar ’ , ’ buitre abante ’ , ’ Vals 2 de Shostakovich ’ , ’
Jurasic Park ’ , ’ Las mil y una noches ’]
13 David [ ’ morado ’ , ’ perro ’ , ’A mi manera ’ , ’ El padrino ’ , ’ La apolog í a de
S ó crates ’]
14 Omar [ ’ verde ’ , ’ tibur ó n ’ , ’ oh bambino mio caro ’ , ’ midiendo el mundo ’ ,
’ an á lisis matem á tico ’]
15 Erika [ ’ azul ’ , ’ le ó n ’ , ’ Obertura 1812 ’ , ’ Coco ’ , ’ Harry Potter ’]
16 Nataly [ ’ turquesa ’ , ’ perro ’ , ’ amnesia ’ , ’ Harry Potter ’ , ’ Mal amor ’]
17 Roberto [ ’ rojo ’ , ’ serpiente ’ , ’ Ella ’ , ’ La gran pelea ’ , ’ Cuentos de
Andersen ’]
18 Carlos IV [ ’ azul ’ , ’ ara ñ a ’ , ’ No more work ’ , ’ Terminator ’ , ’ El arte de
amar ’]
19 Ana [ ’ azul ’ , ’ gato ’ , ’ humanity ’ , ’ saw ’ , ’ cr ó nicas de una muerte
anunciada ’]
20 Beto [ ’ morado ’ , ’ gato ’ , ’ my inmortal ’ , ’ El Padrino ’ , ’ El Padrino ’]
21 Jahir [ ’ azul ’ , ’ perro ’ , ’ viva la vida ’ , ’ Los indestructibles ’ , ’ El
zarco ’]
22 Ad á n [ ’ azul ’ , ’ puma ’ , ’ vaquero rockanrolero ’ , ’ Transformers ’ , ’ El
Quijote ’]
23 Frida [ ’ azul ’ , ’ cobaya ’ , ’ cuando t ú no est á s ’ , ’ MegaMente ’ , ’ La
soledad de los n ú meros primos ’]
1 In [2]:
2 for a , b in d . items () :
3 print ( " El animal favorito de " + a + " es el " + b [1]+ " , le gusta \
tararear " + b [2]+ " y le encant ó leer " + b [ -1])
4 Out [2]:
5 El animal favorito de Luis es el buitre abante , le gusta tararear Vals
2 de Shostakovich y le encant ó leer Las mil y una noches
6 El animal favorito de David es el perro , le gusta tararear A mi manera
y le encant ó leer La apolog í a de S ó crates
7 El animal favorito de Omar es el tibur ón , le gusta tararear oh bambino
mio caro y le encant ó leer an á lisis matem á tico
8 El animal favorito de Erika es el le ón , le gusta tararear Obertura
1812 y le encant ó leer Harry Potter
9 El animal favorito de Nataly es el perro , le gusta tararear amnesia y
52 CAPÍTULO 3. ESTRUCTURAS DE DATOS BÁSICOS
A, Á, a, á → 0 G, g → 6 M, m → @ R, r → ( X, x →>
B, b → 1 H, h → 7 N, n → $ S, s →) Y, y →<
C, c → 2 I, Í, i, í → 8 Ñ, ñ → + T, t → [ Z, z →=
D, d → 3 J, j → 9 O, Ó, o, ó → − U, Ú, u, ú →]
E, É, e, é → 4 K, k → # P, p → ∗ V, v → {
F, f → 5 L, l → % Q, q → / W, w →}
Ejercicios:
a) Dada la Tabla 3.1 de encriptación (Recuerda que estos signos en Python van entre-
comillados):
y mapeando los espacios en blanco a guiones bajos (_), encripta el siguiente mensaje:
“qué voy a hacer no sé no encuentro nada nada nada la solución no sé cómo encontrarla
dime trato de olvidarte y yo quiero olvidarte y yo no sé cómo te olvido siempre en mi
mente”.
b) desencripta el siguiente mensaje, usando el mismo lenguaje de encriptación, y donde
todo quede en minúsculas, sin acento:
“2]0$3-_4%_0@-(_%%460_0)8_34_4)[0_@0$4(0_]$-_$8_)4_30_$8_2]4$[0_4%
_20(][0%_(4{4(3424_<_4%_6]0@0278[-_5%-(424_<_%0_)-60_)4_(4{84$[0”
3.3. Tuplas
En esta ocasión vamos a aprovechar la estructura de tuplas para resolver un problema.
El problema consiste en que, en una universidad, las materias del ciclo básico se progra-
man para que no haya traslape en un dado semestre, pero, hay varios estudiantes que
toman materias de diferente semestre para los cuales puede haber traslapes. Hagamos
un programa que cuente, vía entrevista previa, cuántos estudiantes requieren llevar un
3.3. TUPLAS 53
par dado de materias, para evitar traslapes entre ellas, dando prioridad a las que tengan
una mayor frecuencia, y dejando fuera los pares de materias que pertenecen al mismo
semestre y por los cuáles no nos tendríamos que preocupar (esto es opcional).
Suponemos que los datos están en excel, así que convirtamos primero a .txt separado
por tabs, o a .csv (separado por comas). Una vez que tenemos nuestro archivo .txt o
.csv, hacemos
1 In [1]:
2 f = open ( " C :/ Users / eriko / Desktop / porquer í a / horarios / materias . txt " )
3 # si el archivo est á en el mismo directorio , solo pones " materias . txt "
4 A = [ line . strip () . split ( " \ t " ) for line in f ]
5 f . close ()
6 print ( A )
7 Out [1]:
8 [[ ’ Beto ’ , ’ MN ’ , ’ PA ’ , ’ II ’ , ’ MD2 ’ , ’ TF ’] ,
9 [ ’ Frida ’ , ’ TF ’ , ’ PE ’ , ’ MNED ’ , ’ PA ’] ,
10 [ ’ Jahir ’ , ’ MN ’ , ’ PA ’ , ’ EM ’ , ’ CV ’] ,
11 [ ’ Omar ’ , ’ AM ’ , ’ MD2 ’ , ’ PA ’ , ’ AS ’] ,
12 [ ’ Ana ’ , ’ TF ’ , ’ PE ’ , ’ MNED ’ , ’ PA ’ , ’ MD2 ’] ,
13 [ ’ Nataly ’ , ’ MN ’ , ’ TF ’ , ’ PA ’ , ’ MD1 ’ , ’ CV ’] ,
14 [ ’ Luis ’ , ’ MN ’ , ’ MEC2 ’ , ’ PA ’ , ’ MD1 ’ , ’ CV ’] ,
15 [ ’ Adam ’ , ’ TF ’ , ’ MD2 ’ , ’ PA ’ , ’ AM ’ , ’ PE ’] ,
16 [ ’ Carlos ’ , ’ TF ’ , ’ AM ’ , ’ PA ’ , ’ AR1 ’ ]]
Queremos trabajar con las materias nada más, así que construyamos otra lista que
contenga sólo a estas:
1 In [2]:
2 materias =[ i [1:] for i in A ] # misma lista pero desde segundo í ndice
3 print ( materias )
4 Out [2]:
5 [[ ’ MN ’ , ’ PA ’ , ’ II ’ , ’ MD2 ’ , ’ TF ’] ,
6 [ ’ TF ’ , ’ PE ’ , ’ MNED ’ , ’ PA ’] ,
7 [ ’ MN ’ , ’ PA ’ , ’ EM ’ , ’ CV ’] ,
8 [ ’ AM ’ , ’ MD2 ’ , ’ PA ’ , ’ AS ’] ,
9 [ ’ TF ’ , ’ PE ’ , ’ MNED ’ , ’ PA ’ , ’ MD2 ’] ,
10 [ ’ MN ’ , ’ TF ’ , ’ PA ’ , ’ MD1 ’ , ’ CV ’] ,
11 [ ’ MN ’ , ’ MEC2 ’ , ’ PA ’ , ’ MD1 ’ , ’ CV ’] ,
12 [ ’ TF ’ , ’ MD2 ’ , ’ PA ’ , ’ AM ’ , ’ PE ’] ,
13 [ ’ TF ’ , ’ AM ’ , ’ PA ’ , ’ AR1 ’ ]]
La idea es contar, para cada par de materias, cuántos estudiantes las van a tomar a
ambas. Necesitamos algo del tipo cuenta de (mat1, mat2) igual a un número. Lo que
se nos puede venir a la mente rápidamente es usar un diccionario para llevar la cuenta.
Nada más que el detalle es que necesitamos guardar un par de cadenas, las materias,
que podamos separar fácilmente, y no podemos usar una lista como clave, por ser
mutable. Podríamos intentar juntar las dos materias en una sola cadena separada por
ejemplo por un guion, pero, precisamente para eso están las tuplas. Las tuplas guardan
uno o más elementos, de forma similar a las listas, con la diferencia de que la tupla
es inmutable. Su notación es a = (o1 , o2 , . . . , on ), y así, nuestro diccionario sería de la
forma cuenta[(mat1, mat2)].
Las tuplas pueden usarse como ya vimos en el tema de las funciones, cuando definimos
54 CAPÍTULO 3. ESTRUCTURAS DE DATOS BÁSICOS
una función y queremos retornar más de un objeto, o como en el caso actual, que
requerimos una clave de más de un objeto en un diccionario, entre otros usos.
Siguiendo con el ejemplo, contemos cuántos estudiantes están pidiendo el mismo par
de cursos. Por cada estudiante, vamos a ordenar primero su lista de materias, pues no
queremos que (mat1, mat2) se considere distinta a (mat2, mat1). Después, con 2 for
loops vamos a formar todos los posibles pares de materias.
1 In [3]:
2 cuenta = dict () # aqu í llevamos la cuenta de cada par de materias
3 for lista in materias :
4 lista . sort () # ordenamos la lista de cada estudiante
5 for i in range ( len ( lista ) ) :
6 for j in range ( i +1 , len ( lista ) ) :
7 cuenta [( lista [ i ] , lista [ j ]) ]=\
8 cuenta . get (( lista [ i ] , lista [ j ]) ,0) +1
Sería más conveniente que tuviéramos el diccionario invertido, donde juntáramos los
pares de materias con igual frecuencia. Definamos otro diccionario donde invirtamos
los papeles de key y value:
1 In [5]:
2 frecuencias = dict ()
3 # si la frecuencia b todav í a no existe en el diccionario ,
4 # le asignamos una lista vac í a por default
5 # el par de materias , a , se agrega poco a poco
6 for a , b in cuenta . items () :
7 frecuencias [ b ]= frecuencias . get (b ,[]) +[ a ]
Las claves del diccionario frecuencias son, por tanto, las frecuencias, y sus valores son
una lista de los pares de materias con esa frecuencia
1 In [6]:
2 print ( frecuencias )
3 Out [6]:
3.3. TUPLAS 55
Concluimos exhortando que, por favor, si esos pares de materias pertenecen a diferentes
semestres, no se programen en el mismo horario, dando prioridad a los pares de materias
con mayor frecuencia.
4 Numpy
En este capítulo veremos uno de los módulos más utilizados en las ciencias y la ciencia de
datos, que es el numpy, que nos sirve para hacer operaciones vectoriales, así como facilitar
la graficación, entre otras. Numpy en sí es todo un mundo y seguramente quedaremos
cortos en todo lo que se puede hacer con este módulo, pero hay muchos tutoriales y
recursos en internet para complementar lo visto aquí. Los objetivos de aprendizaje para
el lector en este capítulo son, generar arreglos, en particular de tipo vector y matriz
para operar con ellos en el ámbito del Álgebra Lineal. Mapear funciones de manera
sencilla, con la ayuda de los vectores. Aprender diversas funciones de numpy para sus
aplicaciones en diferentes ámbitos, en otros módulos que requieren de la estructura de
arreglos. Seleccionar elementos de un arreglo dados diversos criterios, para aplicaciones
varias. Realizar operaciones de tipo estadístico con los arreglos. Para ahondar más en
el módulo de numpy, es la bibliografía se recomienda el manual en Ciencias de Datos
en Python de Jake VanderPlas (2016), pero hay mucho material en línea que puedes
revisar.
Recuerda que, aunque no se muestra en todos los ejercicios, cuando empiezas una sesión
y trabajas con numpy debes importar el módulo con import numpy as np. También,
como recordatorio, aunque parezca que haya que memorizar cómo usar una función, en
spyder cuando se escribe ésta, además de que se despliegan todas las posibles funciones
que empiezan con determinada letra, al escribir los paréntesis aparece qué argumentos
se deben dar a la función, y cuáles son los valores por default de algunos de ellos, que
podríamos cambiar en caso de ser necesario.
57
58 CAPÍTULO 4. NUMPY
7 Out [1]:
8 [ -5.174109498544745 , -4.5758066333301315 , -2.0731458360008705 ,
3.978716493246764 , 3.486395009384143 , 8.0 , 12.513604990615857 ,
12.021283506753235 , 18.07314583600087 , 2 0. 57 580 66 33 33 013 ]
Si aplicamos la función con map en el mismo intervalo, hay que comentar un detalle.
Hay objetos que se llaman “generadores” que calculan resultados sin que se guarden en
la memoria, se ejecutan al vuelo. Para guardar los resultados debemos anteponer list
al mapeo, al ser map un generador:
1 In [2]:
2 A2 = list ( map ( funci ón , range ( -5 ,5) ) )
3 print ( A2 )
4 Out [2]:
5 [ -5.174109498544745 , -4.5758066333301315 , -2.0731458360008705 ,
3.978716493246764 , 3.486395009384143 , 8.0 , 12.513604990615857 ,
12.021283506753235 , 18.07314583600087 , 2 0. 57 580 66 33 33 013 ]
Si ahora lo hacemos con numpy, cuyo alias es np, no hay necesidad de hacer un for loop
o de mapear en un intervalo:
1 In [3]:
2 import numpy as np
3 x = np . arange ( -5 ,5) # crea un arreglo en el intervalo [ -5 ,5)
4 y = 3* x -2* np . sin (4* x ) +8
5 print ( y )
6 Out [3]:
7 [ -5.1741095 -4.57580663 -2.07314584 3.97871649 3.48639501 8.
8 12.51360499 12.02128351 18.07314584 20.57580663]
Como vemos en el ejemplo, en la multiplicación 3x, cada una de las entradas del arreglo
x se multiplica por el escalar 3, y cuando se suma el 8, a cada una de las entradas del
arreglo 3x se le suma el escalar 8. Y, al igual que el módulo math, numpy tiene las
funciones usuales como las funciones trigonométricas, o constantes como π, sólo que
numpy puede aplicar funciones a todo un arreglo, como en el ejemplo con la función
seno, y no sólo a un número, es más general. Numpy tiene incluso el módulo random como
submódulo, que se usa de manera similar a como lo aprendimos en el segundo capítulo,
sólo que ahora hay que especificar la cantidad de números aleatorios que queramos,
cuando aplique. Para checar el contenido de numpy usamos dir(), como siempre, pero
en esta ocasión no lo vamos a imprimir en estas páginas por lo enorme del módulo que,
además, contiene submódulos.
4.1.1. Vectores
Los objetos de numpy, como ya adelantamos, son arreglos, ndarray, pero con ello no
nos referimos necesariamente nada más a objetos tipo vectores, también se pueden
representar matrices y objetos de mayor dimensión. Empezando con los arreglos de
tipo vector, unas de las formas de generarlos son:
1 In [4]:
2 # se da un rango y el paso , que no es necesariamente entero
3 a = np . arange ( -2 ,3 ,0.5)
4 # se da un rango y la cantidad de n ú meros que queremos en ese rango
4.1. GENERACIÓN DE ARREGLOS 59
5 b = np . linspace ( -2 ,3 ,21)
6 # convierte una iterable , como un rango o una lista , en un arreglo
7 c = np . array ( range ( -2 ,4) )
8 print ( " a " ,a )
9 print ( " b " ,b )
10 print ( " c " ,c )
11 Out [4]:
12 a [ -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5]
13 b [ -2. -1.75 -1.5 -1.25 -1. -0.75 -0.5 -0.25 0. 0.25 0.5
0.75 1. 1.25 1.5 1.75 2. 2.25 2.5 2.75 3. ]
14 c [ -2 -1 0 1 2 3]
Vemos el tipo de objeto, como siempre, con type(), pero ahora también podemos ver
el tipo de elementos que hay en el arreglo con el atributo dtype:
1 In [5]:
2 for i in (a ,b , c ) :
3 print ( type ( i ) , i . dtype )
4 Out [5]:
5 < class ’ numpy . ndarray ’ > float64
6 < class ’ numpy . ndarray ’ > float64
7 < class ’ numpy . ndarray ’ > int32
En este ejemplo vemos que los elementos del vector c son de tipo entero, int. Si quisié-
ramos cambiarlos a float, por ejemplo, lo podemos hacer desde el inicio especificando
el dtype, o con una conversión posterior:
1 In [6]:
2 d = np . array ( range ( -2 ,3) , dtype = float )
3 c = c . astype ( float )
4 print (d , d . dtype )
5 print (c , c . dtype )
6 Out [6]:
7 [ -2. -1. 0. 1. 2.] float64
8 [ -2. -1. 0. 1. 2.] float64
Ejercicios:
1. Crea un arreglo tipo vector en el intervalo (0, π/6) que consista de 10 puntos. En-
cuentra la tangente de todos estos valores.
60 CAPÍTULO 4. NUMPY
2. Crea un arreglo tipo vector en el intervalo (0, 3) con un paso de tamaño 0.2. Encuentra
el cubo de todas sus componentes.
3. Crea un arreglo de 9 entradas con números tomados con la misma probabilidad
(distribución uniforme) entre el 4 y el 8. Encuentra el logaritmo de estos números.
4.1.2. Matrices
Para arreglos tipo matriz, una forma de generarlas es a partir de una lista de listas, o
lista anidada, con la forma general [Link](), al igual que con vectores:
1 In [1]:
2 # matriz de 2 x3 a partir de n ú meros dados , y sus dimensiones :
3 A = np . array ([[1 ,4 ,6] ,[7 ,11 ,15]])
4 print (A , " , dims = n , m = " ,A . shape )
5 Out [1]:
6 [[ 1 4 6]
7 [ 7 11 15]] , dims = n , m = (2 , 3)
Otra forma es para cuando se quieren matrices diagonales, sólo se usa la función eye
(I, y eye suenan igual en inglés, “ai”):
1 In [2]:
2 # matriz diagonal de 3 x3 multiplicada por una constante :
3 print (5* np . eye (3) )
4 Out [2]:
5 [[5. 0. 0.]
6 [0. 5. 0.]
7 [0. 0. 5.]]
Otra forma es con el submódulo random, donde en lugar de dar un solo número como
el número de elementos, damos el tamaño o shape de la matriz, (n, m). Dependiendo
de la función, también será posible o no tener diferente intervalo de valores para las
diferentes coordenadas:
1 In [3]:
2 # matriz de 3 x2 de enteros aleatorios del 1 al 100:
3 # no se puede dar diferentes intevalos :
4 print ( np . random . randint (1 ,100 ,(3 ,2) ) )
5 # matriz de 2 x3 de n ú meros aleatorios del 1 al 20:
6 print ( np . random . uniform (1 ,20 ,(2 ,3) ) )
7 # matriz de 2 x3 de n ú meros con la primera columna entre el 1 y el 2 ,
8 # la segunda entre el 10 y el 20 , y la tercera entre el 100 y 200:
9 print ( np . random . uniform ((1 ,10 ,100) ,(2 ,20 ,200) ,(2 ,3) ) )
10 Out [3]:
11 [[76 59]
12 [48 17]
13 [13 94]]
14 [[ 9.35052495 15.85297476 14.00794956]
15 [14.99681483 10.29903439 19.12583672]]
16 [[ 1.15373916 16.49118853 155.74135276]
17 [ 1.86853158 11.43628016 144.79043472]]
Otra forma de generar matrices es empezar con matrices con entradas igual a ceros o
unos, que posteriormente serán cambiadas. Se pueden generar dando el tamaño de las
4.2. OPERACIONES CON VECTORES Y MATRICES 61
dimensiones con las funciones zeros y ones, o tomándolas de alguna otra matriz, con
las funciones zeros_like y ones_like:
1 In [4]:
2 # matriz de 3 x2 de ceros :
3 A = np . zeros ((3 ,2) )
4 print ( A )
5 # matriz de unos del mismo tama ñ o que A :
6 print ( np . ones_like ( A ) )
7 Out [4]:
8 [[0. 0.]
9 [0. 0.]
10 [0. 0.]]
11 [[1. 1.]
12 [1. 1.]
13 [1. 1.]]
Ejercicios:
1. Genera una matriz de 2 × 3 con números aleatorios entre el 0 y el 1. Además de
la distribución uniforme, existe la función random del submódulo random que genera
estos números positivos menores a 1, y que tiene como único argumento el tamaño de
la matriz.
2. Genera una matriz de ceros del mismo tamaño que la del problema 1.
19 a - b : [ -3 -3 -3]
20 ab punto : 32
21 ab outer : [[ 4 5 6]
22 [ 8 10 12]
23 [12 15 18]]
24 Axb : [ -3 6 -3]
25 Aa : [14 32]
26 A ^ T : [[1 4]
27 [2 5]
28 [3 6]]
29 AB : [[22 28]
30 [49 64]]
¡Hay que tener cuidado con el producto de matrices y vectores! La forma en que operan
los arreglos se llama element-wise, o elemento a elemento, como lo vemos en la suma y
resta de vectores, el primer elemento de uno con el primer elemento del otro, y así. Si
usáramos el signo * como producto, en lugar de dot, se multiplicarían los términos en
forma element-wise:
1 In [2]:
2 print ( a * b )
3 Out [2]:
4 array ([ 4 , 10 , 18])
Ejercicios:
1. Suma de vectores. Un turista, en una ciudad con cuadras muy bien alineadas y
del mismo tamaño, se desplaza desde su hotel 7 cuadras hacia el Este y 2 cuadras hacia
el Norte para llegar al primer lugar turístico. Al terminar, sale y recorre 2 cuadras hacia
el Oeste y una cuadra hacia el Norte para su segunda parada. Horas más tarde se va
finalmente a comer a un muy buen restaurante localizado 3 cuadras hacia el Sur y 2
cuadras hacia el Oeste desde su último punto. Con respecto al hotel, ¿cuál es su vector
de desplazamiento medido en número de cuadras?
2. Producto punto. En física, cuando se aplica una fuerza constante el trabajo se
define como el producto punto del vector fuerza y el vector desplazamiento. Una persona
jala una caja con una fuerza cuyas componentes son (15, 7)N . La caja se desplaza 6 m
por el suelo. ¿Qué trabajo realiza la persona sobre la caja?
3. Producto cruz. La componente magnética de la fuerza de Lorentz sobre una partí-
cula está dada por F = (q/c) v × B, donde q es la carga de la partícula, v su vector de
velocidad, c la velocidad de la luz, y B el vector de campo magnético. Un electrón, cuya
carga es igual a q = −1.6 × 10−19 C, tiene un vector de velocidad igual a v = (0.3c, 0.1c)
en un plano, y cruza una región donde hay un campo magnético cuyas componentes son
B = (0.03, 0.02)T en el mismo plano del vector velocidad. Encuentra la magnitud de
la fuerza que siente el electrón debido a la presencia del campo magnético, en Newtons
(Nota: en numpy, cuando los vectores son en 3D, el producto cruz se expresa también
como un vector en 3D. Cuando los vectores viven en 2D, el producto cruz se expresa
como una magnitud, que, como sabemos, es la magintud de un vector que vive en la
dirección perpendicular al plano).
4.2. OPERACIONES CON VECTORES Y MATRICES 63
√
4. Producto de matriz y vector. El vector v = ( 3, 1) se rota 60◦ en la dirección
antihoraria en el plano x-y. ¿Cuáles son sus componentes después de la rotación? Re-
cuerda que la operación
de rotación
está dada por v ′ = Rv, con la matriz de rotación
cos θ − sin θ
dada por R = .
sin θ cos θ
4.2.2. Broadcasting
En algunos casos, se pueden hacer operaciones con objetos de diferentes dimensiones.
Ya vimos un ejemplo con la función 3x − 2 sin (4x) + 8, donde el 8 puede pasar de ser un
escalar a ser un vector del mismo tamaño que x. A esta ampliación del tamaño de las
dimensiones se le llama broadcasting, que, traducido al español, significa radiodifusión,
pero aquí el broad significa ampliar. Hay reglas para ello, por ejemplo, no podemos
sumar un vector de dos entradas con uno de tres entradas, pues los vectores son uni-
dimensionales y el tamaño de su única dimensión difiere. Pero, por ejemplo, podemos
sumar un número a un vector porque, aunque el número no tiene dimensiones, se le
puede dotar de una dimensión que iguale en tamaño a la del vector. En general, cuando
se pueda dotar de una dimensión extra a un objeto, esta dimensión se amplía para
igualar el tamaño de una de las dimensiones del otro objeto. Sin embargo, la expansión
de dimensiones no es tan arbitraria, las nuevas dimensiones que aparecen son las pri-
meras. Por ejemplo, si un vector se tiene que expandir para sumarse con una matriz, el
vector, de tener una dimensión, pasa a tener dos dimensiones, la de los renglones y la
de las columnas, y el tamaño de la dimensión de las columnas va a ser igual al tamaño
original del vector, mientras que la nueva dimensión, de los renglones, que van primero,
se amplía para tener el mismo tamaño de la matriz. Por tanto, para que la suma no
marque error el tamaño de las columnas de la matriz a sumar debe ser igual al tamaño
del vector original.
Veamos unos ejemplos:
- Un número se puede expandir para tener las dimensiones de un vector o una matriz:
1 In [3]:
2 a = np . array ([1 ,2 ,3]) # vector de 3 entradas
3 A = np . array ([[1 ,2 ,3] ,[4 ,5 ,6]]) # matriz de 2 x3
4 num =3
5 print ( " a + num : " ,a + num ) # vector m á s escalar ( - > convertido a vector )
6 print ( " A + num : " ,A + num ) # matriz m á s escalar ( - > convertido a matriz )
7 Out [3]:
8 a + num : [4 5 6]
9 A + num : [[4 5 6]
10 [7 8 9]]
En este ejemplo, si la matriz hubiera sido de 3 × 2, la suma con el vector marcaría error.
Ejercicios:
Checa si las siguientes operaciones se pueden resolver con broadcasting, hazlo a mano
de ser posible, y después checa con Python el resultado:
2 4 6
a = (3, 2, 1), A=
7 9 15
a) a + A, b) a + AT , c) a ∗ A (¡element wise!)
Vemos que el vector tiene una sola dimensión, con tamaño 3 (las tuplas de un solo
elemento tienen una coma, para diferenciarlas de un objeto entre paréntesis normales).
En algunas operaciones, el vector es tratado como si fuera un ente diferente a las matri-
ces. Por ejemplo, en la multiplicación entre matrices, con dot, se debe de tener cuidado
con las dimensiones de las matrices, hay que transponer en caso de que sea necesario.
Con los vectores, no importa, numpy entiende que debe hacer un producto punto o un
producto exterior entre ellos, con dot y outer, sin necesidad de transponer ningún
vector (en el primero, se multiplica un vector renglón por un vector columna y en el se-
gundo se invierten los papeles), sólo importa que los vectores tengan el mismo tamaño.
En otros casos, hay operaciones matriciales que marcan error cuando se realizan entre
una matriz y un vector, porque se espera que los arreglos tengan la misma dimensión.
Hay una función, reshape(), que permite cambiar las dimensiones de cualquier arre-
glo, siempre que su número de entradas no cambie. Con esta función podemos cambiar
nuestro vector para que se entienda como una matriz, y podamos hablar sin confusión
de vectores de tipo renglón o de tipo columna.
Veamos por ejemplo la función concatenate(), que junta dos o más arreglos, o dos o
más matrices para formar una más grande, actuando sobre una matriz y el vector a de
arriba:
1 In [2]:
2 A = np . array ([[3 ,6 ,2] ,[1 ,3 ,7]])
3 print ( np . concatenate (( A , a ) ) )
4 ----------------------------------------------------------------------
5 ValueError Traceback ( most recent call last )
6 < ipython - input -31 -2 de60d974339 > in < module >()
7 1 A = np . array ([[3 ,6 ,2] ,[1 ,3 ,7]])
8 ----> 2 np . concatenate (( A , a ) )
9
4.2. OPERACIONES CON VECTORES Y MATRICES 65
10 ValueError : all the input arrays must have same number of dimensions
Hay que notar que el vector ahora tiene doble paréntesis, ahora es una matriz de un solo
renglón, tiene dos dimensiones que ahora sí son claramente de tamaño 1 × 3. Tratamos
de nuevo de concatenar:
1 In [4]:
2 print ( np . concatenate (( A , a ) ) )
3 Out [4]:
4 array ([[3 , 6 , 2] ,
5 [1 , 3 , 7] ,
6 [8 , 4 , 2]])
Ejercicios:
1. Convierte el arreglo [Link](1,30,3) en una matriz de una sola columna. Para
saber la dimensión del vector original, usa el atributo shape.
2. Convierte el arreglo [Link](1,21,12) en una matriz de 4 × 3.
3. Convierte el arreglo [Link](3,9,20) en una matriz de un solo renglón.
Puedes checar con help para saber más sobre las condiciones para aplicar estas funcio-
nes, ¡y para qué sirven! Vamos a hacer las operaciones más conocidas con una matriz
cuadrada:
1 In [2]:
2 C = np . array ([[ -1 ,2] ,[2 ,1]]) # matriz cuadrada de 2 x2
3 c = np . array ([4 ,5]) # vector
4 print ( " det ( C ) : " , np . linalg . det ( C ) ) # determinante de C
66 CAPÍTULO 4. NUMPY
Ejercicios:
0.4 0.2
1. Dada la matriz A = , encuentra a) la inversa de la matriz, b) su determi-
0.5 0.8
nante, y c) sus eigenvalores y eigenvectores.
2. Resuelve el siguiente sistema de ecuaciones usando lo visto en esta sección:
3x + y − 2z = 2, 2x − y + 4z = 7, −x + 2y − z = 3
4.3. Comparaciones
Cuando comparamos listas completas, tenemos una sola variable booleana como res-
puesta:
1 In [1]:
2 A = [[1 ,2 ,3] ,[4 ,5 ,6]]
3 B = [[1 ,2 ,3] ,[4 ,5 ,6]]
4 print ( A == B )
5 Out [1]:
6 True
Sin embargo, cuando se comparan arreglos, tenemos como resultado un arreglo de boo-
leanos, pues la operación, de nuevo, es pair-wise:
1 In [2]:
2 A = np . array ( A )
3 B = np . array ( B )
4 print ( A == B )
5 Out [2]:
6 array ([[ True , True , True ] ,
7 [ True , True , True ]])
Esto puede generar errores por ejemplo si tenemos un if donde Python espera una sola
variable como respuesta. En ese caso, se usa [Link]() para saber si todos los elementos
del arreglo cumplen con la condición:
4.4. SELECCIÓN DE ELEMENTOS DE UN ARREGLO 67
1 In [3]:
2 print ( np . all ( A == B ) )
3 Out [3]:
4 True
También se cuenta con [Link](), para saber si al menos un elemento cumple con alguna
condición:
1 In [4]:
2 A = np . array ([1 ,2 ,3])
3 B = np . array ([3 ,2 ,1])
4 print (A > B )
5 print ( np . any (A > B ) )
6 Out [4]:
7 [ False False True ]
8 True
Ejercicios:
Crea un arreglo de tamaño 20 entre 0 y 1 con la función random del módulo random.
Si todos los números del arreglo son mayores a 0.05, imprime “¡gané!”. De otra forma,
imprime “lástima Margarita”.
Cuando los arreglos son de tipo matriz, hay algunas diferencias con las listas anidadas.
En resumen, tenemos que, si la matriz en cuestión es A:
- A[i] da el renglón i, al igual que con las listas,
68 CAPÍTULO 4. NUMPY
- pero a diferencia de las listas, la columna j se llama con A[:,j]. Con listas habría
que llamar a un for loop para seleccionar a esos elementos,
- el elemento en el renglón i y columna j se llama con A[i,j]. En las listas, el análogo
sería A[i][j], que también funciona en los arreglos de numpy,
- para tener elementos del renglón i, inclusivo, al j, exclusivo, y columnas de la k a la
l, como podemos imaginar, se usa A[i:j,k:l]:
1 In [2]:
2 A = np . array ([[1 ,2 ,3] ,[4 ,5 ,6] ,[7 ,8 ,9]])
3 print ( " A [2]: " , A [2]) # tercer rengl ó n ( í ndice 2)
4 print ( " A [: ,1] " , A [: ,1]) # segunda columna ( í ndice 1)
5 # elemento del segundo rengl ó n ( í ndice 1) y tercera columna ( í ndice 2)
6 print ( " A [1 ,2]: " , A [1 ,2])
7 # renglones con í ndices 1 y 2 , columnas con í ndices 0 y 1
8 print ( " A [1:3 ,:2]: " ,A [1:3 ,:2])
9 Out [2]:
10 A [2]: [7 8 9]
11 A [: ,1] [2 5 8]
12 A [1 ,2]: 6
13 A [1:3 ,:2]: [[4 5]
14 [7 8]]
Con esto en mente, vamos a hacer un ejercicio donde generemos una matriz llena de
ceros de un cierto tamaño, y después modificamos sus entradas de acuerdo a una cierta
condición:
1 In [3]:
2 A = np . zeros ((3 ,3) ) # matriz de ceros de 3 x3
3 for i in range (3) :
4 for j in range (3) :
5 if j -i >=0:
6 A [i , j ]=( i +1) *( j +2)
7 print ( A )
8 Out [3]:
9 array ([[ 2. , 3. , 4.] ,
10 [ 0. , 6. , 8.] ,
11 [ 0. , 0. , 12.]])
Ejercicios:
4 8 11
1. Dada la matriz A = 25 3 8 , encuentra la submatriz que consiste en los 2
2 6 7
primeros renglones y las 3 columnas de la matriz A. Multiplica esta submatriz por los
3 últimos elementos del vector a = (4, 8, 9, 11)
2. Dada la matriz [Link]((20,3)), selecciona la primera columna.
Ejercicios:
−5 6 2 −9
4 3 5 8
Dada la matriz A = 2 −1 7 −4, selecciona a) la primera y última columnas,
−8 4 11 3
y b) el primer y último renglones.
Cuando decimos, en el ejemplo, que A < 20, tenemos un arreglo lleno de Trues y
Falses, como ya vimos en la sección de comparación de arreglos. Al hacer A[A<20],
seleccionamos los elementos donde A < 20 es True.
Si queremos más de una condición, se usan los operadores and, or y not, pero la notación
es ahora con los símbolos &, |, y ~, respectivamente. Las diferentes condiciones deben
estar encerrada entre paréntesis:
1 In [2]:
2 print ( A [( A <20) & ( A %2==1) ]) # n ú meros impares menores a 20
3 Out [2]:
4 array ([11 , 9 , 1 , 11])
Ejercicios:
1. Dada la lista de estudiantes
A = [Link]([“Benito”, ”Pedro”, ”Alicia”, ”Carmen”, ”Jesús”])
y sus calificaciones respectivas B = [Link]([8,7,9,6,5]), selecciona a los estudian-
tes que obtuvieron una calificación igual o mayor a 8 (es muy fácil, pero hazlo con
numpy)
2. Crea un arreglo de 500 números aleatorios entre el 1 y el 1, 000 y selecciona los
múltiplos de 3 cuyas raíces cúbicas sean mayor o igual a 8.
11 redondeando a 4 decimales :
12 [[1. 0.7071]
13 [0. 0.5 ]]
Además de estas funciones, Python cuenta con funciones de agregación, que actúan
sobre todos o un conjunto de los datos para dar como resultado un número. Por ejemplo,
querríamos sumar los datos o sacar su promedio. Estas operaciones son más comunes
en tablas de datos, como en Excel, nada más que con Python uno tiene la ventaja de
programar cosas más específicas y hacer gráficas de una manera más fácil y rápida.
Hay un módulo más ad-hoc para trabajar con tablas de datos, que es el pandas, el
cual maneja bien archivos con datos faltantes y el cual mostraremos más adelante. Por
mientras veamos cómo manejar estas operaciones en numpy.
Esto está muy bien, pero lo que queremos es encontrar el promedio de cada estudiante,
o el promedio de los resultados de cada examen por separado, no el promedio de todos
los números. Resulta que, cuando estamos escribiendo en spyder los paréntesis de la
función, vemos que uno de los argumentos es axis=None. Lo que nos dice axis es qué
eje debe colapsarse para hacer la operación, y si por default no está ninguno de los dos
ejes, es None, se toma el promedio de todos los números.
La notación es como sigue, axis=0 significa que vamos en la dirección de los renglones,
de abajo hacia arriba, sumando los números y promediando. El resultado es que se
“colapsan” los renglones y obtenemos el promedio de las columnas. Por otro lado, axis=1
significa que vamos en la dirección de las columnas, de izquierda a derecha, resultando
en el “colapso” de las columnas y terminando con el promedio de los renglones. Así, si
queremos saber el promedio de los estudiantes escogemos axis=1, y para el promedio
de cada examen, escogemos axis=0:
1 In [3]:
2 print ( A . mean ( axis =0) )
3 print ( A . mean ( axis =1) )
4 Out [3]:
72 CAPÍTULO 4. NUMPY
El mejor promedio es el del segundo estudiante, y el examen donde les fue mejor a los
estudiantes, en promedio, fue el segundo.
Ejercicios:
450 221 114
1. Dada la matriz 325 324 628, encuentra la suma, el promedio, la mediana y la
836 716 902
desviación estándar de los datos de cada columna. Además de la función mean(), usa
las funciones sum(), median() y std().
2. Dada la matriz
[Link](8)
A=[Link]((3,10))
Encuentra los valores máximos y mínimos por renglón. Encuentra también los índices
donde se encuentran esos números. Para ello, además de las funciones max() y min(),
usa las funciones argmax() y argmin().
1 3 −6 1
3. Dadas las matrices A = ,yB= , concatena las matrices primero
7 9 3 4
para tener una nueva matriz de tamaño 2 × 4, y después para tener una matriz de
tamaño 4 × 2. Usa la función concatenate() para ello.
5 Imágenes y gráficas
73
74 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
5.1. [Link]
5.1.1. Gráficas en dos dimensiones: plot y scatter
El módulo más tradicional para graficar en Python es el [Link], el cual
tiene como alias plt. Aquí, pyplot es un submódulo de matplotlib. La primera forma
de graficar que veremos es la llamada “estilo Matlab”. La receta para graficar en 2D es
escribir las x’s en una lista o arreglo de numpy, seguido de las y’s en otra lista o arreglo,
y otros argumentos optativos. Aparte se colocan el título y el nombre de los ejes, como
primeras acciones básicas. Por ejemplo (Figura 5.1):
1 In [1]:
2 import numpy as np
3 import matplotlib . pyplot as plt
4 x = np . linspace ( -5 ,10 ,100)
5 y =3* x -2* np . sin (4* x ) +8
6 plt . figure ( facecolor = " pink " )
7 plt . plot (x ,y , c = " r " )
8 plt . xlabel ( " x " , color = " brown " , size =20)
9 plt . ylabel ( " y " , color = " brown " , size =20)
10 plt . title ( " Mi hermosa funci ó n :) " , color = " b " , size =20 ,\
11 family = " Verdana " )
12 plt . show ()
Como siempre, para ver qué hace cada función usamos help(), por ejemplo,
help([Link]).
Vamos a ver cada función:
[Link]() se coloca al inicio de cada figura, pare evitar que, al graficar, se empalme
con la anterior. Como argumento, se puede poner el tamaño de la figura,
figsize=(10,8), por decir, el color de fondo, facecolor=”pink”, entre otras.
[Link]() es la función principal para graficar. Tiene como argumentos la secuencia
5.1. [Link] 75
de números x’s, la secuencia de números y’s, y como opciones el color o c, que puede
recibir cualquier notación de color, como nombres, “red”, sistema RGB, (0, 1, 0), etc.
Para tener una lista de nombres de colores, se puede checar
[Link] También aquí va el estilo de línea
linestyle o ls=”dashed”, etc.
[Link]() y [Link]() son para darle nombre a los ejes, con argumentos op-
tativos el tamaño de la letra, fontsize o size, el tipo de letra, family, entre otras.
[Link](), para el título, con argumentos similares a [Link]().
[Link]() es para mostrar la figura. No es obligatoria en spyder, la imagen se muestra
de cualquier forma, pero, si se grafican varias figuras en un mismo programa, si se no se
pone show(), las figuras se van almacenando en memoria y se muestran hasta al final,
es mejor agregar esta función.
Si se quieren más de dos curvas en la misma figura, es conveniente poner una etiqueta
para cada gráfica, con [Link]() y label. Además, en caso de tener que imprimir
en blanco y negro, sería necesario distinguir las curvas, por lo que agregaríamos el tipo
de línea con ls o linestyle, que soporta los tipos “dotted” (“-“), “dashed” (“--”),
“dotted” (“:”) y “dashdot” (“-.”):
1 In [2]:
2 x = np . arange ( -1 ,1 ,0.1)
3 ys =[ x , x **2 , x **3]
4 etiquetas =[ " lineal , $x$ " , " cuadr á tica , $x ^2 $ " , " c ú bica , $x ^3 $ " ]
5 l í neas = [ " -" ," --" ," : " ]
6 plt . figure ()
7 for y , eti , lin in zip ( ys , etiquetas , l í neas ) :
8 plt . plot (x , y , label = eti , ls = lin )
9 plt . legend ()
10 plt . title ( " algunas funciones " , color = " darkred " )
11 plt . show ()
76 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
Vemos en este ejemplo que los textos soportan LATEX. También, no hay que especifi-
car necesariamente el color, por cada gráfica matplotlib selecciona un color diferente
(Figura 5.2).
Para que en lugar de líneas grafiquemos puntos, como datos, en lugar de plot() usamos
scatter(), con los mismos argumentos aunque ahora podemos agregar el tamaño de
los puntos, con s, de size, y, podemos agregar la forma de los puntos, con marker
(también disponible en plot, pero tiene más sentido en scatter).
Por ejemplo, veamos algunas medidas del largo de los brazos extendidos de unas perso-
nas contra sus alturas. Según Vitrubio, el cociente de estos dos números es idealmente
igual a uno y por tanto esperamos una línea recta con pendiente de alrededor de 1.
Según los datos, tenemos:
1 In [3]:
2 datos = np . array ([(1.67 ,1.77) ,(1.67 ,1.76) ,(1.60 ,1.70) ,(1.63 ,1.72) ,\
3 (1.55 ,1.54) ,(1.60 ,1.61) ,(1.59 ,1.66) ,(1.785 ,1.815) ,(1.62 ,1.745) ,\
4 (1.61 ,1.63) ,(1.54 ,1.585) ,(1.72 ,1.785) ,(1.71 ,1.785) ,(1.49 ,1.575) ,\
(1.67 ,1.70) ,(1.78 ,1.80) ])
5 plt . figure ( facecolor = " aliceblue " )
6 # la primera columna de datos son las x ’s , la segunda las y ’s
7 plt . scatter ( datos [: ,0] , datos [: ,1] , s =40 , marker = " o " ,c = " darkorange " )
8 plt . xlabel ( " altura " , color = " purple " , size =15)
9 plt . ylabel ( " brazos extendidos " , color = " purple " , size =15)
10 plt . show ()
Con los pocos datos se puede ver (Figura 5.3) que sí hay un crecimiento lineal, aunque
en general el largo de los brazos extendidos en un poco mayor que la altura.
Algunos de las formas de los puntos disponibles son “D” -diamante, “s” -cuadrado,
“*” -estrella, “^” -triángulo, “o” -círculo, y varias más que se pueden encontrar en
[Link]
Hay que remarcar en que hay que tener cuidado con las abreviaciones; c o color está
permitido dentro de plot, para la gráfica, pero para las cadenas se usa color, solamente.
Lo mismo con size, éste se usa para las cadenas, mientras que s se usa para el tamaño
5.1. [Link] 77
de los puntos. Para los colores, las abreviaciones permitidas son “r”, “g”, “b”, “c”, “m”, “y”,
“k” y “w” para el rojo, verde, azul, cian (turquesa), magenta, amarillo, negro y blanco.
Ejercicios:
Grafica en la misma figura los puntos de las funciones
π
sin (0.5x), 3 cos (2x) y 4 sin x −
4
en el intervalo (0, 5π) (da unos 300 puntos para cada gráfica, para ello usa linspace).
Aplica diferentes tipos de puntos y dale a cada gráfica una etiqueta. Para ello usa un
for con zip que tome 3 listas, una para las funciones, otra para el label, y otra pare
el marker. Agrega título y nombres a los ejes.
El plt.tight_layout() es necesario para que los nombres de los ejes no queden super-
puestos con las subfiguras de al lado. Pudieron haber sido 5 subfiguras en este ejemplo,
y en ese caso el último cuadro quedaría vacío. Hay que hallar la configuración óptima
de renglones y columnas para dar el mejor resultado visual.
Hay otra función muy popular para hacer subfiguras que es subplots(), en plural, sólo
que esta otra función en realidad es la que se utiliza en la segunda forma que tiene
[Link] para graficar, y que se refiere como el “estilo orientado a objetos”.
En esta otra forma de graficar, en lugar de llamar a las funciones directamente con
plt, se crea una figura y ejes y, con éstos últimos, que hacen de “objeto”, se llaman
a las funciones. Veamos un ejemplo primero donde se grafica una sola figura, usando
subplots() (Figura 5.5):
78 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
1 In [2]:
2 x = np . arange ( - np . pi ,2* np . pi , np . pi /24)
3 y =3* np . sin (2* x ) - np . cos ( x )
4 fig , ax = plt . subplots ( figsize =(8 ,6) , facecolor = " aliceblue " )
5 ax . plot (x ,y , color = " dodgerblue " )
6 ax . set_xlabel ( " x " , fontsize =20 , color = " r " )
7 ax . set_ylabel ( " y " , fontsize =20 , color = " r " )
8 ax . set_title ( " Con ’ ax ’" , fontsize =20 , color = " r " )
9 plt . show ()
En este ejemplo vemos que, aunque existen las mismas funciones llamando a ax o a
plt, por ejemplo plot, hay otras funciones, como las de etiquetar los ejes o dar el
título, donde se coloca el prefijo set en el modo orientado a objetos. Veamos otro
ejemplo donde hay subfiguras. Desde el inicio se da el número de renglones y columnas
en subplots, y, para acceder a cada subfigura se llama a ax y se dan las coordenadas
[renglón,columna] de la subfigura, con la numeración empezando desde 0, no desde 1
(Figura 5.6).
1 In [3]:
2 x = np . arange (0 ,10 ,0.2)
3 fig , ax = plt . subplots (2 ,2)
4 ax [0 ,0]. plot (x ,2* np . exp (0.2* x ) , color = " dodgerblue " )
5 ax [0 ,0]. set_xlabel ( " t " , color = " b " , fontsize =12)
6 ax [0 ,0]. set_ylabel ( " $Ae ^{ bt } $ " , color = " b " , fontsize =12)
7 ax [0 ,1]. plot (x ,2* np . exp ( -0.2* x ) , color = " dodgerblue " )
8 ax [0 ,1]. set_xlabel ( " t " , color = " b " , fontsize =12)
9 ax [0 ,1]. set_ylabel ( " $Ae ^{ bt } $ " , color = " b " , fontsize =12)
10 ax [1 ,0]. plot (x ,2* np . exp (0.2* x ) * np . sin (4* x ) , color = " dodgerblue " )
11 ax [1 ,0]. set_xlabel ( " t " , color = " b " , fontsize =12)
12 ax [1 ,0]. set_ylabel ( " $Ae ^{ bt }\ sin (\ omega t ) $ " , color = " b " , fontsize =12)
13 ax [1 ,1]. plot (x ,2* np . exp ( -0.2* x ) * np . sin (4* x ) , color = " dodgerblue " )
14 ax [1 ,1]. set_xlabel ( " t " , color = " b " , fontsize =12)
15 ax [1 ,1]. set_ylabel ( " $Ae ^{ - bt }\ sin (\ omega t ) $ " , color = " b " , fontsize =12)
16 plt . tight_layout ()
17 plt . show ()
80 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
plt ax
[Link]() fig, ax= [Link]()
[Link](r,c,i) fig, ax= [Link](r,c)
[Link]() ax[i,j].plot()
[Link](), [Link]() [Link](), [Link]()
[Link](), [Link](), etc. [Link](), [Link](), etc.
[Link](), [Link]() ax.set_xlabel(), [Link]()
[Link](), etc. ax.set_title(), etc.
Tabla 5.1: Dos formas de graficar, tipo Matlab y tipo orientado a objetos
En este texto vamos a seguir usando cuando se pueda la forma más directa de graficar,
con plt, pero como referencia en la Tabla 5.1 se presentan las principales diferencias
entre las dos formas.
Ejercicios:
1. En tu casa, selecciona al menos 30 libros (de primaria, la Biblia, diccionarios, lo que
encuentres), mide su largo y haz un histograma con estos datos.
Los perros de trabajo y los pequeños fueron preferidos sobre los intimidantes, como
era de imaginarse (Figura 5.8). ¿Y los datos de los estudiantes hombres? Para hacer
la comparación, podemos graficar en la misma gráfica apilando dos grupos de barras,
por cada género, pero primero normalizamos los números para tener proporciones pues
el número de mujeres y hombres es diferente. Para poder tener las barras encimadas,
hacemos la opción bottom, que por default es cero, igual a la altura de las barras
previas:
82 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
1 1 In [2]:
2 2 # convertimos la lista en arreglo para dividir f á cilmente entre el
3 # n ú mero de personas
4 muj = np . array ( muj ) /52 # 52 mujeres
5 hom = np . array ([20 ,24 ,14 ,5]) /63 # 63 hombres
6 plt . figure ()
7 plt . bar (A , muj , label = " mujeres " , color = " blueviolet " )
8 plt . bar (A , hom , label = " hombres " , bottom = muj , color = " dodgerblue " )
9 plt . legend ()
10 plt . title ( " Mi perro favorito " )
11 plt . show ()
Ejercicios:
Se le preguntó a un grupo de estudiantes universitarios si creían en un tipo de Dios.
Las respuestas posibles fueron a) sí, soy creyente, b) no, soy ateo, c) no sé, tengo dudas
existenciales. El número de respuestas por género fue:
sí creo no creo no sé total
mujeres 37 6 13 56
hombres 25 16 25 66
Haz una gráfica de barras apilada por género de las proporciones de los resultados.
(a) (b)
Figura 5.11: (a) Mostrar imágenes (Penélope) con [Link], (b) Imagen de Penélope
manipulada
tener que escribir todo el path al llamar al archivo), entonces, para mostrarla en pantalla
hacemos (Figura 5.11):
1 In [1]:
2 import matplotlib . pyplot as plt
3 penelopita = plt . imread ( " Pen é lope . jpeg " )
4 plt . figure ()
5 plt . imshow ( penelopita )
6 plt . show ()
En este ejemplo en particular, el arreglo de numpy, imagen, tiene tres dimensiones, las
dos primeras para los renglones y columnas que conforman los pixeles, y una más porque
en cada pixel no hay un simple número, sino una triada del sistema RGB de color, hay
esa tercera dimensión para escoger el contenido de rojo, de verde o de azul, [R,G,B]. Si
la figura fuera en grises, entonces sí nada más habría un número en cada pixel. También,
en la tercera dimensión puede haber 4 números, el último para la transparencia alpha,
si se usa el sistema RGBA.
Veamos qué pasa si quitamos el contenido de azul de la imagen (Figura 5.11):
1 In [2]:
2 # hacemos una copia de la imagen , y en todos los pixeles de la copia
3 # hacemos 0 el contenido de azul ( í ndice = 2)
4 penelopita2 = np . copy ( penelopita )
5 penelopita2 [: ,: ,2] = 0
6 plt . figure ()
7 plt . imshow ( penelopita2 )
8 plt . axis ( " off " )
9 plt . show ()
5.2. IMÁGENES Y PROYECCIONES EN EL PLANO 85
(a) (b)
Figura 5.12: (a) Imagen de Blanca Nieves, (b) Imagen de Blanca Nieves manipulada
Podemos cambiar de color a BlancaNieves para ponerla de color azul. Para ello, como
los colores blancuzcos se representan con colores con contenido parecido de rojo, verde
y azul, y de intensidad alta (en una escala del 0 al 255, o del 0 al 1), y los colores
negruzcos igualmente tienen contenidos similares de los tres colores, pero en intensidad
baja, podemos seleccionar pixeles donde los tres canales de color tengan una intensidad
mayor a un cierto valor, para cambiar su color. Vamos a usar [Link]() para comparar
los 3 canales de color en todos los pixeles, y obtener una sola variable booleana (Figura
5.12):
1 In [4]:
2 n , m , k = blancanieves . shape # tama ñ o de la imagen
3 azulnieves = np . copy ( blancanieves )
4 for i in range ( n ) :
5 for j in range ( m ) :
6 if np . all ( blancanieves [i , j ] >= [100 ,100 ,100]) :
7 azulnieves [i , j ]= [110 ,50 ,255]
8 plt . figure ()
9 plt . imshow ( azulnieves )
10 plt . axis ( " off " )
11 plt . show ()
86 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
Creemos ahora nuestro propio arreglo de numpy para dejar más en claro el hecho de que
las imágenes son matrices con triadas RGB como entradas. Vamos a poner diferentes
combinaciones de 1, 0 y 0.5 (puede ser cualquier fracción) a cada color y ver el resultado
(Figura 5.13). Como recordatorio de óptica, en la Tabla 5.2 se muestran los resultados
de sumar luces de diferentes colores con mayor o menor intensidad (los verdaderos
nombres y combinaciones se encuentran en la web):
1 In [5]:
2 import numpy as np
3 import matplotlib . pyplot as plt
4 im1 = np . array ([[[1 ,0 ,0] , [0 ,1 ,0] , [0 ,0 ,1] , [1 ,1 ,0]] ,\
5 [[1 ,0 ,1] , [0 ,1 ,1] , [1 ,1 ,1] , [0.5 ,0.5 ,0.5]] ,\
6 [[0 ,0 ,0] , [1 ,0.5 ,0] , [0.5 ,0 ,1] , [1 ,0 ,0.5]] ,\
7 [[0 ,0.5 ,1] , [0 ,0 ,0.5] , [0 ,0.5 ,0] , [0.5 ,0 ,0]]])
8 plt . imshow ( im1 )
9 plt . show ()
Ejercicios:
Lee una figura que descargues en el mismo directorio de un archivo Python, encuentra
su tamaño con shape, divide mentalmente la figura en cuatro cuadrantes y muestra en
pantalla la subfigura que corresponda a tomar el cuadrante superior izquierdo.
vamos a usar la función meshgrid, que hace una malla de puntos de las intersecciones
de las x’s y y’s que demos. Entonces, digamos que queremos mapear la función sin (xy):
1 In [1]:
2 x = np . linspace ( -5 ,5 ,100) # 100 puntos en el intervalo ( -5 ,5)
3 y = np . linspace ( -5 ,5 ,100)
4 X , Y = np . meshgrid (x , y )
5 z = np . sin ( X * Y )
6 plt . figure ()
7 plt . imshow ( z )
8 plt . show ()
Vamos a comentar algunas cosas. Aunque los intervalos que escogimos fueron de (−5, 5)
en los dos ejes, la figura muestra otros intervalos. También, aunque no sea claro en la
Figura 5.14, ésta está invertida, de arriba a abajo. Finalmente, no obtuvimos una figura
en grises, a pesar de no haber dado una tercera dimensión para el contendido RGB, y,
por default, nos salió esa combinación de colores, que podemos modificar.
Como ya vimos, imshow muestra imágenes de formatos png, etc. Pero, la notación usual
de las imágenes es comenzar con el cero en la esquina superior izquierda, e incrementar
la cuenta de los pixeles hacia abajo y hacia la derecha. Por ello, hay que invertir el
orden de los renglones para nuestros propósitos, seleccionando origin=”lower”. Luego,
escogimos 100 puntos para cada eje, y eso es lo que vemos en la figura, donde se
cuenta el número de pixeles. Hay que meter con la mano el intervalo deseado con
extent[xmin,xmax,ymin,ymax]. Sobre la versión de los colores, podemos escoger una
gama variada de colormaps con la función cmap. La versión por default es “viridis”,
y la gama completa está en
[Link] Vamos a darle un
toque de frozen a la figura escogiendo la versión “cool”. Finalmente, se puede agregar
una barra para indicar el valor de la función dependiendo del color, con la función
colorbar (Figura 5.15):
1 In [2]:
2 plt . figure ()
3 plt . imshow (z , extent =[ -5 ,5 , -5 ,5] , origin = " lower " , cmap = " cool " )
88 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
Figura 5.15: Proyección en el plano de z = sin xy, con barra de colores y colormap cool
4 plt . colorbar ()
5 plt . show ()
En este ejemplo, quedan claras las curvas de nivel, pero, ¿siempre es así?, intentemos
otra función:
1 In [3]:
2 x = np . linspace ( -5 ,5 ,100) # 100 puntos en el intervalo ( -5 ,5)
3 y = np . linspace ( -5 ,5 ,100)
4 X , Y = np . meshgrid (x , y )
5 z = (X - Y ) /(1+ X **2+ Y **2)
6 plt . figure ()
7 plt . imshow (z , extent =[ -5 ,5 , -5 ,5] , origin = " lower " , cmap = " winter " )
8 plt . colorbar ()
9 plt . show ()
La Figura 5.16 queda muy bonita, parece que 2 cometas se van a encontrar. De la barra
de colores vemos que la zona con el verde más claro y brillante es donde hay un máximo
e, igualmente, el azul más brillante es para donde hay un mínimo. Pero las fronteras
5.2. IMÁGENES Y PROYECCIONES EN EL PLANO 89
están tan difuminadas que no parece que haya curvas de nivel. En realidad, la gráfica
que obtuvimos es un “mapa de calor”, heat map, que sí nos da idea sobre las curvas de
nivel en algunos de los casos, pero para ese propósito en específico está contruida la
función contour, que se puede usar sola o junto con imshow (o alguna otra función),
para el delineado claro de las curvas. Además de z, se debe agregar la malla de puntos
X y Y como argumentos de contour (Figura 5.17):
1 In [4]:
2 plt . figure ()
3 plt . imshow (z , extent =[ -5 ,5 , -5 ,5] , origin = " lower " , cmap = " winter " )
4 plt . colorbar ()
5 plt . contour (X ,Y , z )
6 plt . show ()
Otra función que tiene el mismo rol que imshow en las gráficas heat map es pcolormesh,
que es más fácil de usar pues no hay que invertir la imagen, y no hay que especificar el
intervalo de valores (Figura 5.18):
1 In [5]:
2 x = np . linspace ( -5 ,5 ,100) # 100 puntos en el intervalo ( -5 ,5)
3 y = np . linspace ( -5 ,5 ,100)
4 X , Y = np . meshgrid (x , y )
5 z = np . sin ( X ) - np . sin ( Y )
6 plt . figure ()
7 plt . pcolormesh (X ,Y ,z , cmap = " rainbow " )
8 plt . colorbar ()
9 plt . contour (X ,Y , z )
10 plt . show ()
Ejercicios:
Haz las gráficas de las curvas de nivel de las funciones
Figura 5.18: Proyección y curvas de nivel de la función z = sin x − sin y, con pcolormesh
Vamos a explicar las nuevas funciones. El [Link]() se refiere a “get current axes”
(o usar [Link]()) y significa que vamos a tomar los ejes, darles un nombre, ax, y
tratarlos como objeto. El argumento de gca() es la proyección que se va a usar, aquí
es “3d”, pero podría ser “polar” para coordenadas polares. En la siguiente línea, usamos
5.3. GRÁFICAS EN TRES DIMENSIONES 91
el método del objeto ax para graficar, que es scatter3D porque son puntos. Y en la
siguiente línea, usamos el método para dar título, nombre de ejes y otros, al mismo
tiempo, [Link](). Equivalentemente, para poder usar opciones en los títulos, nombres
de ejes, y otras, podemos aplicar independientemente las funciones ax.set_xlabel(),
ax.set_title(), etc, como ya se había comentado.
Ejercicios:
Grafica la siguiente curva paramétrica:
La figura admite tanto color, para colores sólidos, como cmap para degradados.
Ejercicios:
a) z = y 2 − x2 , b) z = (x − y)2 , y c) z = |xy|
5.4. ANIMACIONES 93
5.4. Animaciones
Hay módulos especiales en Python para animaciones, pero podemos hacer animaciones
simples con pyplot en el modo interactivo, así que en esta sección cambiamos a ese
modo como se explicó al principio del capítulo. La función para lograrlo es pause, que
va a esperar el tiempo que le demos en el argumento, en segundos, para dibujar el
siguiente cuadro.
Vamos a ejemplificar con los llamados autómatas celulares, donde hay unas celdas, en
general en 2D, y esas celdas siguen instrucciones de acuerdo con lo que está alrededor.
Para simular las celdas vamos a usar una matriz y representarla como imagen con
imshow.
Empezamos con la regla de paridad, donde las instrucciones son:
- los vecinos de una celda son las celdas de arriba, abajo, izquierda y derecha
- la celda está prendida (1) o apagada (0)
- la celda se prende cuando la suma de sus vecinos es impar, se apaga cuando es par.
Para una celda dada con índices (i,j), los vecinos de arriba y abajo son, respectiva-
mente, las celdas (i-1,j) y (i+1,j), mientras que los vecinos izquierdo y derecho son
las celdas con índices (i,j-1) y (i,j+1).
Vamos a empezar con una sola celda prendida y con condiciones periódicas a la frontera,
esto es, los vecinos a la derecha de las celdas de la última columna son las celdas de la
primera columna, y lo mismo con el primer y último renglones. Cuando hay este tipo
de condiciones se usa el módulo ( %), con módulo el tamaño del intervalo. Por ejemplo,
si hay 5 columnas, la vecina derecha o “sexta” columna pasa a ser la primera. El índice
de la “sexta” columna es 5 y el intervalo es también de tamaño 5, por lo que 5 %5 = 0
nos retorna a la primera columna con índice 0. Entonces:
1 In [1]:
2 import numpy as np
3 import matplotlib . pyplot as plt
4 n =100; m =100
5 # matriz de 100 x100 llena de ceros :
6 imagen = np . zeros (( n , m ) )
7 # un solo punto prendido , en el centro
8 imagen [50 ,50]= 1
9 plt . figure ()
10 # damos unos 30 cuadros de animaci ó n :
11 for _ in range (30) :
12 # muestra la imagen :
13 plt . imshow ( imagen , cmap = " autumn " )
14 # espera medio segundo :
15 plt . pause (0.5)
16 # copia lo que ten í as en la imagen porque se va a ir modificando :
17 old = imagen . copy ()
18 for i in range ( n ) :
19 for j in range ( m ) :
20 # suma de tus vecinos , arriba , abajo , etc :
21 t = old [( i -1) %n , j ] + old [( i +1) %n , j ] + \
22 old [i ,( j -1) %m ] + old [i ,( j +1) %m ]
23 if t %2==0:
94 CAPÍTULO 5. IMÁGENES Y GRÁFICAS
24 imagen [i , j ]=0
25 else :
26 imagen [i , j ]=1
27 plt . show ()
Ejercicios:
Annealing rule. Simula lo que pasa a unas celdas con la annealing rule, donde las
reglas son:
- los vecinos de una celda son los 8 alrededor
- la celda está prendida (1) o apagada (0)
- si la suma de los vecinos a una celda es menor o igual a 3, o igual a 5, se prende; si es
igual a 4 o mayor o igual a 6, se apaga.
Comienza con una matriz llena de 0’s y 1’s al azar con
[Link](0,2,(n,m)), con n,m el tamaño de la matriz, y con condiciones
periódicas a la frontera.
Juego de la vida de Conway. Conway desafortunadamente murió por coronavirus el
11 de abril de 2019 y entre sus legados dejó este juego de la vida de autómatas celulares.
Las reglas son:
- los vecinos de una celda son los 8 alrededor
- la celda está prendida (1) o apagada (0)
- una celda apagada con 3 vecinas prendidas, se prende; una celda prendida con 2 o 3
vecinas prendidas permanece prendida, de otra forma, se apaga.
Comienza con una matriz llena de 0’s excepto por el patrón en el centro. Aunque puede
haber varias configuraciones de inicio , usa como patrón para las celdas encendidas el
de la Figura 5.22 (el rojo es la celda encendida, como el tercer renglón):
6 Scipy y aplicaciones en matemáticas
En este capítulo veremos el módulo scipy que es un módulo para cálculo científico.
Tiene aplicaciones para Ecuaciones Diferenciales, Estadística, Integrales, Optimización,
Interpolación, más de Álgebra Lineal, Series de Fourier, Funciones especiales, Procesa-
miento de señales, entre otras. Este libro no es tan ambicioso y sólo veremos algunas
aplicaciones de Ecuaciones Diferenciales, Estadística y Regresión Lineal. Estos temas
están presentes de manera explícita en los programas de estudio de algunas materias
de la licenciatura de Modelación Matemática, o carreras similares, pero también están
presentes y son de utilidad para las ingenierías. Los objetivos de aprendizaje de este
capítulo son aplicar las funciones de scipy en diversos problemas de los ámbitos de las
Ecuaciones Diferenciales Ordinarias y de la Probabilidad y Estadística. También, hacer
un repaso de los fundamentos teóricos de la Inferencia Estadística y las Pruebas de
Hipótesis. Hacer un repaso de la derivación de las ecuaciones necesarias para hacer una
Regresión Lineal. Construir nuestras propias funciones para la resolución numérica de
Ecuaciones Diferenciales Ordinarias y para hacer Regresiones Lineales con el método de
mínimos cuadrados. Para ahondar más en la parte teórica de estos temas de matemá-
ticas, en la bibliografía se recomiendan los libros del área de Ecuaciones Diferenciales y
Modelación Matemática de Dennis Zill (2018) y de Barnes y Fulford (2015), los libros
de Álgebra Lineal de David Lay (2016) y de Bernard Kolman y David Hill (2006), y
el curso en línea de Estadística en Coursera de Mine Cetinkaya, de la Universidad de
Duke.
En el submódulo integrate del módulo scipy de Python, hay varias funciones para
resolver ecuaciones diferenciales de manera numérica, aunque sólo comentaremos la fun-
ción odeint. Pero antes de eso, implementemos nuestras propias funciones para resolver
Ecuaciones Diferenciales, primero para una ecuación con una variable dependiente, y
después para un sistema de dos ecuaciones, utilizando el método de Runge-Kutta de
cuarto orden.
95
96 CAPÍTULO 6. SCIPY Y APLICACIONES EN MATEMÁTICAS
h
xn+1 = xn + h, yn+1 = yn + (k1 + 2k2 + 2k3 + k4 )
6
h h
k1 = f (xn , yn ), k2 = f (xn + , yn + k1 ),
2 2
h h
k3 = f (xn + , yn + k2 ), k4 = f (xn + h, yn + hk3 )
2 2
Hagamos el ejemplo de una función analítica que se puede resolver fácilmente, como un
decaimiento exponencial (cambiamos nombres a las variables, en lugar de x, y, es t, x,
para enfatizar el tiempo):
dx
= −kx, x(0) = 3, k = 0.5/día
dt
1 In [2]:
2 def decaimiento (t , x ) :
3 return ( -0.5* x )
1 In [3]:
2 # t0 =0 , tf =5 , x0 =3 , h =0.05 , fun = decaimiento
3 t , x = Runge (0 ,5 ,3 ,0.05 , decaimiento )
4 plt . figure ( facecolor = " mistyrose " )
5 plt . plot (t ,x , c = " r " )
6 plt . xlabel ( " t " , fontsize =25)
7 plt . title ( " $x =3 e ^{ -0.5 t } $ " , fontsize =25)
8 plt . show ()
Hagamos otro ejercicio, el del crecimiento de una población con restricciones (crecimien-
to dependiente de la densidad de población), cuya ecuación es la ecuación logística:
dx x
= rx 1 −
dt K
m1 = f (tn , xn , yn ), k1 = g(tn , xn , yn )
h h h h h h
m2 = f tn + , xn + m1 , yn + k1 , k2 = g tn + , xn + m1 , yn + k1 ,
2 2 2 2 2 2
h h h h h h
m3 = f tn + , xn + m2 , yn + k2 , k3 = g tn + , xn + m2 , yn + k2 ,
2 2 2 2 2 2
m4 = f (tn + h, xn + hm3 , yn + hk3 ), k4 = g(tn + h, xn + hm3 , yn + hk3 )
5 x [0]= x0 ; y [0]= y0
6 for i in range ( len ( t ) -1) :
7 m1 , k1 = fun ( x [ i ] , y [ i ])
8 m2 , k2 = fun ( x [ i ]+ h * m1 /2 , y [ i ]+ h * k1 /2)
9 m3 , k3 = fun ( x [ i ]+ h * m2 /2 , y [ i ]+ h * k2 /2)
10 m4 , k4 = fun ( x [ i ]+ h * m3 , y [ i ]+ h * k3 )
11 x [ i +1]= x [ i ]+ h *( m1 +2* m2 +2* m3 + m4 ) /6
12 y [ i +1]= y [ i ]+ h *( k1 +2* k2 +2* k3 + k4 ) /6
13 return (t ,x , y )
dx dy
= βx − c1 xy, = c2 xy − αy, x(t0 ) = x0 , y(t0 ) = y0
dt dt
Donde x denota a la presa, y al depredador, y con c1 y c2 los términos de interacción
entre la presa y el depredador que hace que disminuya la presa y aumenten los de-
predadores, respectivamente, β el término de crecimiento de la presa y α el término
de disminución de los depredadores. Seleccionemos para nuestro ejemplo los valores
β = 1/año, α = 0.5/año, c1 = 0.01/año, c2 = 0.004/año.
Definimos entonces la función para la presa-depredador en Python y llamamos a la
función de Runge-Kutta con un tamaño de paso h = 0.05 y con unos valores iniciales
de x(0) = 200 presas, y(0) = 100 depredadores, y un tiempo final de 20 años, por decir.
Graficamos x y y vs t (Figura 6.3):
1 In [6]:
2 beta =1; alpha =0.5; c1 =0.01; c2 =0.004
3 def presa_dep (x , y ) :
4 return ( beta *x - c1 * x *y , c2 * x *y - alpha * y )
5 # t0 =0 , tf =20 , x0 =200 , y0 =100 , h =0.05 , fun = presa_dep
6 t , x , y = Runge_2 (0 , 20 , 200 , 100 , 0.05 , presa_dep )
7 plt . figure ()
8 plt . plot (t , x , label = " presa " , ls = " -" )
9 plt . plot (t , y , label = " depredador " , ls = " -. " )
10 plt . legend ( fontsize =18)
11 plt . xlabel ( " t ( a ñ os ) " , fontsize =20)
12 plt . show ()
Figura 6.4: Soluciones en el espacio fase del sistema presa-depredador, para diferentes
condiciones inciales del depredador. Todas las soluciones orbitan alrededor de un punto
de equilibrio, expresado en términos de los parámetros del sistema.
6.1. SOLUCIONES NUMÉRICAS A EDO 101
Como se aprecia, las curvas solución en el espacio fase, dadas las diferentes condiciones
iniciales, todas circularían alrededor de un punto de equilibrio estable, dado por los
parámetros del sistema. El tema de la estabilidad de los puntos críticos amerita otro
libro, pero para no dejar al lector con la duda podemos al menos hablar sobre cómo
determinar los puntos críticos.
Para ello, las ecuaciones diferenciales tienen que igualarse a cero, como es la costumbre.
Para el sistema presa depredador tendríamos:
dx dy
= βx − c1 xy = x(β − c1 y) = 0, = c2 xy − αy = y(c2 x − α) = 0
dt dt
Si x = 0, entonces dx/dt es igual a cero, pero para que dy/dt también sea cero al mismo
tiempo, necesariamente y tendría que ser cero. Un punto crítico es entonces (0, 0). La
otra posibilidad es que, para que dx/dt sea cero, y = β/c1 , y entonces para que dy/dt
sea cero también al mismo tiempo, necesariamente x = α/c2 . Insertando los valores de
los parámetros, el punto crítico del ejemplo que vimos es:
1 In [8]:
2 x_crit = alpha / c2 ; y_crit = beta / c1
3 print ( " punto cr í tico : " )
4 print ( x_crit , y_crit )
5 Out [8]:
6 punto cr í tico :
7 125.0 100.0
Veamos el caso cuando las poblaciones iniciales son x(0) = 200 presas y y(0) = 30
depredadores, los parámetros son los mismos que en el ejercicio de la presa-depredador,
6.1. SOLUCIONES NUMÉRICAS A EDO 103
Vemos que en el argumento de deri, ponemos una sola variable z que después desglo-
samos en las variables requeridas en el primer renglón de la función. Lo que retorna
odeint son 2 vectores columna donde cada vector es una secuencia de números con
la solución a cada variable, del mismo tamaño que el arreglo t, por lo que hay que
seleccionar cada columna para graficar (Figura 6.6):
1 In [2]:
2 plt . figure ()
3 # x = z [: ,0] , primer columna , y = z [: ,1] , segunda columna
4 plt . plot (t , z [: ,0] , label = " presa " , ls = " -" ,c = " orange " )
5 plt . plot (t , z [: ,1] , label = " depredador " , ls = " : " ,c = " brown " )
6 plt . xlabel ( " t ( a ñ os ) " , fontsize =20)
7 plt . legend ( fontsize =20)
8 plt . show ()
Vemos que el tamaño de las dos poblaciones oscila con el tiempo y eventualmente llegan
a un valor de equilibrio. Podemos graficar también las dos soluciones en el espacio fase
(Figura 6.7):
104 CAPÍTULO 6. SCIPY Y APLICACIONES EN MATEMÁTICAS
Figura 6.7: Espacio fase del sistema presa-depredador con recursos limitados para la
presa.
1 In [3]:
2 plt . figure ( facecolor = " aliceblue " )
3 plt . plot ( z [: ,0] , z [: ,1] , c = " purple " )
4 plt . xlabel ( " presa " , fontsize =20)
5 plt . ylabel ( " depredador " , fontsize =20)
6 plt . show ()
Vemos aquí también la oscilación de las soluciones para acercarse a un punto estable o
atractor. Se deja al lector que encuentre el valor del punto crítico.
Ejercicios:
1. Dado el modelo SIR (Susceptibles-Infectados-Recuperados) para el contagio de una
enfermedad curable, donde las ecuaciones están dadas por:
dS dI dR
= −βSI, = βSI − γI, = γI
dt dt dt
encuentra las soluciones numéricas en un periodo de 20 días dadas las condiciones
iniciales de que hay 950 personas susceptibles en un pueblo, 1 persona infectada y 0
personas recuperadas. Los valores de los parámetros son β = 0.0015/día y γ = 0.4/día.
Dibuja las soluciones con respecto al tiempo, para ese intervalo de tiempo.
2. Dibuja las soluciones del problema de dos especies que compiten por recursos, a)
con respecto al tiempo y b) en el espacio fase, en un periodo de tiempo de 5 años. Las
ecuaciones que describen a las dos especies en competencia son:
dx dy
= a1 x − b1 x2 − c1 xy, = a2 y − b2 y 2 − c2 xy
dt dt
donde, para cada ecuación, el primer término es el término de crecimiento, el segundo
es el de la competencia entre la misma especie, y el tercero es la competencia entre las
2 especies. Los valores de los parámetros son:
a1 = 0.2, a2 = 0.09, b1 = 0.007, b2 = 0.01, c1 = 0.005, c2 = 0.004
6.2. DISTRIBUCIONES DE PROBABILIDAD 105
todos por unidad de año. Supón que las poblaciones inicales son x(0) = 1, 000 y y(0) =
900.
Por ejemplo, supongamos que en un fin de semana un canal propone dos películas para
el horario estelar, y la que tenga más votos se proyecta. Los resultados de la votación son
un 42 % para Harry Potter y 58 % para Terminator. Si se seleccionan seis personas al
azar, ¿cuál es la probabilidad de que exactamente tres de ellas voten por Harry Potter?
Con Python es fácil responder a esta pregunta. La distribución binomial se accede me-
diante la función binom, y la pregunta se contesta usando la función pmf, con argumento
número de éxitos k = 3, y dando también como parámetros el número de ensayos y la
probabilidad de éxito n y p:
1 In [1]:
2 from scipy . stats import binom
3 # n ú mero de intentos , probabilidad de é xito y n ú mero de é xitos
106 CAPÍTULO 6. SCIPY Y APLICACIONES EN MATEMÁTICAS
4 n =6; p =0.42; k =3
5 print ( binom . pmf (k ,n , p ) )
6 Out [1]:
7 0 .2 8 9 10 9 15 7 12 0 0 00 0 7
Si ahora se pregunta por la probabilidad de que a lo más tres personas hayan votado
por la película de Harry Potter, la función más apropiada para responder es la cdf,
pues suma las probabilidades desde k = 0 hasta k = 3 éxitos:
1 In [2]:
2 print ( " a lo m á s k : " , binom . cdf (k ,n , p ) )
3 Out [2]:
4 a lo m á s k : 0.79201423936
Ejercicios:
Según datos de la Secretaría de Salud del 2020 en México, 73 % de los adultos tienen
obesidad. Si se escogen ocho adultos al azar,
a) ¿Cuál es el número esperado de adultos con obesidad? Usa la función mean con
argumento k, n y p.
b) ¿Cuál es la probabilidad de que todos ellos tengan obesidad?
c) ¿Cuál es la probabilidad de que al menos seis tengan obesidad?
12 plt . title ( " Estatura adultas mayores deportistas " , color = " indigo " ,\
fontsize =18)
13 plt . xlabel ( " estatura " , color = " indigo " , fontsize =20)
14 plt . show ()
Ejercicios:
El promedio del coeficiente intelectual de la gente es de 100, con desviación estándar
igual a 15, ¿cuál es la probabilidad de que una persona puntúe más de 140 puntos?, ¿y
la probabilidad de que su coeficiente esté entre 110 y 130?
Esto significa que, si tomamos una sola muestra, como es el caso en la vida real, hay
un 68 % de probabilidad de que el promedio que midamos esté entre −1 SE y +1 SE
del promedio real. La forma de proceder, entonces, es, tomar el promedio de nuestra
muestra, y agregar un margen de error donde se trate de capturar el promedio real de la
población con una cierta probabilidad, que se llama nivel de confianza, CL (confidence
level). Así, si queremos 68.3 % de nivel de confianza, reportamos la medición como x ± 1
SE, al 68.3 % de nivel de confianza, y si queremos el 95 % reportamos la medida como
x ± 1.96 SE, al 95 % de nivel de confianza (no es exactamente 2 SE). Esto nos construye
los intervalos de confianza.
Para proceder con los cálculos, hay que pasar primero de la variable de nuestro pro-
blema x a una variable estandarizada z que justo mide a cuántos errores estándar (o
desviaciones estándar usualmente, depende del problema) estamos del promedio, y el
cambio de x a z está dado por
x − x̄
z= .
SE
Así, el centro de la distribución es z = 0, y z = 1 corresponde a 1 SE.
Entonces, dado un nivel de confianza, para encontrar el valor crítico z ∗ correspondeinte
al intervalo x ± z ∗ SE, nos ayudamos de la Figura 6.11. La función φ(z) que se muestra
no es más que la probabilidad acumulada de −∞ hasta z. Si z > 0, entonces φ(−z)
corresponde a la cola izquierda de la gaussiana. De las figuras, se ve que el área central
que nos interesa, CL, es igual a
Figura 6.11: Forma pictórica de ver que, por un lado, la parte central del CL es igual a
1 − 2φ(−z), y que φ(−z) = 1 − φ(z).
1 In [1]:
2 from scipy import stats
3 print ( stats . norm . ppf ((1+0.95) /2) )
4 Out [1]:
5 1.95 996398454 0054
6.3.1. Promedios
Vamos a hacer el ejemplo de obtener un intevalo al 95 % de nivel de confianza del pro-
medio de las masas de los estudiantes universitarios varones en el plantel de Iztapalapa,
con 32 datos y usando la distribución t de student para ser un poco más conservativos.
Los datos:
1 In [4]:
2 A = np . array ([49.5 , 90 , 89.5 , 77 , 78 , 54.5 , 110.5 , 65 , 65.5 , 62 , 60 ,\
66.5 , 67.5 , 98 , 67 , 73 , 86 , 67 , 83 , 75.5 , 64.5 , 66 , 71.5 , 82 , 85 ,\
61.5 , 102 , 103 , 77.5 , 112 , 87 , 86.5])
4 Out [5]:
5 (77.609375 , 15.631865679098416 , 32)
Por lo que tenemos un 95 % de confianza de que el promedio de las masas de todos los
estudiantes varones del plantel Iztapalapa está en el intervalo entre 77.6 ± 5.6 kg, es
decir, que está entre 72.0 kg y 83.2 kg.
6.3.2. Proporciones
Si lo que ahora medimos es una proporción p, como cuando tenemos variables categóri-
cas, se trabaja de la misma
q forma que en la sección anterior pero ahora el error estándar
está dado por SE = p(1−p) n
. Digamos que queremos saber a qué proporción de perso-
nas les gusta la cebolla en el plantel Iztapalapa. Se les preguntó a 116 personas, entre
estudiantes, profesores y administrativos, de las que 86 contestaron que sí les gusta y
30 no. Entonces, la proporción de personas a las que sí les gusta la cebolla:
1 In [8]:
2 p = 86/116
3 print ( p )
4 Out [8]:
5 0. 74 13 793 10 34 482 76
µ ̸= a 2 (1 − φ(z)), 2*([Link](z)),
si z > 0, o si z > 0, o
2φ(z) si z < 0 2*[Link](-z)
si z < 0
Ejercicios:
Encuentra el intervalo al 95 % de confianza de la proporción de adultos a los que les
gusta el ajo, si se les preguntó a 116 adultos y 72 contestaron que sí, los demás que no.
Desde que vemos una z tan grande, ya sabemos que el p_value debe ser muy pequeño,
sólo hay que recordar que con z = 1.96 ya tenemos 95 % de probabilidad en el medio
y queda α = 5 % en las colas. Por ser p_value menor que α, rechazamos la H_0, y
hay suficiente evidencia para decir que los 2 tipos de poblaciones muestran diferente
habilidad en la lectura.
Ejercicios:
Según los datos de la prueba de PISA de 2015 y considerando a todos los países in-
volucrados, los resultados en matemáticas muestran que las niñas promedian un valor
de 486 puntos con un SE de 0.5, y los niños promedian 494 puntos, con un SE de 0.6.
¿Se puede decir que hay una diferencia por género estadísticamente significativa en los
resultados de PISA en matemáticas?
1 In [1]:
2 nh =61; nm =55
3 ph =32/ nh ; pm =33/ nm
4 print ( " ph , pm y ( ph - pm ) : " , ph , pm , ph - pm )
5 Out [1]:
6 ph , pm y ( ph - pm ) : 0.5 24 59 016 39 34 42 63 0.6 -0.07540983606557372
La diferencia es negativa, podríamos tomar el valor absoluto pero dejémoslo así para
saber tratar todos los casos. Ahora, como vamos a proponer una proporción p para hallar
el SE, lo que se suele usar, cuando hay una diferencia de proporciones independientes,
es no favorecer a ningún grupo y usar la proporción del total de los que dicen que sí,
respecto al total de entrevistados, que se llama p_pool, pp = (32 + 33)/(61 + 55), y así
el SE se vuelve: s
pp (1 − pp ) pp (1 − pp )
SE = +
nh nm
Como el p_value no es menor que α = 0.05, no hay evidencia suficiente para rechazar
la H_0, no podemos decir que hay diferencia por género en el gusto por el heavy metal
para el grueso de estudiantes del plantel Iztapalapa.
Ejercicios:
¿Hay alguna diferencia por género en el gusto de estudiantes universitarios en Iztapalapa
por el reggaetón? Los datos obtenidos es que a 21 de 55 mujeres sí les gusta, y a 26 de
61 hombres también.
En la última ecuación
P usamos la definición del promedio o valor esperado de una va-
i xi
riable, ⟨x⟩ = n . Es de esperarse el resultado, nos está diciendo que, en promedio,
⟨y⟩ = m⟨x⟩ + b. Derivando respecto al otro parámetro:
∂f X X X
= 2m x2i − 2 xi yi + 2b xi = 0
∂m i i i
Hay otro método, usando Álgebra Lineal, que es eficiente y que se puede escalar fácil-
mente a cuando hay más variables involucradas. El método consiste en escribir las n
ecuaciones yi = mxi + b + ξi en forma matricial:
1 x1 ξ1 y1
1 x2 ξ2 y2
b
.. .. + .. = ..
. . m . .
1 xn ξn yn
Ap + ξ = y (6.2)
Con p el vector con los parámetros (b, m), y y ξ los vectores de las y’s y de los errores, y A
la matriz de n renglones con primera columna una lista de unos, y la segunda columna el
vector x. La estrategia del Álgebra Lineal es ver a ξ como un vector ortogonal al espacio
generado por las columnas de A, de modo que al hacer el producto de la traspuesta de
A con la ecuación (6.2), el término del error se elimine:
AT ξ = 0 (6.3)
Desarrollando la ecuación (6.3) (con ξi = yi −mxi −b) se obtienen los mismos resultados
que en las ecuaciones (6.1). Para imaginarnos la situación, es como si nos aproximáramos
a un punto fuera de una recta. Nos movemos por la recta y lo que resta para llegar al
punto es la distancia del punto a la recta, que sabemos que es mínima en la dirección
perpendicular del punto a la recta. O si nos imaginamos a un punto fuera de un plano, y
queremos aproximarnos al punto. Nos movemos por el plano, y lo que resta para llegar
al punto, si queremos que sea mínimo, es la distancia en la dirección perpendicular del
punto al plano. De esta forma, ξ se minimiza cuando es perpendicular a Ap.
Multiplicando a la izquierda la ecuación (6.2) por AT , y usando la ecuación (6.3) tene-
mos:
AT Ap = AT y (6.4)
La ecuación (6.4) es muy sencilla para resolver, pues si sólo tenemos una variable inde-
pendiente y A es una matriz de n × 2, entonces AT A tiene un tamaño de 2 × 2 y AT Ap
es un vector de tamaño 2. En general, si hay r variables independientes, AT A es una
matriz de tamaño (r + 1) × (r + 1).
Vamos a hacer un ejemplo con las medidas sugeridas por el hombre de Vitrubio. Medi-
mos la extensión de los brazos y la altura de 33 personas. Según Vitrubio, las medidas
deben ser las mismas.
118 CAPÍTULO 6. SCIPY Y APLICACIONES EN MATEMÁTICAS
1 In [1]:
2 import numpy as np
3 import matplotlib . pyplot as plt
4 puntos = np . array ([(1.67 ,1.77) ,(1.67 ,1.76) ,(1.60 ,1.70) ,(1.63 ,1.72) ,\
5 (1.55 ,1.54) ,(1.60 ,1.61) ,(1.59 ,1.66) ,(1.785 ,1.815) ,(1.62 ,1.745) ,\
6 (1.61 ,1.63) ,(1.54 ,1.585) ,(1.72 ,1.785) ,(1.71 ,1.785) ,(1.49 ,1.575) ,\
7 (1.67 ,1.70) ,(1.78 ,1.80) ,(1.52 ,1.575) ,(1.73 ,1.885) ,(1.62 ,1.68) ,\
8 (1.68 ,1.765) ,(1.66 ,1.725) ,(1.45 ,1.485) ,(1.71 ,1.79) ,(1.73 ,1.84) ,\
9 (1.65 ,1.68) ,(1.58 ,1.685) ,(1.63 ,1.635) ,(1.73 ,1.73) ,(1.58 ,1.56) ,\
10 (1.55 ,1.595) ,(1.625 ,1.67) ,(1.715 ,1.83) ,(1.49 ,1.55) ])
11 x = puntos [: ,0] # la primera columna de puntos son las xs
12 y = puntos [: ,1] # la segunda columna son las ys
Usemos primero el método de Álgebra Lineal con la ecuación (6.4). Para ello, prepara-
mos la matriz A de n × 2 con puros unos, con [Link], y después la segunda columna
la reemplazamos con el vector x:
1 In [2]:
2 A = np . ones (( len ( x ) ,2) )
3 A [: ,1]= x # reemplazamos la segunda columna por el vector x
4 C = np . dot ( A .T , A ) # hacemos el producto de A traspuesta y A
5 c = np . dot ( A .T , y ) # hacemos el producto de A traspuesta y y
6 # resolvemos el sistema de ecuaciones Cp =c , con p =( b , m )
7 b , m = np . linalg . solve (C , c )
8 print (m , b )
9 Out [2]:
10 1. 08 72 209 25 80 082 34 -0.08257271475082933
La recta quedó bien ajustada, y en la Figura 6.13 se observa que, al menos entre los
participantes, hubo una tendencia a tener brazos más largos respecto a su estatura,
según el ideal de Vitrubio.
Comparemos con el cálculo directo en términos de varianzas, covarianzas y promedios,
Ecs. (6.1):
1 In [4]:
2 # calculamos sigma_xy y sigma ^2 _x con la matriz de covarianza ,
3 # en la diagonal principal est á n las varianzas de x y y
4 # y en la diagonal cruzada la covarianza entre x y y
5 m2 = np . cov (x , y ) [0 ,1]/ np . cov (x , y ) [0 ,0]
6 b2 = np . mean ( y ) - m2 * np . mean ( x )
7 print ( m2 , b2 )
8 Out [4]:
9 1. 08 72 209 25 80 086 56 -0.08257271475089834
6.5. REGRESIÓN LINEAL CON MÍNIMOS CUADRADOS 119
Figura 6.13: Datos medidos del largo de los brazos extendidos respecto a la altura de
algunas personas. Se muestra la recta ajustada mediante mínimos cuadrados vs la recta
ideal y = x.
Aunque el cálculo con las varianzas, covarianzas y promedios fue más directo y simple,
usando un programa de cálculo como en Python, se prefiere el método de Álgebra Lineal
para la generalización sencilla a más variables.
Para terminar esta sección, hablemos del caso en que la variable dependiente no dependa
linealmente de la variable independiente o variables independientes. En este caso se
podrían hacer cambios de variables para que las potencias de todas las nuevas variables
sean uno.
Pongamos el ejemplo del péndulo simple. Haciendo un diagrama de cuerpo libre, se
llega a la siguiente relación entre el periodo del péndulo T (tiempo que tarda en hacer
una oscilación) y su largo L:
s
L
T = 2π
g
√
Donde g es la gravedad del planeta. Se puede entonces cambiar a la variable z = L y
la ecuación queda lineal:
2π
T =√ z
g
Hay formas de linearizar ecuaciones, como usar logaritmos, pero ese es un tema que
amerita sus propias notas.
Ejercicios:
1. Con un grupo de compañeros, checa la aseveración de que el tamaño de la planta de
los pies es el mismo que el de los antebrazos (del codo a la muñeca). Haz unas mediciones
120 CAPÍTULO 6. SCIPY Y APLICACIONES EN MATEMÁTICAS
y encuentra la pendiente de la recta que mejor se ajusta. Haz más experimentos como
relacionar la estatura de las personas con su masa.
2. Juanita tiene que presentar un proyecto de ciencias y se le ocurre a última hora hacer
un experimento con un péndulo para medir la gravedad de la Tierra. Hace mediciones
de diferentes longitudes de una cuerda, amarrada a una masita, que funciona como
péndulo, y sus respectivos periodos de oscilación. Cambia a una √ nueva variable, que
es la raíz de la longitud, para ajustar la mejor recta de T vs L y encontrar así la
gravedad. ¿Qué valor de la gravedad encontró Juanita si obtuvo las siguientes medidas?
(Spoiler: no le salió 9.8 m⁄s2 ).
6.5.2. [Link]
En esta sección vamos a ver la función linregress del submódulo stats del módulo
scipy, para hacer una regresión lineal. Esta función retorna 5 valores: la pendiente m,
la ordenada al origen b, el coeficiente de correlación R, el p_value y el error estándar
SE.
Como recordatorio, el coeficiente de correlación mide qué tan relacionados están x y y.
Si los puntos medidos están todos sobre la recta propuesta, y la pendiente es positiva,
entonces R = 1. Si están sobre la recta propuesta pero la pendiente es negativa, R = −1.
R es un número entre −1 y 1, entre mayor sea el valor absoluto, mayor la relación lineal
entre x y y. Por definición, R = σσxxyσy , donde σxy es la covarianza de x y y, y σx y σy
son sus desviaciones estándar. Sobre el SE, éste es el error estándar de la pendiente,
por si queremos dar un margen de error, m ± M E = m ± t∗ SE. Para la t∗ , ésta se
calcula como en las secciones previas, dado un intervalo de confianza, sólo que ahora los
grados de libertad no son igual al número de datos menos 1, sino a n − 2, pues dando
la pendiente y la ordenada al origen perdemos 2 grados de libertad. Por último, sobre
el p_value, éste procede de tener como hipótesis nula que la pendiente es cero, por lo
que un valor pequeño de p_value nos asegura tener una pendiente diferente de cero.
Sigamos con el ejemplo de Vitrubio para ver cómo funciona linregress:
1 In [1]:
2 import numpy as np
3 import matplotlib . pyplot as plt
4 puntos = np . array ([(1.67 ,1.77) ,(1.67 ,1.76) ,(1.60 ,1.70) ,(1.63 ,1.72) ,\
5 (1.55 ,1.54) ,(1.60 ,1.61) ,(1.59 ,1.66) ,(1.785 ,1.815) ,(1.62 ,1.745) ,\
6 (1.61 ,1.63) ,(1.54 ,1.585) ,(1.72 ,1.785) ,(1.71 ,1.785) ,(1.49 ,1.575) ,\
7 (1.67 ,1.70) ,(1.78 ,1.80) ,(1.52 ,1.575) ,(1.73 ,1.885) ,(1.62 ,1.68) ,\
8 (1.68 ,1.765) ,(1.66 ,1.725) ,(1.45 ,1.485) ,(1.71 ,1.79) ,(1.73 ,1.84) ,\
9 (1.65 ,1.68) ,(1.58 ,1.685) ,(1.63 ,1.635) ,(1.73 ,1.73) ,(1.58 ,1.56) ,\
10 (1.55 ,1.595) ,(1.625 ,1.67) ,(1.715 ,1.83) ,(1.49 ,1.55) ])
11 x = puntos [: ,0] # la primera columna de puntos son las xs
12 y = puntos [: ,1] # la segunda columna son las ys
1 In [2]:
2 from scipy import stats
3 m , b , R , p_value , SE = stats . linregress (x , y )
4 print ( " m : " , m , " b : " , b , " R : " , R , " p_value : " , p_value , " SE : " , SE )
5 Out [2]:
6 m : 1. 087 22 09 258 00 86 56 b : -0.08257271475089834 R : 0 .9 126 16 08 340 10 65 3
p_value : 1.40373 18682809 71 e -13 SE : 0 .0 8 7 47 3 90 0 50 0 4 18 4 2
O sea que m = 1.09 ± 0.18, o m está entre 0.91 y 1.27 al 95 % de nivel de confianza,
por lo que no se descarta el ideal de Vitrubio, a ese nivel de confianza.
6.5.3. [Link]
Veamos ahora el desempeño de la función OLS del módulo statsmodel. Esta función se
ocupa cuando hay más de una variable independiente (como en el caso de las funciones
de sklearn). Si ingresamos tal cual los datos en la función, el resultado va a carecer de
la ordenada al origen:
1 In [4]:
2 import statsmodels . api as sm
3 model = sm . OLS (y , x ) . fit () # la y va antes que la x !
4 print ( model . summary () )
5 OLS Regression Results
6 ====================================================================
7 Dep . Variable : y R - squared :
0.999
8 Model : OLS Adj . R - squared :
0.999
9 Method : Least Squares F - statistic :
5.585 e +04
10 Date : Tue , 11 Sep 2018 Prob (F - statistic ) :
1.87 e -53
11 Time : 18:06:11 Log - Likelihood :
58.927
12 No . Observations : 33 AIC :
-115.9
13 Df Residuals : 32 BIC :
-114.4
14 Df Model : 1
15 Covariance Type : nonrobust
16 ====================================================================
17 coef std err t P >| t | [0.025
0.975]
18 --------------------------------------------------------------------
122 CAPÍTULO 6. SCIPY Y APLICACIONES EN MATEMÁTICAS
Antes de corregir por la falta de ordenada al origen, podemos ver en la parte central
del reporte todo lo referente a la pendiente, identificada como x1: su valor m, SE, t∗ al
95 % de confianza, p_value y extremos del intervalo al 95 % de confianza.
En la parte de arriba vemos a R2 y Radj2
. Ya dijimos lo que es el coeficiente de correlación
R. También se usa R , que tiene el significado de ser el cociente entre la variabilidad
2
de los datos explicada por el modelo, y la variabilidad de los datos total. Cuando hay
más de una variable independiente, R2 aumenta, el modelo está mejor explicado con
más variables. Pero puede ser que en realidad unas variables no aporten mucho, podría
haber redundancia. Así, la Radj2
penaliza la introducción de más variables explicatorias.
Si aumenta, es que estuvo bien meter una variable más, si no, es mejor quedarse hasta
la variable anterior. En el reporte, al haber una sola variable, R2 = Radj 2
, pero al ajustar
para permitir la ordenada al origen Radj va a cambiar, en este caso, va a disminuir.
2
Como vemos, ya aparece la información para los dos parámetros, la pendiente (x1) y la
ordenada al origen (const). La información es consistente con nuestros cálculos previos.
7 Pandas
Uno de los temas que más auge ha tenido en los últimos años en el área de ciencias es
la Ciencia de Datos. En este capítulo vamos ver una introducción al campo usando el
módulo de pandas para leer archivos csv y darle sentido a los datos. Los archivos csv,
separados por comas, son un tipo de archivo plano utilizado ampliamente para guardar
información en tablas. Es similar al Excel, con la diferencia de que los archivos cvs no
constan de varias pestañas, y de ahí el término plano. Pandas puede leer directamente
archivos Excel, pero vamos a circunscribirnos a archivos csv. Los objetivos de apren-
dizaje de esta unidad, son que el lector aprenda a leer archivos csv, explore sus datos,
los grafique, extraiga información de ellos, como información estadística, y los opere
y manipule para los fines pertinentes. Para ahondar en la información de este tema,
en la bibliografía se recomienda el manual en Ciencias de Datos en Python de Jake
VanderPlas (2016).
125
126 CAPÍTULO 7. PANDAS
Tabla 7.1: Exceso de fallecimientos por el coronavirus, entre julio de 2020 y abril de
2022
El método head() muestra los primeros cinco renglones, pero puede ponerse el número
deseado como argumento. Vemos que la primera columna es un rango de números
que comienza desde el 0, y que esa columna no existe en el archivo csv original. Esa
columna, el index, no forma parte de los datos y nos va a servir para ubicarlos, así
como los nombres de las columnas, columns, que en nuestro ejemplo son “día” y los
nombres de los países. Podríamos dejar que los renglones sean ubicados por un número,
pero sería más útil si los datos fueran ubicados por la fecha. Para que la columna de
“día” pase a ser el index, volvemos a leer el archivo y usamos la opción index_col para
asignarla (también se puede usar la función pd.set_index()):
1 In [2]:
2 exceso = pd . read_csv ( " exc eso_mort alidad . csv " , index_col = " d í a " )
3 print ( exceso . head () )
4 Out [2]:
5 M é xico Francia Italia Espa ñ a
6 día
7 05/07/2020 96304 14838 44400 42178
8 04/10/2020 211167 18033 50096 52683
9 03/01/2021 324302 47363 102037 70087
10 04/04/2021 485482 55893 121843 76615
11 04/07/2021 501729 63230 140258 77972
7.1. LECTURA Y EXPLORACIÓN DE DATOS 127
Si hubiera otro tipo de información en los primeros renglones, se pueden saltar con la
opción skiprows en el argumento. Por ejemplo, si hubiera 2 renglones antes del nombre
de las columnas, se pondría skiprows=2. Si en cambio, no hubiera información de las
columnas, lo ideal sería agregar nombres en el archivo Excel o csv directamente, pero
por si alguna razón no se puede o debe modificar el archivo, entonces se usa la opción
header=None, y los nombres de las columnas será su numeración empezando desde el
cero.
Pidamos ahora información del tipo de datos y su número:
1 In [3]:
2 print ( exceso . info () )
3 Out [3]:
4 < class ’ pandas . core . frame . DataFrame ’ >
5 Index : 8 entries , 05/07/2020 to 03/04/2022
6 Data columns ( total 4 columns ) :
7 # Column Non - Null Count Dtype
8 --- ------ - - - - - - - - - - - - - - -----
9 0 M é xico 8 non - null int64
10 1 Francia 8 non - null int64
11 2 Italia 8 non - null int64
12 3 Espa ñ a 8 non - null int64
13 dtypes : int64 (4)
14 memory usage : 320.0+ bytes
Ahora procedamos a graficar. Si se grafican todas las columnas a la vez, se hace un solo
llamado a plot, donde el eje x es el index. Se pueden dar argumentos para graficar
(Figura 7.1).
1 In [5]:
2 exceso . plot ( figsize =(9 ,5) , lw =10) # lw , ancho de la l í nea
3 Out [5]:
4 < Axes : xlabel = ’d í a ’ >
Esto difiere de como funciona numpy, donde los renglones tienen preferencia sobre las
columnas. Vemos que, además de los valores, aparece el index (día).
Si queremos sólo los valores, por alguna razón, los seleccionamos con el método values:
1 In [7]:
2 print ( exceso [ " M é xico " ]. values )
3 Out [7]:
4 [ 96304 211167 324302 485482 501729 620989 647104 697921]
Una sola columna como la que seleccionamos, deja de ser un DataFrame y es sólo una
Series, mientras que cuando seleccionamos los valores de la columna, convertimos la
serie en un arreglo de numpy:
7.1. LECTURA Y EXPLORACIÓN DE DATOS 129
1 In [8]:
2 print ( type ( exceso ) )
3 print ( type ( exceso [ " M é xico " ]) )
4 print ( type ( exceso [ " M é xico " ]. values ) )
5 Out [8]:
6 < class ’ pandas . core . frame . DataFrame ’ >
7 < class ’ pandas . core . series . Series ’ >
8 < class ’ numpy . ndarray ’ >
Si queremos una selección de columnas, en este caso, de países, se llaman con una lista
como argumento del dataframe:
1 In [9]:
2 print ( exceso [[ " M é xico " ," Espa ñ a " ]])
3 Out [9]:
4 M é xico Espa ñ a
5 día
6 05/07/2020 96304 42178
7 04/10/2020 211167 52683
8 03/01/2021 324302 70087
9 04/04/2021 485482 76615
10 04/07/2021 501729 77972
11 03/10/2021 620989 88153
12 02/01/2022 647104 97313
13 03/04/2022 697921 98107
O hacemos:
1 In [11]:
2 print ( exceso . iloc [4])
3 Out [11]:
4 M é xico 501729
5 Francia 63230
6 Italia 140258
7 Espa ñ a 77972
8 Name : 04/07/2021 , dtype : int64
Si queremos varios renglones, lo hacemos con slice. Para el loc el slice es inclusivo,
incluye el último elemento de la selección, y para iloc es exclusivo, no lo incluye.
130 CAPÍTULO 7. PANDAS
1 In [12]:
2 print ( exceso . loc [ " 04/10/2020 " : " 03/10/2021 " ])
3 Out [12]:
4 M é xico Francia Italia Espa ñ a
5 día
6 04/10/2020 211167 18033 50096 52683
7 03/01/2021 324302 47363 102037 70087
8 04/04/2021 485482 55893 121843 76615
9 04/07/2021 501729 63230 140258 77972
10 03/10/2021 620989 67264 152767 88153
Finalmente, si se seleccionan columnas pero con slice, hay que usar también el método
loc:
1 In [14]:
2 print ( exceso . loc [: , " Francia " : " Espa ñ a " ])
3 Out [14]:
4 Francia Italia Espa ñ a
5 día
6 05/07/2020 14838 44400 42178
7 04/10/2020 18033 50096 52683
8 03/01/2021 47363 102037 70087
9 04/04/2021 55893 121843 76615
10 04/07/2021 63230 140258 77972
11 03/10/2021 67264 152767 88153
12 02/01/2022 80073 168226 97313
13 03/04/2022 84833 181854 98107
Ejercicios:
a) Para la Tabla 7.2 sobre el total de pruebas de covid por cada mil habitantes en
algunos países, grafica los datos en función del día:
b) Selecciona los datos de los días del 4 de abril de 2020 al 2 de enero de 2022 desde
Alemania a España.
Tabla 7.2: Total acumulado de pruebas por covid por cada mil habitantes
Como hay columnas numéricas, podemos darnos una idea de la estadística de éstas con
el método describe:
1 In [2]:
2 print ( estud . describe () )
3 Out [2]:
4 prog 1 prog 2
5 count 20.000000 20.000000
6 mean 2.750000 2.800000
7 std 1.517442 1.472556
8 min 0.000000 0.000000
9 25 % 2.000000 2.000000
10 50 % 3.000000 3.000000
11 75 % 4.000000 4.000000
12 max 5.000000 5.000000
132 CAPÍTULO 7. PANDAS
Podríamos pedir por separado alguna variable de agregación como el promedio. Como
ya se mencionó, las columnas tienen prioridad en pandas, así que por default el eje
sobre el que se hace el promedio es axis=0, o columnas:
1 In [3]:
2 print ( estud . mean () )
3 Out [3]:
4 prog 1 2.75
5 prog 2 2.80
6 dtype : float64
Es sencillo operar numéricamente con las columnas, pero para operar con aquellas cuyos
elementos sean cadenas primero hay que llamar a la función str, y utilizar sus métodos
(por decir, [Link]()). En nuestro ejemplo, además de la información del plantel y
carrera a la que pertenecen los estudiantes se proporciona la matrícula. Resulta ser que
los 2 primeros números de la matrícula representan la generación del estudiante. Así
que podemos extraer la generación con slicing, tomando los primeros 2 números:
1 In [7]:
2 estud [ " gen " ]= estud [ " matr í cula " ]. str [:2]
3 print ( estud . head () )
4 Out [7]:
5 matr í cula plantel carrera prog 1 prog 2 suma gen
6 0 15 -095 DV FeHI 3 5 8 15
7 1 13 -005 SLT NyS 2 4 6 13
8 2 16 -206 SLT Software 4 3 7 16
9 3 16 -115 CL CPyAU 5 4 9 16
10 4 14 -204 Cua ISEI 0 2 2 14
¿Cómo saber los valores distintos que aparecen en cada columna?, con el método
unique, que da el conjunto de los valores únicos de la columna dada:
1 In [8]:
2 print ( estud [ " carrera " ]. unique () )
3 Out [8]:
4 [ ’ FeHI ’ ’ NyS ’ ’ Software ’ ’ CPyAU ’ ’ ISEI ’ ’ CL ’ ’ PS ’ ’ ISET ’]
Si queremos filtrar renglones para un valor específico de una variable, se hace como en
numpy. Por ejemplo, si filtramos a los estudiantes que pertenecen al plantel “CL”:
1 In [9]:
2 print ( estud [ estud [ " plantel " ]== " CL " ])
3 Out [9]:
4 matr í cula plantel carrera prog 1 prog 2 promedio gen
5 3 16 -115 CL CPyAU 5 4 4.5 16
6 9 14 -098 CL CPyAU 1 4 2.5 14
7 11 14 -283 CL ISET 4 3 3.5 14
8 14 15 -126 CL ISEI 2 3 2.5 15
9 15 17 -111 CL ISET 5 3 4.0 17
10 18 16 -142 CL CPyAU 0 0 0.0 16
Además de count se puede usar otra función de agregación, como sum, por ejemplo, si
se quisiera sumar los puntajes de cada serie, agrupados por carrera y plantel.
Ejercicios:
Considera la Tabla 7.4 sobre películas de animación por computadora, rankeada por su
calificación en rottentomatoes, y donde en la columna de “protagonista”, se describe al
personaje o personajes principales:
a) Selecciona las películas de Disney, y obtén el porcentaje promedio de calificaciones.
Haz lo mismo con las películas de Pixar.
b) Agrupa los datos por el tipo de protagonista, y cuenta el número de películas que
pertenece a cada categoría de protagonista.
Como siempre, queremos poner como índice la columna de la fecha. Podemos hacer esto
con index_col, como en la sección pasada, pero vamos a usar la función set_index
como alternativa.
En la sección pasada tuvimos pocos datos y no fue muy necesario manejar de forma
correcta la columna de fechas, pero lo mejor es usar el método to_datetime de pandas
sobre todo para mostrar figuras sin los números encimados. Procedemos entonces a
cambiar el formato de la columna de las fechas a datetime, y la convertimos en nuestro
index. Restringimos también los datos seleccionando las fechas desde el 1 de abril de
2020 hasta el 1 de abril de 2022. Como el formato datetime cambia el orden de los
días, meses y años (día/mes/año), al seleccionar las fechas hay que ser cuidadosos de
seguir el nuevo formato (año-mes-día).
1 In [2]:
2 todos [ " date " ]= pd . to_datetime ( todos [ " date " ])
3 todos = todos . set_index ( " date " )
4 todos = todos . loc [ " 2020 -04 -01 " : " 2022 -04 -01 " ]
5 print ( todos . head () )
Enseguida, vamos a seleccionar sólo algunas de las columnas a graficar, la de pruebas por
cada mil habitantes (“total_tests_per_thousand”), la de exceso de mortalidad acu-
mulada por millón de habitantes (“excess_mortality_cumulative_per_million”) y
la de total de dosis de vacunas por cada cien habitantes
(“total_vaccinations_per_hundred”). Escribimos en una lista los nombres de las
columnas y en otra los títulos correspondientes para las gráficas. Para graficar, usamos
un bucle for para seleccionar la columna y el título, y un for interno para seleccionar
cada país (en lugar de usar una lista explícita con los países, abreviamos con la lista
generada con el método unique de la columna de países). A diferencia de lo que vimos
en la sección 6.1, vamos a agregar uno a uno a cada país en la gráfica con pyplot.
También, en lugar de plot graficamos puntos con scatter para enfatizar los saltos de
información (Figura 7.2, Figura 7.3 y Figura 7.4).
138 CAPÍTULO 7. PANDAS
1 In [3]:
2 columnas =[ " t o t a l _ t e s t s _ p e r _ t h o u s a n d " ,\
3 " e x c e s s _ m o r t a l i t y _ c u m u l a t i v e _ p e r _ m i l l i o n " ,\
4 " total_vaccinations_per_hundred "]
5 t í tulos =[ " total de pruebas por mil habitantes " ," exceso de mortalidad \
acumulado por mill ó n de habitantes " ," total de dosis de vacunas por \
cada cien habitantes " ]
6 for j , k in zip ( columnas , t í tulos ) :
7 plt . figure ( figsize =(10 ,6) )
8 for i in todos [ " location " ]. unique () :
9 a = todos [ todos [ " location " ]== i ] # seleccionamos el pa í s
10 plt . scatter ( a . index , a [ j ] , label =i , marker = " D " ,s =30)
11 # eje x los d í as , eje y la columna seleccionada
12 plt . legend ( fontsize =20)
13 plt . title (k , fontsize =20)
14 plt . show ()
7.2. AGREGACIÓN, FILTRADO, AGRUPAMIENTO 139
Figura 7.4: Total de vacunas por cada 100 habitantes (no es el total de vacunados, pues
se puede repetir la dosis).
Ejercicios:
Usa el archivo del coronavirus para hacer tus propias indagaciones con los países y
columnas deseadas.
8 Machine Learning
141
142 CAPÍTULO 8. MACHINE LEARNING
que vamos a trabajar, escogiendo datos que están guardados en la misma biblioteca de
sklearn.
Las medidas de los sépalos y pétalos están guardadas en data, que es un arreglo de
numpy donde por cada renglón hay mediciones de una flor, y las columnas denotan las
variables a medir: el largo y ancho del sépalo (columnas 0 y 1) y el largo y ancho del
pétalo (columnas 2 y 3). Esa es la forma como deben guardarse los datos, ya sea que se
tengan arreglos de numpy, DataFrames de pandas o hasta listas de listas, las columnas
deben denotar a las variables medidas y los renglones a cada nuevo experimento. En
target, un arreglo de numpy tipo vector, está guardada la información, por renglón,
de la etiqueta o tipo de flor usando los valores 0, 1 y 2. Esto es, por cada renglón
de data, le corresponde el tipo de flor en target, 0 por Setosa, 1 por Versicolor y 2
por Virginica. En target_names están tal cual los nombres del tipo de flor, y por eso
sabemos el orden, y en feature_names las variables a medir (largo y ancho del sépalo
y el pétalo).
Extraemos la información y exploramos su contenido:
1 In [2]:
2 X = iris . data ; y = iris . target
3 nombres = iris . target_names ; var = iris . feature_names
4 print ( nombres )
5 print ( var )
6 print ( y )
7 print ( X . shape , y . shape ) # tama ñ o de los datos
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 143
8 Out [2]:
9 [ ’ setosa ’ ’ versicolor ’ ’ virginica ’]
10 [ ’ sepal length ( cm ) ’ , ’ sepal width ( cm ) ’ , ’ petal length ( cm ) ’ , ’ petal
width ( cm ) ’]
11 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
12 (150 , 4) (150 ,)
Son 150 datos, y vemos que las flores no están puestas al azar, están ordenadas primero
las setosa, luego las versicolor y al final las virginica.
La notación común es seleccionar X para los datos de las variables independientes, y y
para la variable categórica. Las variables independientes son así x_0=X[:,0], la primera
columna, x_1=X[:,1], la segunda columna, etc., (hay que recordar que la numeración
en Python empieza con cero). Si trabajáramos desde pandas, x_0=X["nombre_0"], etc.
Antes de hacer cualquier cosa con Aprendizaje Automático hay que terminar de explorar
bien los datos. Grafiquemos entonces, de una sola vez, todas las combinaciones de pares
de variables, para ver sus relaciones. Existe la función scatter_matrix en pandas
para hacerlo, donde los elementos en la diagonal son los histogramas de las variables
independientes. Se aplicaría: scatter_matrix( nombre del Dataframe [nombres de las
columnas en una lista, y como opcional el tamaño de la figura con figsize] ). Como
vamos a seguir en numpy, hagámoslo a mano (Figura 8.1):
1 In [3]:
2 import matplotlib . pyplot as plt
3 k =0 # k es un contador para llevar la cuenta de la subfigura
4 plt . figure ()
5 for j in range (4) : # por ser 4 variables
6 for i in range (4) :
7 k +=1
8 plt . subplot (4 ,4 , k ) # 4 renglones , 4 columnas , figura k
9 # la columna X [: i ] para una variable , X [: , j ] para la otra
10 plt . scatter ( X [: , i ] , X [: , j ] , c =y , cmap = " rainbow " )
11 # c =y , color de puntos de acuerdo a la etiqueta 0 , 1 o 2
12 if j ==3: # cuando en el eje y est á la ú ltima variable
13 plt . xlabel ( var [ i ]) # escribe el nombre de la variable
14 if i ==0: # cuando en el eje x est á la primera variable
15 plt . ylabel ( var [ j ] , fontsize =7)
16 plt . tight_layout ()
17 plt . show ()
Para que aparecieran nada más los nombres de las variables en los ejes cartesianos,
a la izquierda y en la parte de abajo de la figura, usamos unos if internos para que
aparecieran sólo en algunas subfiguras.
Si aumenta el número de variables, graficar en una forma tipo matriz va a hacer difícil
ver con detalle las figuras. Además, hay redundancia al graficar xi vs xj y luego xj vs
xi , además de graficar las variables contra sí mismas, xi vs xi . Grafiquemos entonces
eligiendo el número de columnas deseadas, y entonces dejando sólo el número de renglo-
nes necesarios. Si n es el número de variables y cada subfigura requiere de dos variables,
144 CAPÍTULO 8. MACHINE LEARNING
Figura 8.1: Gráficas en 2D de las combinaciones de las cuatro variables de los datos de
las flores iris. Hay redundancia de información al invertir el orden de las variables, xi
vs xj y xj vs xi , y al graficar cada variable contra sí misma, xi vs xi .
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 145
n! n(n − 1)
Cn2 = =
2!(n − 2)! 2
En cada una de las subfiguras se ve que los datos se separan decentemente, según el
tipo de flor, pero de primera vista la mejor elección para representar los datos en 2
dimensiones sería tomar el largo y ancho de los pétalos (las 2 últimas columnas de
datos o X). Esto lo vamos a hacer para que, por un lado, visualmente sean más claros
los ejercicios de clasificación que veremos en las siguientes secciones, y por el otro, para
trabajar con menos dimensiones.
Figura 8.2: Dos renglones y tres columnas para graficar en 2D las seis combinaciones
únicas de las cuatro variables.
Figura 8.3: Selección de las dos variables de las últimas columnas para graficar los datos
de las flores iris, el largo y ancho del pétalo.
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 147
Ejercicios:
1. En un archivo de Python independiente, para no mezclar con las flores iris, con el que
seguiremos trabajando en las secciones subsecuentes, carga los datos sobre vinos, que
se encuentra igualmente en el módulo datasets y se carga con la función load_wine
(datasets.load_wine)
2. Explora el contenido de vinos con la función dir
3. ¿Cuántas son las variables que se midieron? Imprime el nombre de las variables
4. ¿Cuántas categorías de vinos hay? Imprime el nombre de las categorías
5. Guarda la información de los variables independientes en la matriz X y las categorías
en y.
6. ¿Cuántos datos o mediciones hay?, encuentra el número.
7. Explora graficando pares de variables
8. Si clasificáramos usando sólo 2 variables, ¿cuál del siguiente par de variables arroja-
rían mejores resultados? (tiene menos superposición de puntos):
a) proanthocyanis vs hue
b) non_flavanoid_phenols vs hue
c) total_phenols vs color_intensity
Antes de ver cada método de clasificación en particular, vamos a ver cómo se aplican
los métodos en general en sklearn.
El procedimiento comienza llamando al método que se va a utilizar con sus argumentos
respectivos. En seguida se aplica la función fit con argumentos la X y y de entre-
namiento, tal cual para entrenar los datos. Posteriormente se prueba con la función
predict con argumento la X de prueba, cuyos resultados se comparan con la y de
prueba. Los pasos se verían así:
148 CAPÍTULO 8. MACHINE LEARNING
Esta función la vamos a aplicar para cada uno de los métodos de las secciones siguientes.
Para no sólo tener el error cometido, sino visualizar los datos, vamos a definir una
función para graficar, no sólo los datos, sino para colorear la zona de graficación según
lo que predice el método utilizado. Uno de los argumentos de la función para graficar
es el modelo retornado por la función predice de arriba, para predecir el color de las
zonas. Vamos a construir una malla y colorear cada punto de ésta:
1 In [8]:
2 colores =[ " lime " ," r " ," blue " ]
3 colores1 =[ " olive " ," pink " ," dodgerblue " ]
4 def zona ( modelo ,i ,j , X_train , y_train , X_test , y_test , t í tulo , estilo ) :
5 plt . figure ()
6 # se prepara el á rea para colorear , con los valores m í nimo
7 # y m á ximo de las variables
8 xmin = min ( np . min ( X_train [: , i ]) , np . min ( X_test [: , i ]) )
9 ymin = min ( np . min ( X_train [: , j ]) , np . min ( X_test [: , j ]) )
10 xmax = max ( np . max ( X_train [: , i ]) , np . max ( X_test [: , i ]) )
11 ymax = max ( np . max ( X_train [: , j ]) , np . max ( X_test [: , j ]) )
12 # se encuentra el rango para redefinir los m í nimos y m á ximos
13 # y definir una buena zona de graficaci ó n
14 xrange = xmax - xmin ; yrange = ymax - ymin
15 xmin = xmin - xrange /10; xmax = xmax + xrange /10
16 ymin = ymin - yrange /10; ymax = ymax + yrange /10
17 # la malla o grid donde se va a colorear cada punto :
18 xx , yy = np . meshgrid ( np . linspace ( xmin , xmax ,300) ,\
19 np . linspace ( ymin , ymax ,300) )
20 grid = np . c_ [ xx . ravel () , yy . ravel () ]
21 # se predice en los puntos de la malla :
22 Z = np . array ([ modelo . predict ( a . reshape (1 ,2) ) \
23 for a in grid ]) . reshape ( xx . shape )
24 # se colorean los puntos de la malla , seg ú n la predicci ó n Z :
25 plt . pcolormesh ( xx , yy ,Z , cmap = estilo )
26 # se colorean los puntos de entrenamiento y de prueba con
27 # diferente color :
28 for k in range (3) :
29 plt . scatter ( X_train [ y_train == k , i ] ,\
30 X_train [ y_train == k , j ] , color = colores [ k ] ,\
31 label = nombres [ k ]+ " , train " ,s =50)
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 149
En la función predice, llamamos a esa función con k = 1. Hay más parámetros que
se pueden cambiar, pero no vamos a utilizar. Por ejemplo, si quisiéramos que los pun-
tos más cercanos tuvieran mayor peso, se podría cambiar el parámetro weights, de
“uniform”, que es el valor por default, a “distance”, donde la influencia disminuye
con el inverso de la distancia. Escogiendo el largo y el ancho del pétalo como variables,
con índices 2 y 3, respectivamente:
1 In [10]:
2 modelo , error =\
3 predice ( K N e i g hb o r s C l a s s i f i e r (1) ,2 ,3 , X_train , y_train , X_test , y_test )
4 print ( error )
5 zona ( modelo ,2 ,3 , X_train , y_train , X_test , y_test ,\
150 CAPÍTULO 8. MACHINE LEARNING
Figura 8.4: Clasificación de los datos de las flores iris con vecinos más cercanos. Con
sólo un vecino, el modelo queda sobreajustado.
A comparar con:
1 In [11]:
2 modelo , error =\
3 predice ( K N e i g hb o r s C l a s s i f i e r (9) ,2 ,3 , X_train , y_train , X_test , y_test )
4 print ( error )
5 zona ( modelo , 2 , 3 , X_train , y_train , X_test , y_test ,\
6 " clasificaci ó n con k - Neighbors , k =9 " ," autumn " )
7 Out [11]:
8 0 .0 5 2 63 1 57 8 94 7 3 68 4 2
Comparando las figuras (8.4) y (8.5), vemos que, con más vecinos, los bordes son más
suaves. Aunque para cada conjunto de datos se trataría de probar diferentes valores de
k, una buena elección sería entre k = 3 y k = 5, pues k = 1 sobre ajusta los datos y
valores grandes de k dan resultados más burdos.
Ejercicios:
1. Considera los puntos en el plano cartesiano, “azules”, (0, 3), (2, 2) y (2, 4), y los puntos
“rojos”, (5, 3), (5, 1) y (6, 4). Si hay que clasificar al punto (3, 2), con k = 1 vecinos más
cercanos, ¿en qué categoría de color quedaría el punto?, ¿y si se utilizara vecinos más
cercanos con k = 3?
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 151
Figura 8.5: Clasificación con vecinos más cercanos. Con un número mayor de vecinos,
el modelo se relaja.
2. Volvemos a usar los datos de los vinos, que se quedó como ejercicio de la primera
sección. Separa los datos en datos de entrenamiento y de prueba usando la función
train_test_split con los valores por default, más el random_state de tu gusto. Se-
lecciona las variables “alcohol” y “flavanoids”, con índices 0 y 6, respectivamente,
para entrenar y probar los datos, y para graficar usando Vecinos más cercanos con k = 1
y k = 11. ¿Cuál de las 2 elecciones reduce el error y hace ver mejor las fronteras?
1 In [12]:
2 from sklearn . tree import D e c i s i o n T r e e C l a s s i f i e r
3 modelo , error = predice ( D e c i s i o n T r e e C l a s s i f i e r () ,2 ,3 , X_train , y_train ,
X_test , y_test )
4 print ( error )
5 zona ( modelo ,2 ,3 , X_train , y_train , X_test , y_test ,\
6 " clasificaci ó n con á rbol de decisi ó n " ," prism " )
7 Out [12]:
8 0 .0 5 2 63 1 57 8 94 7 3 68 4 2
Lo que hace el programa es hacer pasar líneas entre los puntos, horizontales para la
variable del ancho del pétalo, y verticales para el largo del pétalo. En cada paso, se-
lecciona la división que mejor separe a los puntos por categoría, usando criterios que
explicaremos más adelante. Rompe empates de manera aleatoria, por lo que no siempre
se obtienen los mismos resultados. Por ejemplo, en el resultado de la Figura 8.7, la línea
horizontal que divide los puntos de la setosa del resto pudo haber sido el primer paso.
La siguiente división pudo ser la línea horizontal que divide a los otros 2 tipos de iris.
Vemos que la Figura 8.7 se ve un poco rara, pues por ejemplo hay una barra vertical
verde en la zona de las versicolor. Esto se debe a que los puntos de la versicolor y de la
virginica tienen un poco de traslape, y el algoritmo sigue haciendo divisiones para dejar
a los puntos en la categoría correcta. A esto se le llama sobreajuste (overfitting). Antes
de tratar de remediar la situación, corramos otras veces el programa para obtener otra
posible configuración. El resultado se muestra en la Figura 8.8:
Para evitar el sobreajuste, se pueden buscar las opciones de la función
DecisionTreeClassifier con help(DecisionTreeClassifier). La opción max_depth
nos puede ayudar para reducir la profundidad del árbol, hacerlo más corto. En la si-
guiente corrida, se selecciona max_depth=3 (Figura 8.9):
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 153
Figura 8.7: Clasificación de los datos de las flores irirs con el método de árbol de decisión.
Figura 8.8: Otra configuración para la clasificación de los datos con árbol de decisión,
con el mismo valor del error en los datos de prueba.
154 CAPÍTULO 8. MACHINE LEARNING
Figura 8.9: Clasificación de los datos con árbol de decisión, con una altura del árbol
igual a 3 (o 3 bifurcaciones).
1 In [13]:
2 modelo , error = predice ( D e c i s i o n T r e e C l a s s i f i e r ( max_depth =3) ,2 ,3 ,\
3 X_train , y_train , X_test , y_test )
4 print ( error )
5 zona ( modelo ,2 ,3 , X_train , y_train , X_test , y_test ,\
6 " clasificaci ó n con á rbol de decisi ó n " ," prism " )
7 Out [13]:
8 0 .0 5 2 63 1 57 8 94 7 3 68 4 2
¿Cuál es el criterio para la mejor partición en cada paso? En sklearn hay tres medi-
das, la impureza de Gini, la ganancia de información (entropía) y la pérdida logística
(log_loss). Sólo mencionaremos la Impureza de Gini, que es el criterio por default que
usa DecisionTreeClassifier.
En la impureza de Gini, se mide la proporción de datos de cierta categoría que quedan
en una rama del árbol cuando se divide. Está dado por la expresión:
m
X
IG = 1 − fi2
i=1
Figura 8.10: Árbol de decisión asociado a la figura (8.9), mostrando el índice de Gini
en cada grupo. La bifurcación a la izquierda responde positivamente a la pregunta, a
la derecha la respuesta es negativa.
1 In [14]:
2 from sklearn . tree import plot_tree
3 plot_tree ( modelo , filled = True )
4 Out [14]:
5 [ Text (0.4 , 0.875 , ’x [1] <= 0.7\ ngini = 0.666\ nsamples = 112\ nvalue =
[38 , 36 , 38] ’) ,
6 Text (0.2 , 0.625 , ’ gini = 0.0\ nsamples = 38\ nvalue = [38 , 0 , 0] ’) ,
7 Text (0.6 , 0.625 , ’x [1] <= 1.75\ ngini = 0.5\ nsamples = 74\ nvalue = [0 ,
36 , 38] ’) ,
8 Text (0.4 , 0.375 , ’x [0] <= 5.45\ ngini = 0.142\ nsamples = 39\ nvalue =
[0 , 36 , 3] ’) ,
9 Text (0.2 , 0.125 , ’ gini = 0.1\ nsamples = 38\ nvalue = [0 , 36 , 2] ’) ,
10 Text (0.6 , 0.125 , ’ gini = 0.0\ nsamples = 1\ nvalue = [0 , 0 , 1] ’) ,
11 Text (0.8 , 0.375 , ’ gini = 0.0\ nsamples = 35\ nvalue = [0 , 0 , 35] ’) ]
12
Al principio, hay un solo grupo de 112 elementos (recordemos que estamos trabajando
con los datos de prueba, el 75 % de los 150 datos originales). Hay 38 datos de setosa y
el mismo número de virginica, y hay 36 datos de versicolor. Se ingresan estos datos en
la fórmula de la Impureza de Gini, quedando en este paso como:
2 2 2
38 36 38
IG = 1 − − − = 0.666
112 112 112
Se hace la primera división, horizontal, según si el ancho del pétalo es menor o mayor
a 0.7 cm. Las setosas quedan así separadas en el grupo del ancho del pétalo ≤ 0.7 cm,
156 CAPÍTULO 8. MACHINE LEARNING
Se vuelve a hacer una división horizontal, para dividir a las flores según si el ancho del
pétalo es menor a 1.75 cm (pero mayor a 0.7 cm), creando 3 franjas horizontales. De
esta forma quedan separadas las virginicas en la parte superior, el Índice de Gini ahí
es cero, pero en la franja de en medio todavía hay 3 virginicas mezcladas con las 36
versicolor. Se hace una tercera y última división, donde se parte verticalmente la franja
de en medio, según si el largo del pétalo es mayor o menor a 5.45 cm, dejando todavía
2 virginicas mezcladas con las versicolor. Como seleccionamos que la profundidad del
árbol fuera de 3, ahí para el algoritmo.
Ejercicios:
1. Continuamos con el ejercicio de los datos de los vinos, seleccionando las columas 0 y 6
de “alcohol” y “flavonoids”. Corre el árbol de decisión con las opciones por default,
y después selecciona max_depth=3 y max_depth=2. Corre algunas veces el programa
para cada selección. ¿Cuál es la mejor elección del parámetro max_depth? Explica.
2. Dibuja el árbol de decisión creado con max_depth=2 con plot_tree.
Para encontrar los valores óptimos en aprendizaje automático se utiliza el método nu-
mérico de gradient descent, o alguna variación de éste, que de forma sencilla significa
lo siguiente. Dada una función convexa a minimizar L(w), si escogemos aleatoriamente
un valor de w y si la derivada de la función evaluada en ese valor es negativa, esto es,
la recta tangente al punto está inclinada hacia la izquierda, entonces sabemos que para
reducir el valor de la función debemos movernos a la derecha para mejorar la predicción
de w. Si por el contrario, la derivada es positiva, sabemos que nos debemos mover a la
izquierda para mejorar la predicción de la w que minimiza la función (Figura 8.12)
Iterativamente entonces se realiza el procedimiento que mueve a la izquierda o a la
derecha la w:
dL
wi+1 = wi − η
dw
donde η es el tamaño del paso. El procedimiento termina cuando en un siguiente paso
L(w) aumenta, en lugar de disminuir. Como en general son varios los parámetros a
optimizar, la derivada se reemplaza por un gradiente. Algunos de los problemas de
158 CAPÍTULO 8. MACHINE LEARNING
ew·x+b
P (y = 1|x) =
1 + ew·x+b
Ejercicios:
1. Continuando con el ejercicio de los datos de los vinos, con las columnas 0 y 6 de
“alcohol” y “flavonoids”, corre el programa con el método de regresión logística.
Por lo que maximizar el margen 2γ, equivale a minimizar |w|, o mejor, |w|2 (siempre es
mejor maximinar o minimizar cuadrados que valores absolutos).
160 CAPÍTULO 8. MACHINE LEARNING
Figura 8.14: Cuando los puntos están divididos por áreas bien delimitadas, se marca la
línea w · x + b = 0, que es la frontera, y las líneas w · x + b = 1 y w · x + b = −1, que son
las que marcan el margen, se busca maximizar éste. Los puntos que quedan sobre las
líneas del margen se llaman vectores soporte. Los puntos con w · x + b ≥ 1 pertenecen
a una categoría, y con w · x + b ≤ −1 pertenecen a la otra.
La recta central no difiere en 1 unidad de las rectas de los márgenes, difiere en dos
unidades, así que se pueden reescalar las ecuaciones dividiendo por la mitad, para
obtener las siguientes ecuaciones, escritas en la forma w · x + b = 0 y w · x + b = ±1:
1 1 7
x1 + x2 − = 0,
2 2 2
1 1 5 1 1 7
x1 + x2 − = 0 → x1 + x2 − = −1
2 2 2 2 2 2
1 1 9 1 1 7
x1 + x2 − = 0 → x1 + x2 − = 1
2 2 2 2 2 2
Por lo que w1 = w2 = 1/2 y b = −7/2. Por supuesto, esto es parte de la explicación de
los pasos, pero sklearn hace todo por nosotros.
¿Qué pasa si los puntos están mezclados cerca de la frontera y no hay posibilidad de
dividir las zonas? En ese caso, hay que permitir un poco de laxitud, que haya puntos
que puedan estar dentro del margen. Incluso, puntos que estén del lado contrario a su
categoría. Lo que se hace entonces, es tratar de minimizar la función de costo:
n
X
2
L(w) = |w| + C ξi
i=1
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 161
donde cada ξi , con ξi ≥ 0, es la posible violación por ingresar al margen o pasarse del
otro lado de la categoría del punto x(i) (son n puntos). Los puntos x(i) estarían sujetos
entonces a la ecuación:
y (i) w · x(i) + b ≥ 1 − ξi
Hay que notar que, cuando se tiene w · x + b = 0, estamos sobre la frontera, y cuando
w · x + b = ±1, estamos sobre los márgenes. Seleccionando el margen w · x + b = 1, se
tendrían las siguientes situaciones de violación de los márgenes:
* ξ = 0, no hay violación del margen, los puntos quedan bien separados del lado del
grupo que les toca (w · x + b ≥ 1).
* 0 < ξ ≤ 1, hay violación de los márgenes, pero aún 0 ≤ w · x + b < 1, el punto está
entre la frontera y el margen correcto del grupo del punto.
* 1 < ξ ≤ 2, hay violación de los márgenes y −1 ≤ w · x + b < 0, el punto está entre la
frontera y el margen del otro lado del grupo que le corresponde al punto.
* ξ > 2, el punto ya dejó los márgenes y está del lado incorrecto del grupo que le toca.
Lo anterior se puede visualizar en la Figura 8.16, donde sólo se representan puntos
“rojos” que se van adentrando a la zona “azul”:
Para la mayoría de los puntos, ξ = 0, pues noPestán dentro del margen o del lado que
no les corresponde. Cuando C = 0, la suma ni=1 ξi puede tomar cualquier valor y se
permite mucha laxitud en los márgenes, las restricciones son suaves, y cuando C → ∞,
la suma i=1 ξi se tiene que hacer lo más pequeña posible, las restricciones son duras.
Pn
Para generalizar a más de dos categorías, al igual que con la Regresión Logística, se
pueden considerar m parámetros w (para tener varias rectas o hiperplanos), y entonces
la función de costo se vuelve:
m
X n
X
2
L(w) = |wi | + C ξi
i=1 i=1
162 CAPÍTULO 8. MACHINE LEARNING
Figura 8.16: Permisividad para que los puntos “rojos” ingresen a otro tipo de clasifica-
ción, medido por las violaciones a los márgenes ξi
Para que un punto x(i) se considere en la categoría k entonces debe de pasar que, para
cualquier j, con j ̸= k:
wk · x(i) + bk − wj · x(i) − bj ≥ 1 − ξi
Es claro de la Figura 8.17 que hay un sobreajuste, la curva no se ve muy natural. Los
puntos que están encerrados son los que sirvieron como vectores soporte. Las fronteras
son curvas porque el kernel por default es “rbf” (radial basis function), aunque se
podría elegir la opción kernel=”linear” para tener líneas rectas (para ver las opciones
de SVC, hay que mandar a imprimir help(SVC)). Vamos a intentar ahora dejando el
valor por default C = 1 para no constreñir demasiado los puntos:
8.1. MÉTODOS SUPERVISADOS: CLASIFICACIÓN 163
Figura 8.17: Clasificación con SVM, con parámetro C muy grande. El modelo queda
sobreajustado
1 In [17]:
2 modelo , error = predice ( SVC () ,2 ,3 , X_train , y_train , X_test , y_test )
3 print ( error )
4 zona ( modelo , 2 , 3 , X_train , y_train , X_test , y_test ,\
5 " clasificaci ó n con SVM " ," rainbow " )
6 Out [17]:
7 0 .0 5 2 63 1 57 8 94 7 3 68 4 2
Ejercicios:
1. Supón que en un problema de clasificación de datos en 2 dimensiones, x1 y x2 , las
rectas que definen la frontera y los márgenes según Support Vector Machines, son:
x2 = x1 /2 + 6, x2 = x1 /2 + 9, x2 = x1 /2 + 3
Figura 8.18: Clasificación con SVM, con parámetro C pequeño. El modelo no es tan
restrictivo.
P (x|k)P (k)
P (k|x) =
P (x)
Para categorizar un punto x, sólo se toma el valor más alto de P (x|k)P (k) para todas
las clases k, pues el denominador es el mismo para ese punto x.
Antes de proceder a un ejemplo, recordemos cuál es la función de densidad de la dis-
tribución gaussiana en una dimensión:
(x − µ)2
1
ρ(x) = √ exp −
2πσ 2σ 2
Figura 8.19: Clasificación de las flores iris con Gaussian Naive Bayes
Hagamos el ejemplo del conjunto de datos de las iris. Se llama a la función GaussianNB
(Figura 8.19):
1 In [18]:
2 from sklearn . naive_bayes import GaussianNB
3 modelo , error = predice ( GaussianNB () ,2 ,3 , X_train , y_train , X_test , y_test )
4 print ( error )
5 zona ( modelo , 2 , 3 , X_train , y_train , X_test , y_test ,\
6 " clasificaci ó n con naive Bayes " ," Blues " )
7 Out [18]:
8 0 .0 5 2 63 1 57 8 94 7 3 68 4 2
Ejercicios:
1. Para unos ciertos datos en una dimensión hay mediciones correspondientes a tres
categorías, “rojo”, “verde” y “azul”. Si hay 60 datos “rojos”, 40 “verdes” y 50 “azules”,
¿cuál es la frecuencia de cada categoría?
2. El promedio de los datos “rojos” del problema 1 es de µ = 5 y su desviación estándar
es σ = 1.6. Los datos para la categoría “verde” son µ = 3 y σ = 1.4, mientras que
para la categoría “azul” son µ = 6 y σ = 2.5, ¿Cuál de los valores de las funciones de
densidad, si ajustamos distribuciones normales por cada categoría, es el más alto para
el punto x = 4?
3. Si consideramos la probabilidad conjunta de los problemas 1 y 2 de arriba, esto es,
P (x = 4|color)P (color), ¿cuál es la más alta y, por tanto, cuál categoría sería la más
probable para el punto x = 4?
166 CAPÍTULO 8. MACHINE LEARNING
4. Continuando con los datos de los vinos de los ejercicios anteriores, corre el pro-
grama seleccionando el método de Naive Bayes y las columnas 0 y 6 correspondientes
a las variables “alcohol” y “flavonoids”.
Ejercicios
Considera los datos de los vinos de los ejercicios anteriores. Como es de imaginarse, el
método de vecinos más cercanos, que involucra la distancia euclideana entre puntos,
y, por tanto, la combinación de dos o más dimensiones, es sensible a la escala de los
datos. Selecciona dos variables donde no haya mucha intersección entre los datos y en
que las escalas sean muy diferentes. Usa el método de k vecinos más cercanos con los
datos como están, graaficando la zona, y compara con el mismo método pero donde
pre-procesas los datos con el escalador StandardScaler.
8.2. APRENDIZAJE NO SUPERVISADO: PCA 167
Figura 8.20: Proyección de los puntos en 2D sobre una recta en la dirección de máxima
varianza
1
Σ= BB T
n−1
donde
B = (x1 − µ x2 − µ ... xn − µ)
B es una matriz de d × n, por las d dimensiones de cada punto y al haber n puntos.
Por tanto, Σ es una matriz de d × d simétrica.
Hacemos un paréntesis para hablar de unas de las propiedades de las matrices simétricas.
Primero, los eigenvectores de una matriz simétrica (que se define por ser igual a su
transpuesta, A = AT ) son ortogonales.
168 CAPÍTULO 8. MACHINE LEARNING
covarianza en dirección w = wT Σw
U = (u1 u2 ··· uk )
Lo que queremos es proyectar cada uno de los vectores x en las nuevas direcciones ui , y
esto se logra con el producto punto de los vectores, ui · x, o en notación matricial, uTi x.
La matriz U ya conjunta los k eigenvectores principales, por lo que las proyecciones en
las nuevas direcciones quedan como:
uT1 uTi x
xP CA = U T x = ... x = ...
uTk uTk x
8.2. APRENDIZAJE NO SUPERVISADO: PCA 169
Figura 8.21: Gráfica de los datos iris en 2D con las 2 componentes principales
Podemos saber en qué medida están contribuyendo las variables originales (4 variables)
a las componentes principales (2 variables) con el método components_. Graficamos
los resultados de las contribuciones en la Figura 8.22 (primera componente principal)
170 CAPÍTULO 8. MACHINE LEARNING
La variable que más contribuye a la primera componente principal es el largo del pétalo,
mientras que a la segunda componente contribuyen casi en la misma medida el largo y
ancho del sépalo.
Ejercicios:
1. Dados los siguientes 5 puntos en 3 dimensiones:
3 6 −4 0 5
x1 = 5 , x2 = 4 , x3 = 2 , x4 = −2 , x5 = 6
−2 5 1 0 1
Encuentra su vector promedio µ y su matriz de covarianza Σ. ¿Cuáles son las dimen-
siones de Σ?
2. Dada la matriz de covarianza:
3 1
Σ=
1 3
8.3. APRENDIZAJE NO SUPERVISADO: CLUSTERING 171
El cálculo del “centro de masa” es más sencillo que en física, pues todas las masas
son iguales, y se restringe a tomar el promedio de las distancias en cada una de las d
dimensiones. Por ejemplo, en 2 dimensiones, usando los nombres de las coordenadas
usuales (x, y), el centro de masa está dado por:
n n
!
1X 1X
xCM = xi , yi
n i=1 n i=1
Como los primeros centros se eligen aleatoriamente, en sklearn la función que se utiliza
corre varias veces el programa para determinar la mejor elección, minimizando la suma
de las distancias de los puntos a los centros.
Aunque ya conocemos la clasificación de las flores iris, vamos a seguir utilizando ese
conjunto de datos para ver qué clústeres se forman.
Comparemos los grupos reales con los clústeres formados con KMeans, en 2 figuras
por separado (Figura 8.24). En la figura con los clústeres formados, podemos graficar
también los centros para que quede clara su ubicación:
1 In [21]:
2 from sklearn . cluster import KMeans
3 kmeans = KMeans (3) # 3 cl ú sters
4 kmeans . fit ( X [: ,[2 ,3]])
5 pred = kmeans . predict ( X [: ,[2 ,3]])
6 centros = kmeans . cluster_centers_
7 plt . figure ()
8 plt . subplot (2 ,1 ,1) # primera subfigura
9 for k in range (3) :
10 plt . scatter ( X [ y == k ,2] , X [ y == k ,3] ,\
11 color = colores [ k ] , label = nombres [ k ])
12 plt . xlabel ( var [2])
13 plt . ylabel ( var [3])
14 plt . legend ()
15 plt . title ( " Verdaderos grupos " )
16 plt . subplot (2 ,1 ,2) # segunda subfigura
17 for k in range (3) :
18 plt . scatter ( X [ pred == k ,2] , X [ pred == k ,3] , label = nombres [ k ])
19 plt . scatter ( centros [ k ][0] , centros [ k ][1] , color = " yellow " ,\
20 edgecolor = " brown " , s =100)
21 plt . xlabel ( var [2])
22 plt . ylabel ( var [3])
23 plt . title ( " Clusters formados " )
24 plt . tight_layout ()
25 plt . show ()
8.3. APRENDIZAJE NO SUPERVISADO: CLUSTERING 173
Figura 8.24: Arriba: la verdadera clasificación de los datos. Abajo: los datos agrupados
con clustering de k-means. En amarillo se muestran los centros de los clústers
174 CAPÍTULO 8. MACHINE LEARNING
Ejercicios:
1. Dados los puntos:
(1, 4), (2, 1), (4, 2), (5, 3)
Encuentra su centro de masa.
2. Volviendo a los datos de los vinos, y seleccionando las columnas 0 y 6, correspondien-
tes a las variables “alcohol” y “flavonoids”, entrena todos los datos y prueba con ellos
mismos con el método de clustering de k-means.
Donde |Ck | es el número de elementos en el clúster k. Esto es, se suman todas las distan-
cias de los puntos de un clúster al otro, y se divide entre el número de combinaciones.
Así, al principio los 2 puntos más cercanos se unen en un clúster de 2 elementos. Después,
ya sea que otro par de puntos se unan para formar otro clúster de 2 elementos, o un
tercer punto se una al primer clúster. Así hasta que todos los puntos pertenecen a un
solo clúster. Es en un paso intermedio cuando se decide con cuántos clústeres quedarse.
La forma de agrupar clústeres con Clustering jerárquico en sklearn es casi el mismo que
con KMeans, con la función AgglomerativeCluster, la diferencia siendo que esta fun-
ción no tiene el método predict, hay que aplicar el método fit_predict directamente
para ajustar con los datos y probar el método en ellos mismos:
1 In [22]:
2 from sklearn . cluster import A g g l o m e r a t i v e C l u s t e r i n g
3 def cluster ( m é todo , X ) :
4 pred = m é todo . fit_predict ( X )
5 return ( pred )
Como antes, escogemos los datos de las flores iris y probamos la función
AgglomerativeClustering con argumento 3 clústeres. Por default, el criterio de unión
de los clústers es el criterio de “Ward”, pero seleccionemos en su lugar la distancia
promedio entre los clústeres. Esto se selecciona con el parámetro linkage (Figura 8.25):
1 In [23]:
2 pred = cluster ( A g g l o m e r a t i v e C l u s t e r i n g (3 , linkage = " average " ) ,\
3 X [: ,[2 ,3]] )
4 plt . figure ()
5 plt . scatter ( X [: ,2] , X [: ,3] , c = pred , cmap = " prism " )
8.3. APRENDIZAJE NO SUPERVISADO: CLUSTERING 175
Figura 8.25: Clustering jerárquico de los datos, con criterio de unión UPGMA
Seleccionamos UPGMA al ser uno de los métodos más utilizadoss para formar árboles
filogenéticos, aunque no necesariamente es el mejor para formar clústers en general.
Para ver en detalle cómo se van conformando los clústers, grafiquemos un dendrograma.
Primero grafiquemos cómo queda el dendrograma completo, con todos los puntos:
1 In [24]:
2 from scipy . cluster . hierarchy import dendrogram , average
3 plt . figure ( figsize =(10 ,4) )
4 dendrogram ( average ( X [: ,[2 ,3]]) )
5 plt . title ( " Dendrograma con clustering jer á rquico , UPGMA " )
6 plt . show ()
En la Figura 8.26 se ve que, de arriba hacia abajo los datos se dividen en 2 grupos o
clústeres, uno dando lugar al grupo naranja en la parte izquierda, y el otro al verde
en la parte derecha. Los dos grupos se siguen subdividiendo, pero primero lo hace el
grupo verde. Se puede seleccionar el número de clústeres deseados y trazar una línea
horizontal para ver qué puntos quedan en los clústeres a ese nivel.
Para que no queden tan concentrados los puntos, hagamos otros dendrograma fijando
el número de niveles que deseamos mostrar con el parámetro p (Figura 8.27):
1 In [25]:
2 plt . figure ()
3 dendrogram ( average ( X [: ,[2 ,3]]) , p =2 , truncate_mode = " level " )
4 plt . title ( " Dendrograma con clustering jer á rquico , UPGMA " )
5 plt . show ()
176 CAPÍTULO 8. MACHINE LEARNING
En este dendrograma recortado a 2 niveles, los números sin paréntesis, esto es, el 24 y el
44, son los índices de los puntos que permanecen como hojas del árbol, o clústeres de un
elemento. Los números entre paréntesis son el número de puntos que están concentrados
en clústers de más de un elemento a ese nivel. En total, en este ejemplo, la suma de
los números entre paréntesis da 148 puntos, más las 2 hojas, completando los 150 datos
originales.
Ejercicios:
1. Dado el clúster C1 , con puntos con coordenadas:
Encuentra la distancia promedio entre los clústeres, y su distancia mínima (la mínima
distancia de los puntos de un clúster al otro).
2. Volviendo a los datos de los vinos, y seleccionando las columnas 0 y 6, correspon-
dientes a las variables “alcohol” y “flavonoids”, entrena todos los datos y prueba
en ellos mismos con el método de clustering de jerarquía.
3. Dibuja el dendrograma asociado al ejercicio 2, recortado a 3 niveles.
Bibliografía
Libros de Programación
Compeau Phillip y Pevzner Pavel, (2018), Bioinformatics Algorithms, An Active Lear-
ning Approach (3a ). Active Learning Publishers.
Géron, Aurélien, (2017), Hands-On, Machine Learning with Scikit-Learn & TensorFlow,
O’Reilly.
VanderPlas Jake, (2016), Python Data Science Handbook. O´Reilly, o su versión elec-
trónica [Link]
Libros de matemáticas
Barnes, B y Fulford, G. R., (2015), Mathematical Modelling with case studies, (3a ).
CRC Press.
Lay, David C., Lay, Steven R, y McDonald Judi J., (2016), Álgebra Lineal y sus apli-
caciones (5a ), Pearson.
Zill, Dennis G., (2018), Ecuaciones Diferenciales con problemas de valores en la frontera
(9a ), Cengage.
Cursos en línea
Campbell Jennifer y Gries Paul, Learn to program: the Fundamentals [Coursera],
[Link]
179
180 CAPÍTULO 8. MACHINE LEARNING
Referencias en línea
Colores web, en Wikipedia: [Link]
Guo, Phillip, (2014, 7 de julio), Python is now the most popular introductory teaching
language at top U.S. Universities, Communications of the ACM,
[Link]
language-at-top-u-s-universities/
Juego de la vida, en Wikipedia, [Link]
Keep Coding, (2024, 10 de abril), ¿Cuál es el mejor lenguaje de programación? [Top 5],
[Link]
Matplotlib, Choosing Colormaps in Matplotlib:
[Link]
Matplotlib, [Link]: [Link]
Rotten Tomatoes, (s.f.), 100 best computer-animated movies ranked by tomatometer,
[Link]
Programación y matemáticas con Python
se terminó de imprimir en diciembre de 2025,
en el taller de impresión de la
Universidad Autónoma de la Ciudad de México,
San Lorenzo, 290, Col. Del Valle,
Alcaldía Benito Juárez, C. P. 03100,
Ciudad de México con un tiraje de 1000 ejemplares.
Cuidado de la edición: Ángeles Godínez Guevara
Diseño editorial: Sergio Cortés Becerril
En este libro, el lector encontrará, además de la introducción a la programación
con Python, módulos o bibliotecas para graficar, y de algunas áreas de las mate-
máticas como álgebra lineal, estadística, o métodos numéricos para ecuaciones
diferenciales ordinarias, por lo que, además de ser un libro de apoyo para Intro-
ducción a la Programación, también lo es para asignaturas de matemáticas. Adi-
cionalmente, el lector encontrará módulos de introducción a tratamientos de datos
y de machine learning, tan necesarios en la actualidad que serán de utilidad no
sólo para los estudiantes, sino también para la comunidad académica.
ERIKA LORENA ÁLVAREZ RAMÍREZ es profesora-investigadora del
Colegio de Ciencia y Tecnología. Forma parte de la Academia de
Física en el Plantel Casa Libertad. Es Maestra en Ciencias (Físi-
ca) por la UNAM, donde también cursó la Licenciatura en Física.
Sus participaciones académicas y de los órganos de gobierno han
sido como Coordinadora del Colegio de Ciencia y Tecnología
(2023-2025), y como Secretaria Técnica de la Comisión de Ha-
cienda del primero y sexto Consejos Universitarios.