Automatizaciones#

Con Python podemos llegar a realizar desde operaciones sencillas de datos hasta visualizaciones complejas o automatizaciones de procesos.

En Python hay muchas librerias y frameworks creados por la comunidad para resolver muchos procesos. Así que si tienes un proceso que quieras realizar, busca si ya existe una librería con la que puedas facilitarte la programación y en unas cuantas lineas de código.

Guardar mapas#

!pip install folium
Requirement already satisfied: folium in /usr/local/lib/python3.10/dist-packages (0.14.0)
Requirement already satisfied: branca>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from folium) (0.7.2)
Requirement already satisfied: jinja2>=2.9 in /usr/local/lib/python3.10/dist-packages (from folium) (3.1.4)
Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from folium) (1.25.2)
Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from folium) (2.31.0)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2>=2.9->folium) (2.1.5)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->folium) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->folium) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->folium) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->folium) (2024.6.2)
!wget https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_states.json
--2024-07-10 17:23:54--  https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_states.json
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 87688 (86K) [text/plain]
Saving to: ‘us_states.json.1’


us_states.json.1      0%[                    ]       0  --.-KB/s               
us_states.json.1    100%[===================>]  85.63K  --.-KB/s    in 0.02s   

2024-07-10 17:23:54 (3.79 MB/s) - ‘us_states.json.1’ saved [87688/87688]
import pandas as pd

import folium
import json

state_geo = json.load(open("us_states.json"))

state_data = pd.read_csv(
    "https://raw.githubusercontent.com/python-visualization/folium-example-data/main/us_unemployment_oct_2012.csv"
)
state_data.head()
State Unemployment
0 AL 7.1
1 AK 6.8
2 AZ 8.1
3 AR 7.2
4 CA 10.1
m = folium.Map(location=[48, -102], zoom_start=5,width="80%", height="80%", control_scale=True)
# Agreguemos la informacion del dataframe en el mapa
folium.Choropleth(
    geo_data=state_geo,     # data geográfica (JSON con los estados)
    name="choropleth",       # nombre del mapa
    data=state_data,        # dataframe con los datos
    columns=["State", "Unemployment"],  # columnas del dataframe
    key_on="feature.id",    # llave para unir los datos
    fill_color="YlGn",      #
    fill_opacity=0.7,      # opacidad del color
    line_opacity=0.2,      # opacidad de la linea
    legend_name="Unemployment Rate (%)",  # nombre de la leyenda
    highlight=True,        # resaltar el estado al pasar el mouse por encima
    nan_fill_color='white',
    nan_fill_opacity=0.5,
).add_to(m)               # Agregar este gráfico al mapa anterior

folium.LayerControl().add_to(m) # Agregar controles al mapa para moverlo
m
Make this Notebook Trusted to load map: File -> Trust Notebook
m.save("map.html")

Envío de correos con python#

Enviar correos electrónicos desde aplicaciones de Python es una tarea común y necesaria en muchos proyectos, desde notificaciones automáticas hasta boletines informativos. Python facilita esta tarea mediante la biblioteca estándar smtplib, que proporciona una interfaz simple y efectiva para enviar correos electrónicos utilizando el protocolo SMTP (Simple Mail Transfer Protocol).

# Importemos las librerias
from email.message import EmailMessage
import smtplib

# definamos la información que necistamos
remitente = "direccion@gmail.com"
destinatario = "destinatario@ejemplo.com"

mensaje = "¡Hola, mundo!"
email = EmailMessage()

email["From"] = remitente
email["To"] = destinatario
email["Subject"] = "Correo de prueba de automatización"
email.set_content(mensaje)

Para poder autenticarte en el servidor de google necesitas la clave de aplicaciones para poder generar una clave, esta es diferente a la contraseña del correo

Crear Clave de aplicación

smtp = smtplib.SMTP_SSL("smtp.gmail.com") # Iniciamos la conexión a gmail, para otros correos se debe cambiar el parametro.
smtp.login(remitente, "clave_de_gmail_123") # Autenticación con la clave de aplicaciones de google
smtp.sendmail(remitente, destinatario, email.as_string()) # Enviamos el correo
smtp.quit() # Cerramos la conexión
(221,
 b'2.0.0 closing connection 8926c6da1cb9f-4c0b1b1094bsm1286049173.43 - gsmtp')
# Este sería el código para enviar desde un correo de outlook.
smtp = smtplib.SMTP("smtp-mail.outlook.com", port=587)
smtp.starttls()
smtp.login(remitente, "clave_de_outlook_123") # Autenticación con la contraseña del correo (esta si es la contraseña de la cuenta desde la que quieres enviar el correo)
smtp.sendmail(remitente, destinatario, email.as_string())
smtp.quit()

Envío de adjuntos.#

# Importemos las librerias
from email.message import EmailMessage
import smtplib

# definamos la información que necistamos
remitente = "direccion@gmail.com"
destinatario = "destinatario@ejemplo.com"

mensaje = "¡Hola, mundo!"
email = EmailMessage()

email["From"] = remitente
email["To"] = destinatario
email["Subject"] = "¡Enviado desde Python!"

email.set_content(mensaje)


smtp = smtplib.SMTP_SSL("smtp.gmail.com") # Iniciamos la conexión a gmail, para otros correos se debe cambiar el parametro.
smtp.login(remitente, "clave_de_gmail_123") # Autenticación con la clave de aplicaciones de google
(235, b'2.7.0 Accepted')
# Adjuntar un archivo .html que creamos del mapa

with open("map.html", "rb") as f:
    email.add_attachment(
        f.read(),
        filename="map.html",
        maintype="text",
        subtype="html"
    )

import matplotlib.pyplot as plt
# Crear y adjuntar una gráfica como archivo .png
plt.plot([1, 2, 3, 4], [10, 20, 25, 30])
plt.title('Gráfica de ejemplo')

plt.savefig("grafica.png") # Tenemos que guardar el archivo

with open("grafica.png", "rb") as f:
    email.add_attachment(
        f.read(),
        filename="grafica.png",
        maintype="image",
        subtype="png"
    )
_images/db9dc1577fe3d34281f3c0fa78faf15a5be6065081bbb041498d480ed1123c3d.png
smtp.sendmail(remitente, destinatario, email.as_string())
smtp.quit()
(221,
 b'2.0.0 closing connection 8926c6da1cb9f-4c0b1b0d427sm1299772173.64 - gsmtp')

Envío masivo de correos.#

import pandas as pd


df = pd.read_csv("https://raw.githubusercontent.com/BioAITeamLearning/IntroPython_2024_01_UAI/main/Data/users.csv")
df
Unnamed: 0 first_name last_name email email_verified signup_date referral_count balance
0 aaron Aaron Davis aaron6348@gmail.com True 2018-08-31 6 18.14
1 acook Anthony Cook cook@gmail.com True 2018-05-12 2 55.45
2 adam.saunders Adam Saunders adam@gmail.com False 2018-05-29 3 72.12
3 adrian Adrian Fang adrian.fang@teamtreehouse.com True 2018-04-28 3 30.01
4 adrian.blair Adrian Blair adrian9335@gmail.com True 2018-06-16 7 25.85
... ... ... ... ... ... ... ... ...
470 wilson Robert Wilson robert@yahoo.com False 2018-05-16 5 59.75
471 wking Wanda King wanda.king@holt.com True 2018-06-01 2 67.08
472 wright3590 Jacqueline Wright jacqueline.wright@gonzalez.com True 2018-02-08 6 18.48
473 young Jessica Young jessica4028@yahoo.com True 2018-07-17 4 75.39
474 zachary.neal Zachary Neal zneal@gmail.com True 2018-07-26 1 39.90

475 rows × 8 columns

Vamos a enviarle correos a nuestras cuentas verificadas y con un balance mayor a 95 USD

df_verified = df[(df["email_verified"] == True) & (df["balance"] > 95)]
df_verified
Unnamed: 0 first_name last_name email email_verified signup_date referral_count balance
68 christopher Christopher NaN christopher@gmail.com True 2018-07-24 3 96.96
74 clayton6074 Robert Clayton robert@gmail.com True 2018-01-12 7 97.05
84 crane203 Valerie Crane valerie7051@hotmail.com True 2018-05-12 3 98.69
139 eugene4448 Eugene NaN eugene@gmail.com True 2018-06-08 0 95.38
208 jennifer.wong Jennifer Wong jwong@yahoo.com True 2018-07-03 7 95.10
234 joseph2431 Joseph Harris joseph@gmail.com True 2018-03-07 5 95.47
245 karen.snow Karen Snow ksnow@yahoo.com True 2018-05-06 2 99.38
259 king Billy King billy.king@hotmail.com True 2018-05-29 4 98.80
260 king3246 Brittney King brittney@yahoo.com True 2018-04-15 6 98.79
296 margaret265 Margaret NaN margaret@gmail.com True 2018-01-05 1 96.14
357 paul6364 Paul Hill paul2980@hotmail.com True 2018-02-28 1 98.62
361 peter Peter Grimes pgrimes@yahoo.com True 2018-09-02 0 96.79
398 sbennett Steven Bennett sbennett@yahoo.com True 2018-02-07 5 97.38
453 twhite Timothy White white5136@hotmail.com True 2018-07-06 5 99.90
# Veamos que le vamos a mandar a cada uno con la informacion del dataset

for index, row in df_verified.iterrows():
    print(f"Hello,{row['first_name']} this is your balance: {row['balance']}. This is just a test")
Hello,Christopher this is your balance: 96.96. This is just a test
Hello,Robert this is your balance: 97.05. This is just a test
Hello,Valerie this is your balance: 98.69. This is just a test
Hello,Eugene this is your balance: 95.38. This is just a test
Hello,Jennifer this is your balance: 95.1. This is just a test
Hello,Joseph this is your balance: 95.47. This is just a test
Hello,Karen this is your balance: 99.38. This is just a test
Hello,Billy this is your balance: 98.8. This is just a test
Hello,Brittney this is your balance: 98.79. This is just a test
Hello,Margaret this is your balance: 96.14. This is just a test
Hello,Paul this is your balance: 98.62. This is just a test
Hello,Peter this is your balance: 96.79. This is just a test
Hello,Steven this is your balance: 97.38. This is just a test
Hello,Timothy this is your balance: 99.9. This is just a test
# Ahora creemos una función que envíe el correo y que como parametro reciba, mensaje y correo de destino.
# Importemos las librerias
from email.message import EmailMessage
import smtplib

def send_mail(mensaje,destinatario):
  remitente = "remitente@gmail.com"

  email = EmailMessage()

  email["From"] = remitente
  email["To"] = destinatario
  email["Subject"] = "¡Enviado desde Python test!"

  email.set_content(mensaje)


  smtp = smtplib.SMTP_SSL("smtp.gmail.com") # Iniciamos la conexión a gmail, para otros correos se debe cambiar el parametro.
  smtp.login(remitente, "clave_de_gmail_123") # Autenticación con la clave de aplicaciones de google
  smtp.sendmail(remitente, destinatario, email.as_string())
  smtp.quit()
# Ahora enviemos un correo por cada usuario del dataframe
for index, row in df_verified.iterrows():
  mensaje = f"Hello,{row['first_name']} this is your balance: {row['balance']}. This is just a test"
  destino = row['email']
  send_mail(mensaje,destino)

Dash#

Dash es un framework de Python diseñado para crear aplicaciones web interactivas de manera rápida y sencilla. Desarrollado por Plotly, Dash permite a los usuarios construir aplicaciones que integran visualizaciones de datos dinámicas y una interfaz de usuario intuitiva sin necesidad de conocimientos avanzados de desarrollo web.

Estructura de una Aplicación Dash#

Una aplicación típica de Dash consta de tres partes principales:

  1. Layout: Define la estructura visual de la aplicación, especificando los componentes y su disposición en la página. El layout se describe utilizando estructuras de datos de Python.

  2. Callbacks: Define las interacciones entre los componentes. Los callbacks son funciones que especifican cómo los componentes deben actualizarse en respuesta a eventos, como clics de botones o cambios en un formulario.

  3. Servidor: Dash incluye un servidor web integrado basado en Flask, lo que permite ejecutar la aplicación localmente durante el desarrollo y luego desplegarla fácilmente en producción.

!pip install dash # Instalemos la libreria de Dash
Requirement already satisfied: dash in /usr/local/lib/python3.10/dist-packages (2.17.1)
Requirement already satisfied: Flask<3.1,>=1.0.4 in /usr/local/lib/python3.10/dist-packages (from dash) (2.2.5)
Requirement already satisfied: Werkzeug<3.1 in /usr/local/lib/python3.10/dist-packages (from dash) (3.0.3)
Requirement already satisfied: plotly>=5.0.0 in /usr/local/lib/python3.10/dist-packages (from dash) (5.15.0)
Requirement already satisfied: dash-html-components==2.0.0 in /usr/local/lib/python3.10/dist-packages (from dash) (2.0.0)
Requirement already satisfied: dash-core-components==2.0.0 in /usr/local/lib/python3.10/dist-packages (from dash) (2.0.0)
Requirement already satisfied: dash-table==5.0.0 in /usr/local/lib/python3.10/dist-packages (from dash) (5.0.0)
Requirement already satisfied: importlib-metadata in /usr/local/lib/python3.10/dist-packages (from dash) (8.0.0)
Requirement already satisfied: typing-extensions>=4.1.1 in /usr/local/lib/python3.10/dist-packages (from dash) (4.12.2)
Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from dash) (2.31.0)
Requirement already satisfied: retrying in /usr/local/lib/python3.10/dist-packages (from dash) (1.3.4)
Requirement already satisfied: nest-asyncio in /usr/local/lib/python3.10/dist-packages (from dash) (1.6.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from dash) (67.7.2)
Requirement already satisfied: Jinja2>=3.0 in /usr/local/lib/python3.10/dist-packages (from Flask<3.1,>=1.0.4->dash) (3.1.4)
Requirement already satisfied: itsdangerous>=2.0 in /usr/local/lib/python3.10/dist-packages (from Flask<3.1,>=1.0.4->dash) (2.2.0)
Requirement already satisfied: click>=8.0 in /usr/local/lib/python3.10/dist-packages (from Flask<3.1,>=1.0.4->dash) (8.1.7)
Requirement already satisfied: tenacity>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from plotly>=5.0.0->dash) (8.4.2)
Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from plotly>=5.0.0->dash) (24.1)
Requirement already satisfied: MarkupSafe>=2.1.1 in /usr/local/lib/python3.10/dist-packages (from Werkzeug<3.1->dash) (2.1.5)
Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.10/dist-packages (from importlib-metadata->dash) (3.19.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->dash) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->dash) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->dash) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->dash) (2024.6.2)
Requirement already satisfied: six>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from retrying->dash) (1.16.0)
# Este pedazo de código es para poder correr Dash sobre los notebooks sin errores
# Nativamente, dash no funciona en Colab entonces debemos colocar estas dos lineas
from dash import jupyter_dash
jupyter_dash.default_mode="external"
from dash import Dash, html, dcc # Estos son los componentes de Dash que vamos a utilizar más adelante
import plotly.express as px # Importamos la librería de plotly para realizar los gráficos
import pandas as pd
app = Dash(__name__) # Creamos un objeto Dash, esta será la aplicación.
df = pd.DataFrame({
    "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
    "Amount": [4, 1, 2, 2, 4, 5],
    "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
})
df
Fruit Amount City
0 Apples 4 SF
1 Oranges 1 SF
2 Bananas 2 SF
3 Apples 2 Montreal
4 Oranges 4 Montreal
5 Bananas 5 Montreal
# Generamos un gráfico dinámico que luego vamos a poner en el dashboard.
fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
fig
# Definir el layout (distribución) de la aplicación
app.layout = html.Div(children=[
    html.H1(children='Hello Dash'),

    html.Div(children='''
        Dash: A web application framework for your data.
    '''),

    dcc.Graph(
        id='example-graph',
        figure=fig
    )
])

# Ejecutar la aplicación
if __name__ == '__main__':
    app.run_server(debug=True) # Ejecutamos la aplicación y vamos a ver el enlace que se genera, este lo podemos compartir en nuestro trabajo.
Dash app running on:

Ejemplo básico 2#

import plotly.express as px # Importamos la librería de plotly para realizar los gráficos

# Estos son los componentes de Dash que vamos a utilizar más adelante
from dash import Dash, html, dcc, callback, Output, Input
# Datos de ejemplo
df = pd.DataFrame({
    "Año": [2010, 2011, 2012, 2013, 2014],
    "Valor": [10, 15, 13, 17, 20]
})

# Inicializar la aplicación
app = Dash()
# Definir el layout (distribución) de la aplicación
app.layout = html.Div([
    dcc.Graph(id='grafico'), # Acá vamos a colocar un gráfico
    dcc.Slider(               # Debajo del gráfico vamos a colocar un slider
        id='slider',
        min=df['Año'].min(),   # El valor mínimo del slider
        max=df['Año'].max(),   # El valor máximo del slider
        value=df['Año'].min(),  # El valor inicial del slider
        marks={str(año): str(año) for año in df['Año']},  # Etiquetas que va a mostrar el slider
        step=None               # El paso del slider
    )
])
# Definir el callback para actualizar el gráfico
@app.callback(
    Output('grafico', 'figure'), # Definir en que objeto del layout colocar la salida de la funcion
    [Input('slider', 'value')] # Definir en qué objeto del layout está el elemento de entrada
)
def actualizar_grafico(año_seleccionado):
    df_filtrado = df[df['Año'] == año_seleccionado]
    fig = px.bar(df_filtrado, x='Año', y='Valor')
    return fig
# Ejecutar la aplicación
if __name__ == '__main__':
    app.run_server(debug=True) # Ejecutamos la aplicación y vamos a ver el enlace que se genera, este lo podemos compartir en nuestro trabajo.
Dash app running on:

Ejemplo intermedio#

# importamos pandas para leer el archivo csv
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder_unfiltered.csv')
df
country continent year lifeExp pop gdpPercap
0 Afghanistan Asia 1952 28.801 8425333 779.445314
1 Afghanistan Asia 1957 30.332 9240934 820.853030
2 Afghanistan Asia 1962 31.997 10267083 853.100710
3 Afghanistan Asia 1967 34.020 11537966 836.197138
4 Afghanistan Asia 1972 36.088 13079460 739.981106
... ... ... ... ... ... ...
3308 Zimbabwe Africa 1987 62.351 9216418 706.157306
3309 Zimbabwe Africa 1992 60.377 10704340 693.420786
3310 Zimbabwe Africa 1997 46.809 11404948 792.449960
3311 Zimbabwe Africa 2002 39.989 11926563 672.038623
3312 Zimbabwe Africa 2007 43.487 12311143 469.709298

3313 rows × 6 columns

df.keys()
Index(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap'], dtype='object')
import plotly.express as px # Importamos la librería de plotly para realizar los gráficos

# Estos son los componentes de Dash que vamos a utilizar más adelante
from dash import Dash, html, dcc, callback, Output, Input
# Creamos un objeto Dash, esta será la aplicación.
app = Dash()
# Vamos a definirle la organización del tablero que vamos a crear
app.layout = html.Div(
    [
        html.H1("Animated GDP and population over decades"),
        html.P("Select an animation:"),
        dcc.RadioItems(
            id="selection",
            options=["GDP - Scatter", "Population - Bar"],
            value="GDP - Scatter",
        ),
        html.Iframe( # Agreguemos el mapa que hicimos al principio
            srcDoc=open("map.html", "r").read(),
            width="100%",
            height="600px",
        ),
        dcc.Loading(dcc.Graph(id="graph"), type="cube"),
    ]
)
# El callback que va a actualizar la figura cuando se modifique el elemento de input.
@app.callback(
    Output("graph", "figure"), Input("selection", "value") # Definir los objetos de salida y de entrada de datos que se van a interactuar
)
def display_animated_graph(selection):
    animations = { # Definimos un diccionario que contiene para cada opción un tipo de gráfico diferente
        "GDP - Scatter": px.scatter( # El primero es un scatter entonces le asignamos a la llave "GDP - Scatter" como valor el gráfico de scatter con plotly
            df,                      # Establecemos el nombre del dataframe
            x="gdpPercap",            # Establecemos el eje x
            y="lifeExp",              # Establecemos el eje y
            animation_frame="year",   # Establecemos el eje de animación
            animation_group="country", # Establecemos el grupo de animación
            size="pop",                # Establecemos el tamaño del punto proporcional al valor de la columna pop
            color="continent",         # Establecemos el color del punto proporcional al valor de la columna continent
            hover_name="country",      # Establecemos el nombre que va a mostrar cada punto al pasar el mouse por encima
            log_x=True,                # Establecemos el eje x en escala logarítmica
            size_max=55,               # Establecemos el tamaño máximo del punto
            range_x=[100, 100000],     # Establecemos el rango del eje x
            range_y=[25, 90],          # Establecemos el rango del eje y
        ),
        "Population - Bar": px.bar( # El segundo es un bar entonces le asignamos a la llave "Population - Bar" como valor el gráfico de bar con plotly
            df,                      # Establecemos el nombre del dataframe
            x="continent",             # Establecemos el eje x
            y="pop",                   # Establecemos el eje y
            color="continent",          # Establecemos el color del punto proporcional al valor de la columna continent
            animation_frame="year",     # Establecemos el eje de animación
            animation_group="country",  # Establecemos el grupo de animación
            range_y=[0, 4000000000],    # Establecemos el rango del eje y
        ),
    }
    return animations[selection]  # Retornamos el gráfico seleccionado que es el gráfico que está en el diccionario en la llave que se reciba como parámetro en la variable selección.

# Run the app
if __name__ == '__main__':
    app.run(debug=True)
Dash app running on: