Recordando Python

En esta clase repasaremos algunos conceptos de Python que probablemente ya conoces, pero te servirá para familiarizarte con Jupyter Notebook y refrescar la memoria.

Jupyter Notebooks

Ojo Piojo

Cada archivo Jupyter Notebook se compone de celdas, que pueden ser Code, Markdown o Raw.

Attention

Los jupyter notebooks poseen dos modos actualmente, de edición y comandos. Dependiendo del modo que se encuentre activo el teclado realiza diferentes cosas.

  • Modo Edición: Escribes directamente en la celda, de igual manera como si estuvieras en un editor de texto. Se reconoce este modo pues la celda activa posee un borde de color verde (en el theme por defecto).

  • Modo Comando: Es posible editar acciones al notebook como un todo, no dentro de una celda. Por ejemplo, copiar o crear celdas. Se reconoce este modo pues en una celda hay un borde azul al lado izquierdo.

La documentación oficial de Jupyter Notebook es mucho más formal, pero por lo mismo más extensa, aquí está el link para cuando la necesites.

Algunas otras cosas útiles a saber para trabajar utilizando notebooks:

  • File: En él, puede crear un nuevo cuaderno o abrir uno preexistente. Aquí es también a donde iría para cambiar el nombre de un Cuaderno. Creo que el elemento de menú más interesante es la opción Guardar y Checkpoint. Esto le permite crear puntos de control a los que puede retroceder si lo necesita.

  • Edit: Aquí puede cortar, copiar y pegar celdas. Aquí también es donde irías si quisieras eliminar, dividir o fusionar una celda. Puede reordenar celdas aquí también.

  • View: es útil para alternar la visibilidad del encabezado y la barra de herramientas. También puede activar o desactivar los números de línea dentro de las celdas. Aquí también es donde irías si quieres meterte con la barra de herramientas de la celda.

  • Insert: es solo para insertar celdas encima o debajo de la celda seleccionada actualmente.

  • Cell: le permite ejecutar una celda, un grupo de celdas o todas las celdas. También puede ir aquí para cambiar el tipo de celda, aunque personalmente considero que la barra de herramientas es más intuitiva para eso.

  • Kernel: es para trabajar con el kernel que se ejecuta en segundo plano. Aquí puede reiniciar el kernel, volver a conectarlo, apagarlo o incluso cambiar el kernel que está utilizando su computadora portátil.

  • Widgets: es para guardar y borrar el estado del widget. Los widgets son básicamente widgets de JavaScript que puede agregar a sus celdas para crear contenido dinámico utilizando Python (u otro Kernel).

  • Help: es donde debe aprender sobre los atajos de teclado del Notebook, un recorrido por la interfaz de usuario y mucho material de referencia.

Markdown

Jupyter Notebook permite que escribamos texto formateado, es decir, texto con cursiva, negritas, títulos de distintos tamaños, etc., de forma simple. Para ello Jupyter nos permite usar Markdown, que es un lenguaje de marcado (markup) muy popular.

Los lenguajes de markup son lenguajes ideados para procesar texto, algunos de los más conocidos son HTML y \(\LaTeX\). Markdown tiene como objetivo ser un lenguaje de sintaxis minimalista, simple de aprender y usar; de esa forma uno puede dar formato al texto pero sin perder demasiado tiempo en los detalles.

La cantidad de tutoriales en la red sobre Markdown es inmenso, por lo que nos centraremos en indicar las opciones que más se utilizan.

  • Texto en negrita/cursiva: El texto en negrita se indica entre dos pares de asteriscos. De este modo **palabra** aparecerá como palabra. Por otro lado, el texto en cursiva se indica entre dos asteriscos simples; es decir *palabra* aparecerá como palabra.

  • Listas: Las listas en Markdown se realizan indicando un asterisco o un número seguido de un punto si se desean listas numeradas. Markdown organiza automáticamente los items asignándoles el número correcto.

  • Inclusión de imágenes: La sintaxis para incluir imágenes en Markdown es ![nombre alternativo](dirección de la imagen) en donde el nombre alternativo aparecerá en caso de que no se pueda cargar la imágen y la dirección puede referirse a una imagen local o un enlace en Internet.

  • Inclusión de código HTML: El lenguaje Markdown es un subconjunto del lenguaje HTML y en donde se necesite un mayor control del formato, se puede incluir directamente el código HTML.

  • Enlaces: Las celdas de texto pueden contener enlaces, tanto a otras partes del documento, como a páginas en internet u otros archivos locales. Su sintaxis es [texto](dirección del enlace).

  • Fórmulas matemáticas: Gracias al uso de MathJax, se puede incluir código en \(\LaTeX\) para mostrar todo tipo de fórmulas y expresiones matemáticas. Las fórmulas dentro de una línea de texto se escriben entre símbolos de dólar $...$, mientras que las expresiones separadas del texto utilizan símbolos de dólar dobles $$...$$. Los siguientes son ejemplos de fórmulas matemáticas escritas en \(\LaTeX\):

\[p(x) = 3x^2 + 5y^2 + x^2y^2\]
\[e^{\pi i} - 1 = 0\]
\[\lim_{x \rightarrow \infty} 3x+1\]
\[\sum_{n=1}^\infty\frac{1}{n^2}\]
\[\int_0^\infty\frac{\sin x}{x}\,\mathrm{d}x=\frac{\pi}{2}\]
\[\begin{split}R^2 = \begin{pmatrix} c & s \end{pmatrix} \begin{pmatrix} 1 & 0\\ 0 & 1 \end{pmatrix} \begin{pmatrix} c \\ s \end{pmatrix} = c^2 + s^2\end{split}\]

Hola Mundo!

Imprimir texto es tan fácil como:

print('Hello World!')
Hello World!
name = 'John Titor'

Cuando escribes texto entre comillas, simples (‘) o dobles (“) la variable pasa a ser de tipo str (string).

type(name)
str

Guardar variables de string te permite, entre otras cosas, parametrizar mensajes, títulos, loggings, etc.

print("Hola {}!".format(name))
Hola John Titor!

Desde Python 3.6 también puedes hacer uso de los f-strings

print(f"Hola {name}!")
Hola John Titor!

Los strings poseen métodos propios, por ejemplo

name.upper()
'JOHN TITOR'

Tip

En Jupyter Notebook es posible autocompletar métodos y atributos utilizando la tecla TAB, siempre y cuando el objeto ya esté definido.

Tip

Para ver los métodos y atributos de una instancia basta con utilizar dir(variable).

Tip

En Jupyter, no siempre es necesario utilizar print en cada celda.

Números y operaciones

Un pequeño recuerdo de las operaciones matemáticas más comunes.

1 + 2  # Suma
3
100 - 99  # Resta
1
3 * 4  # Multiplicación
12
42 / 4  # En python 2 la división por defecto es entera
10.5
43 // 4  # División parte entera
10
14 % 4  ## Operación módulo
2

Listas, tuplas, conjuntos y diccionarios

Listas, tuplas y conjuntos son colecciones de objetos en Python que poseen ligeras diferencias. Comencemos con las listas que son probablemente las que resultan más naturales.

# Listas
my_list = [1, 2, 3, 4, 5]
print(my_list[0])
1
type(my_list)
list

Puedes acceder a los elementos de tu lista utilizando los corchetes cuadrados [].

print(my_list[0])
print(my_list[-1])
print(my_list[0:2])
print(my_list[2:])
print(my_list[::2])
print(my_list[:-2])
1
5
[1, 2]
[3, 4, 5]
[1, 3, 5]
[1, 2, 3]

Las tuplas en un comienzo se ven muy parecidas a las listas

my_tuple = (1, 2, 3, 4, 5)
print(my_tuple[4])
5
type(my_tuple)
tuple

Las listas pueden ser modificadas

my_list.append(100)  
my_list
[1, 2, 3, 4, 5, 100]
my_list[1] = 42
my_list
[1, 42, 3, 4, 5, 100]

Pero las tuplas no

my_tuple[1] = 42
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-2a776f593879> in <module>
----> 1 my_tuple[1] = 42

TypeError: 'tuple' object does not support item assignment
# Las tuplas son objetos inmutables!
try:
    my_tuple.append(50)
except Exception as e:
    print(e)
'tuple' object has no attribute 'append'

Los conjuntos (sets) son colecciones que no considerar elementos repetidos.

# Conjuntos
my_set = {1, 1, 1, 2, 2, 3}
print(my_set)
{1, 2, 3}

Los conjuntos no tienen orden!

my_set[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-23-158c424478a1> in <module>
----> 1 my_set[0]

TypeError: 'set' object is not subscriptable

Finalmente, los diccionarios son conjuntos en los cuales para acceder a sus valores se necesita de una llave.

my_dict = {
    'llave': 'corazón',
    'sonrisa': 'corazones'
}
print(my_dict['sonrisa'])
corazones
my_dict.values()
dict_values(['corazón', 'corazones'])
my_dict.keys()
dict_keys(['llave', 'sonrisa'])

Otra cosa importante es que tampoco tienen orden! Ni el diccionario, ni sus llaves ni sus valores

my_dict[0]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-27-2e042f7087f7> in <module>
----> 1 my_dict[0]

KeyError: 0
my_dict.keys()[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-b08788a06b3c> in <module>
----> 1 my_dict.keys()[0]

TypeError: 'dict_keys' object is not subscriptable

Control Flow

Es común que en tus desarrollos quieras controlar ciertas acciones, Python no se queda atrás e implementa los condicionales if, elif y else.

x = 7  # Cambia el valor de x cuando quieras
cota_inferior = 5
cota_superior = 10
if x < cota_inferior:
    print('{} es menor que {}'.format(x, cota_inferior))
elif x < cota_superior:
    print('{} es mayor igual que {} y menor que {}'.format(x, cota_inferior, cota_superior))
else:
    print('{} es mayor igual que {}'.format(x, cota_superior))
7 es mayor igual que 5 y menor que 10

Por otros lados los ciclos son útiles cuando quieres ejecutar una acción varias veces hasta que se cumpla una condición.

# Cilo While
i = 0
while i < 10:
    print(f"El cuadrado de {i} es {i ** 2}")
    i += 1
El cuadrado de 0 es 0
El cuadrado de 1 es 1
El cuadrado de 2 es 4
El cuadrado de 3 es 9
El cuadrado de 4 es 16
El cuadrado de 5 es 25
El cuadrado de 6 es 36
El cuadrado de 7 es 49
El cuadrado de 8 es 64
El cuadrado de 9 es 81
# Ciclo For
for i in range(1, 10, 2):
    if i < 5:
        print('{} es menor que 5'.format(i))
    else:
        print('{} es mayor igual que 5'.format(i))
1 es menor que 5
3 es menor que 5
5 es mayor igual que 5
7 es mayor igual que 5
9 es mayor igual que 5

Warning

Los objetos range no son listas

my_range = range(1, 10, 2)
isinstance(my_range, list)  # Puedes validar el tipo de una variable de esta manera
False
type(my_range)
range
range?
my_range.start
1
my_range.start
1
my_range.stop
10
my_range.step
2

Funciones

Para definir tu propia función la sintaxis es muy sencilla, por ejemplo, definamos la función que retorna la parte entera de la diferencia absoluta de dos números.

def abs_diff_floor(x, y):
    """ Retorna la diferencia entera entre dos valores."""
    diff = abs(x - y)
    value = int(diff)
    return value 
abs_diff_floor(20.3, 32.6)
12

O inclusive encapsular cosas que ya hemos hecho con anterioridad, como por ejemplo:

def my_function(x, cota_inferior=5, cota_superior=10):
    if x < cota_inferior:
        print('{} es menor que {}'.format(x, cota_inferior))
    elif x < cota_superior:
        print('{} es mayor igual que {} y menor que {}'.format(x, cota_inferior, cota_superior))
    else:
        print('{} es mayor igual que {}'.format(x, cota_superior))
my_function(5)
5 es mayor igual que 5 y menor que 10
my_function(3, 4, 10)
3 es menor que 4

Librerías

Existen librerías de todo tipo para toda clase de tareas en Python, sin embargo, sería poco eficiente que todas estén disponibles por defecto al iniciar un kernel, por lo que el usuario debe manualmente importarlas o incluso instalarlas.

Por ejemplo, ¿Cómo obtener la raíz cuadrada de 49? Fácil, ¿no?

sqrt(49)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-45-d681b475f5f9> in <module>
----> 1 sqrt(49)

NameError: name 'sqrt' is not defined

Wait?! No está implementada?!

Nop! Solo debes importar el módulo de operaciones matemáticas que viene instalado por defecto en Python, ni te imaginas su nombre.

import math  # Yep, math...
math?
math.sqrt(49)
7.0

También hay otras fuinciones, por ejemplo floor

math.floor(2.6)
2

En ocasiones te gustaría utilizar tus funciones en otros trabajos, para eso los puedes exportar como archivos .py e importarlos cuando lo necesites. Por ejemplo, en la misma carpeta que este notebook está el archivo my_packaged_function.py

%ls
M1L01_about_mat281.ipynb               M3L04_interactive_visualization.ipynb
M1L02_installation.md                  M4L01_modeling.ipynb
M1L02_toolkit_and_setup.ipynb          M4L02_regression.ipynb
M1L03_python_basics.ipynb              M4L03_classification.ipynb
M1L04_git_update.md                    M4L04_classification_nonparametric.ipynb
M1L04_python_ecosystem.ipynb           M4L05_clustering.ipynb
M2L01_scientific_computing.ipynb       M4L06_cross_validation.ipynb
M2L02_linear_algebra.ipynb             M4L07_model_selection.ipynb
M2L03_data_manipulation.ipynb          M4L08_metrics.ipynb
M2L04_data_combining.ipynb             M5L01_real_world.ipynb
M2L05_data_aggregation.ipynb           lorenz.py
M2L06_eda.ipynb                        model.py
M3L01_theory_and_landscape.ipynb       mpl_heatmap.py
M3L02_imperative_visualization.ipynb   my_packaged_function.py
M3L03_declarative_visualization.ipynb
%pycat my_packaged_function.py
import my_packaged_function as mpf  # Puedes importar librerías con un alias

Hint

text Recuerda que en Jupyter puedes explorar la documentación de prácticamente todo con los signos de interrogación ?. Con ?? te permite ver el código de fuente.

mpf?
mpf??
mpf.pkg_abs_diff_floor?
mpf.pkg_abs_diff_floor??
mpf.pkg_abs_diff_floor(10, 2.3)
7

Hint

Jupyter está basado en IPython, por lo que puedes hacer uso de los comandos mágicos.

# Descomenta y ejecuta esta línea para ver la documentación completa
# %magic
# Lista de comandos mágicos disponibles
%lsmagic
Available line magics:
%alias  %alias_magic  %autoawait  %autocall  %automagic  %autosave  %bookmark  %cat  %cd  %clear  %colors  %conda  %config  %connect_info  %cp  %debug  %dhist  %dirs  %doctest_mode  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %lf  %lk  %ll  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %lx  %macro  %magic  %man  %matplotlib  %mkdir  %more  %mv  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %pip  %popd  %pprint  %precision  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %rep  %rerun  %reset  %reset_selective  %rm  %rmdir  %run  %save  %sc  %set_env  %store  %sx  %system  %tb  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode

Available cell magics:
%%!  %%HTML  %%SVG  %%bash  %%capture  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%markdown  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile

Automagic is ON, % prefix IS NOT needed for line magics.

Existen comando mágicos de línea (%)y de celda completa (%%)

%timeit comprehension_list = [i*2 for i in range(1000)]
56.7 µs ± 26.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit

no_comprehension_list = []
for i in range(1000):
    no_comprehension_list.append(i * 2)
103 µs ± 81.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Danger

Ejecutar un timeit es una prueba, no crea tomo tal las variables.

len(comprehension_list)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-62-9fc0a2545cfb> in <module>
----> 1 len(comprehension_list)

NameError: name 'comprehension_list' is not defined