Salida¶


../../_images/2_cover.png

Fig. 35 Ilustración realizada por Bernardo Dinamarca.¶

¬°Hola! Este notebook estar√° basado en recopilar, a trav√©s de la Visualizaci√≥n de Datos, las cifras hist√≥ricas y actuales de la pandemia en Tarapac√°. A√ļn no he indagado c√≥mo hacer que se actualice de forma autom√°tica, pero eventualmente, ¬°lo lograremos!

Importando paquetes¶

Trabajaremos con las librerías del primer notebook y otras. Respecto a las librerías para visualización, no utilizaremos Matplotlib o Seaborn, dado que deseo realizar gráficas interactivas que sean visualmente limpias y atractivas. Además, incorporaremos al equipo Beautiful Soup, que es una librería para manipular lenguaje HTML.

Todo ésto, será en fin de generar dos salidas:

  • Un reporte para el libro.

  • Una p√°gina externa para incrustarse en el sitio de la Universidad Arturo Prat.

Anteriormente, trabajaba con Infogram, la cual es la misma plataforma que utiliza el Gobierno de Chile en su página de Cifras Oficiales. Sin embargo, la plataforma carece de automatización gratuita, y se debe pagar para lograr esa automatización.

En ese sentido, exportaremos una p√°gina HTML, para poder incrustarse en el sitio de la Universidad Arturo Prat.

Respecto a nuestro equipo de librerías habitual, sumamos:

  • Plot.ly (librer√≠a de visualizaci√≥n din√°mica e interactiva a partir de JavaScript).

https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Plotly-logo-01-square.png/1200px-Plotly-logo-01-square.png

Fig. 36 Logo de la librería Plot.ly.¶

  • Beautiful Soup (librer√≠a de Python para extraer datos de archivos HTML y XML).

https://funthon.files.wordpress.com/2017/05/bs.png?w=1024

Fig. 37 Logo de la librería Beautiful Soup.¶

# Importando paquetes

### Librería de manipulación de datos 
import pandas as pd
pd.set_option('use_inf_as_na', True)
pd.options.display.float_format = '{:,.2f}'.format

### Librería de álgebra
import numpy as np

### Librerías para graficar
import plotly.graph_objects as go

### Customizamos opciones de Plot.ly
config = {'displayModeBar': False, 'showTips': False, 'scrollZoom': False}
layout = go.Layout(dragmode=False, font=dict(color='white'),
    xaxis=dict(
        showline=True,
        showgrid=True,
        showticklabels=True,
        linecolor='rgb(204, 204, 204)',
        linewidth=2,
        ticks='outside',
        tickfont=dict(
            family='Arial',
            size=12,
            color='rgb(82, 82, 82)',
        ),
    ),
    yaxis=dict(
        showgrid=True,
        showticklabels=True,
    ),
    autosize=True,
    margin=dict(
        autoexpand=True,
        l=0,
        r=0,
        t=0,
    ),
    showlegend=True,
    paper_bgcolor='rgba(0,0,0,0)',
    plot_bgcolor='rgba(0,0,0,0)',
    legend=dict(
        itemclick="toggleothers",
        itemdoubleclick="toggle",
        orientation="h",
        yanchor="bottom",
        xanchor='right',
        x=1,
        y=1.02
    )
)

### Librería BeautifulSoup para manipular HTML
from bs4 import BeautifulSoup

### Para formato local
import locale
### Seg√ļn Windows o Ubuntu
try:
    ### Windows
    locale.setlocale(locale.LC_ALL, 'esp')
except Exception:
    ### Ubuntu (action)
    locale.setlocale(locale.LC_ALL, 'es_CL.UTF-8')

### Otros paquetes
import math
import os, os.path
import json as json
import datetime
import time
from IPython.display import display, Markdown, HTML, Javascript, IFrame

### Gracias a joelostblom (https://gitlab.com/joelostblom/session_info)
import session_info

Importando datos¶

Hacemos el mismo proceso habitual, con la diferencia que importaremos datos ya procesados.

### Gracias a Daniel Stutzbach y Bruno Bronosky (stackoverflow.com/a/2632251/13746427) ###
sum_ = 0
for string in [name for name in os.listdir('../../out/site/csv/')]:
    sum_ += string.count('data')

## Cargamos cada uno de los csv (basado en el primer notebook)
x = range(1, sum_+1)
data = []
for i in x:
    exec('data += [pd.read_csv("../../out/site/csv/data{}.csv", parse_dates=["Fecha"], index_col=["Fecha"])]'.format(i, i))

Tendencia¶

La diferencia de nuestras publicaciones (en contraste a la forma en que se entregaba información en otras páginas informativas en Instagram) se basaban en la visualización de datos, y en particular, en las tendencias semanales. También, se agregaban cálculos o datos que, en los reportes del Minsal, no se encontraban procesados.

Imaginemos que, lo √ļnico que hici√©ramos fuese entregar cifras aisladas. ¬ŅQu√© retratan? ¬ŅDe d√≥nde venimos? No es correcto que, por un d√≠a, debamos ser positivistas o negativistas. De hecho, esos pensamientos deparan en sesgos, y lo peor, en sesgos que no contemplan la integralidad de la pandemia. Por eso es relevante formar una mirada hol√≠stica en la visualizaci√≥n de los datos. Las cifras de un d√≠a \(X\) pueden ser sumamente variables al d√≠a \(X+1\) en funci√≥n de depender de la muestra (cantidad de ex√°menes PCR informados en el d√≠a \(X\) o \(X+1\)). Por ello, es importante regirse por las tendencias, o por los estad√≠sticos.

El problema es que, al ser Instagram, una plataforma homog√©nea, el storytelling debe ser accesible para todo p√ļblico (con estudios matem√°ticos o no). De esa forma, los reportes ten√≠an una alta presencia de tendencias a trav√©s de gr√°ficas, y no as√≠ de estad√≠sticos (a excepci√≥n de medias m√≥viles semanales).

La comprensi√≥n de la pandemia deb√≠a partir desde el mejor storytelling (el contar una historia detr√°s de los datos). El tomar una idea, o un incidente, y contarla como una historia: Cada d√≠a particular de la pandemia es una hoja de esa historia (una hoja del ‚Äúlibro‚ÄĚ COVID-19 en Tarapac√°). Por esa raz√≥n, desarrollamos el reporte diario no solo con datos duros, sino tambi√©n con gr√°ficas de tendencia semanal.

Cifras significativas¶

Las tablas y gráficos visualizados en la presente sección tienen una a dos cifras significativas. Cualquier sugerencia es bienvenida.

Para los datos, descargar los archivos .CSV procesados. √Čstos est√°n disponibles en el propio libro (secci√≥n Legado ūüĒÄ), o bien, en el repositorio.

¬ŅCu√°ntos gr√°ficos se exportar√°n?¬∂

display(Markdown('> Se exportar√°n un total de **{} gr√°ficos**.'.format(sum_)))

Se exportar√°n un total de 27 gr√°ficos.

Automatizando salida de gráficos¶

A continuaci√≥n, se desarrolla un c√≥digo que recorre cada uno de los archivos .CSV que generamos en el primer notebook, del cual, realiza una ‚Äúespecie‚ÄĚ de resumen de los estad√≠sticos, visualiza los datos en un gr√°fico en Plot.ly y culmina con informaci√≥n adicional sobre:

  • Descarga de los datos (solo funciona al presionar desde el libro publicado, no desde el notebook).

  • La fecha de inicio y fin del gr√°fico.

%%capture reportediario
x = 0
### Título y otras cosas
display(Markdown('<h2 style="font-size:60px;">REPORTE DIARIO</h2>'))
display(Markdown('<h3 style="font-size:20px;">Región de Tarapacá, {}</h3>'.format(data[0].last_valid_index().strftime('%d de %B de %Y'))))

### Recorremos el vector que almacena los DataFrames, uno a uno
for dataframe in data:
    
    ### Para guardar el n√ļmero del gr√°fico (un poco ordinario el m√©todo, lo s√©)
    
    x += 1
    
    ### Definimos una nueva figura
    
    fig = go.Figure(layout=layout)
    
    ### Algunos datos y título
    
    display(Markdown('<h3> Gr√°fico {}</h3>'.format(x)))
    display(Markdown('El gr√°fico contiene las siguientes <b>columnas</b>: '))
    
    ### Recorremos cada una de las columnas del DataFrame anterior
    
    for col in dataframe.columns:
        
        ### Vector de fechas desde primer y √ļltimo dato v√°lido por cada columna
        
        index = dataframe[col].first_valid_index()
        index_ = dataframe[col].last_valid_index()
            
        ### DataFrame seg√ļn filtro de primer dato v√°lido
        
        _df = dataframe[index:]
            
        ### √ćndice de DataFrame seg√ļn filtro anterior
            
        fecha = dataframe[index:].index
    
        ### Columna específica
        
        _col = dataframe[index:][col]
        
        ### A√Īadimos un trazado por cada columna, conectamos los valores para no tener discontinuidad y
        ### suavizamos por interpolación spline
        
        fig.add_trace(go.Scatter(x=_df.index.strftime('%d %b %Y'),
                                 y=_col,
                    mode='lines',
                    name=col,
                    connectgaps=True,
                    line_shape='linear',
                    hovertemplate =
                    '<b>{}</b>: '.format(col) + '%{y:.2f}'+'<br><b>Fecha</b>: %{x}<br>' + "<extra></extra>"))
        
        ### Para colocar en 35¬į las etiquetas del eje X, con el n√ļmero de etiquetas proporcional al n√ļmero de meses
        ### desde el primer dato v√°lido
        
        fig.update_layout(xaxis = go.layout.XAxis(tickangle = 90,
                                                  nticks=len(_df.index.month.unique())))
        ### M√°s datos
        
        display(Markdown(' - <b>{}</b>.'.format(col)))
        display(Markdown("""El mayor valor es de <b>{}</b>, registrado el <b>{}</b>. 
        Asimismo, la mediana es de <b>{}</b>.
        Respecto a la dispersión de los datos, la desviación estándar es del <b>{}</b>. """
                         .format(dataframe[col].max(), dataframe[dataframe[col] == dataframe[col].max()].index[0].strftime('%d de %B de %Y'),
                                 round(dataframe[col].median(), 2), round(dataframe[col].std(), 2))))
        display(Markdown('> El valor en base al √ļltimo reporte diario o epidemiol√≥gico ({}) es de <b>{}</b>.'.format(dataframe[index_:].index[0].strftime('%d de %B de %Y'), dataframe[col][index_])))
    
    ### Mostramos la figura procesada en el ciclo anterior y otros datos. A√Īadimos espaciado
    display(Markdown('<h4>Visualización del gráfico {}</h4> <br> El gráfico, visualizado en <a href="https://plotly.com/python/">Plot.ly</a>: <br>'.format(x)))
    fig.show(config=config)
    
    display(Markdown("""> <b>Notas</b>: 
    <br> - El gr√°fico <b>inicia en el {}</b> y <b>termina el {}</b> en base a los datos disponibles.
    <br> - Para aislar una curva, presionar en el nombre o color en la leyenda. 
    <br> - Para remover una curva, seguir instrucción anterior, con la diferencia de presionar dos veces.""".format(\
                    _df.index[0].strftime('%d de %B de %Y'),
                    dataframe[index_:].index[0].strftime('%d de %B de %Y'))))
    display(Markdown('<h4>Información adicional sobre el gráfico {}</h4> <br>'.format(x)))
    display(Markdown(
    """El <b>gráfico {}</b> utilizó los datos procesados en <a href="https://raw.githubusercontent.com/pandemiaventana/pandemiaventana/main/out/site/csv/data{}.csv">data{}.csv</a>.
    La tabla de datos resumida:""".format(x, x, x, x)))
    display(dataframe)

Automatizando salida para asistenciacovid19¶

En razón de brindar una página web que se pueda incrustar en el sitio de la Universidad Arturo Prat, como también en el presente sitio, generamos las siguientes líneas de codigo.

¬ŅD√≥nde estar√° la salida?¬∂

La salida estar√° disponible en ‚ÄúBalance hist√≥rico ūüďä‚ÄĚ.

¬ŅC√≥mo funciona?¬∂

  • La salida completa de la celda anterior es capturada como lenguaje HTML, pero solo el cuerpo de la p√°gina [Ben20].

En este sentido, dar gracias al usuario Benvida, de Stackoverflow 1. El código original que desarrolló se encuentra a continuación, el cual modificaremos.

%%js
{
    let outputs=[...document.querySelectorAll(".cell")].map(
        cell=> {
            let output=cell.querySelector(".output_text")
            if(output) return output.innerText
            output=cell.querySelector(".rendered_html")
            if(output) return output.innerHTML
            return ""
        }
    )
    
    IPython.notebook.kernel.execute("cell_outputs="+JSON.stringify(outputs))    
}

Inconveniente¶

En primer lugar, el lenguaje de marcado (HTML) solo brinda la estructura b√°sica del sitio, que se compone de texto, im√°genes y scripts. Para que funcionen los scripts, en este caso, Plot.ly, se tienen m√ļltiples dependencias de otras librer√≠as de JavaScript.

JavaScript es un lenguaje de programaci√≥n de alto nivel, el cual, tal como Python, tiene m√ļltiples librer√≠as que se especializan en distintas tareas y funciones. Plot.ly depende de otras librer√≠as para funcionar, por lo que, para que el script de JavaScript, realizado por los creadores de Plot.ly, funcione, debemos no solo incorporar el c√≥digo de los gr√°ficos, sino tambi√©n sus dependencias.

Para ello, incorporamos en el <head> las CDN. Estos son servidores, a partir de los cuales descargamos los archivos que tienen las dependencias.

Cabe recalcar que, además de código JavaScript, también incorporamos lenguaje de hojas de estilo en cascada (CSS), el cual le da formato y estilo al solitario HTML.

En analogía, y para simple comprensión, HTML es el esqueleto (estructura), JS es la musculación (animaciones y movimiento) y CSS es la apariencia externa (piel y atributos físicos).

Algunos arreglos¶

  • La funci√≥n por BenVida es ejecutada una vez se termin√≥ de ejecutar todas las celdas, y por ende, los outputs no son recopilados hasta que el Kernel culmina su ejecuci√≥n.


html = open('balance.html','w')
html.write('''<html><head><link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'><script src='https://cdn.plot.ly/plotly-latest.min.js'></script><script src='https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js'></script></head><body>''' +cell_outputs[9] +'''</body></html>''')
html.close()

Aqu√≠ se nos genera el siguiente problema: La variable cell_outputs no es asignada hasta que el Kernel termina su ejecuci√≥n, por lo que si des√©aramos actualizar el archivo HTML, en base a la celda anterior de c√≥digo (donde est√°n los gr√°ficos Plot.ly), no podr√≠amos, porque la variable cell_outputs todav√≠a no est√° asignada. ¬ŅLa soluci√≥n? Ejecutar el c√≥digo Python tras la ejecuci√≥n del c√≥digo JavaScript, y de esta forma, no tenemos inconvenientes. Para ello, utilizaremos la misma funci√≥n que utiliz√≥ BenVida, IPython.notebook.kernel.execute("") donde en " " debe ir el c√≥digo Python.

Por otro lado, al ser código JavaScript con Python, ¡se debe tener cuidado al utilizar comillas sobre doble comillas o viceversa! De otra forma, tendremos un error. Por esta razón, utilizamos la función format() de Python (ya que las URL deben ir entre comillas, le decimos a Python que se encargue de esa situación, sin que el input de JavaScript nos resulte en error).

Detalles¶

El sitio se encuentra en el directorio ./out/site. Respecto al funcionamiento del código para procesarlo:

  • Modificamos al funci√≥n para no solamente obtener el texto, sino que todo el HTML.

  • Para no desperdiciar los estilos CSS que utiliza Jupyter Notebooks, copi√© los mismos archivos CSS en la carpeta del sitio (ipython.min.css y style.min.css). Los archivos se pueden encontrar en el directorio de Anaconda, usuario/anaconda3/Lib/site-packages/notebook/static/style.

  • Adem√°s, en el mismo directorio se encuentra el archivo JavaScript, plotly.js, que debe estar en el directorio para que funcionen los gr√°ficos interactivos.

  • Se remueven los <div> con clase prompt, que es lo que brinda el margen a la izquierda en Jupyter Notebooks.

    IPython.notebook.kernel.execute("removals = soup.find_all(attrs={'class': 'prompt'})")
    IPython.notebook.kernel.execute("for removal in removals: removal.decompose()")
    IPython.notebook.kernel.execute("soup = str(soup)")

Nuevo inconveniente¶

Al automatizar el c√≥digo en GitHub, logr√© apreciar que el c√≥digo Python a trav√©s de JavaScript no es ejecutado. √Čsto produce que los archivos no se actualicen.

Por ello, tuve que aproximar otra solución.

Con el comando mágico de Jupyter Notebook, %%capture, capturamos la salida de una celda específica.

En este caso puntual, almacenamos la salida en la variable reportediario con el comando mágico %%capture. Esa salida debemos convertirla a texto, o string en inglés, el problema es que, al ser un output de una celda, posee algunos textos que no deseamos:

  • El tipo de variable que se est√° imprimiendo.

  • Configuraci√≥n de Plot.ly en diccionario.

  • Datos de tabla sin formato.

En las siguientes líneas de código:

  • Limpieza de textos.

  • Damos formato de documento HTML.

  • A√Īadimos Bootstrap 2 para que el documento HTML no quede plano, a√Īadiendo c√≥digo desde sus Docs [Boo20].

  • Entre otros.

outputs_ = reportediario.outputs

vec_out = []
for outs_ in outputs_:
    ### Convertimos a lista los outputs
    vec_out += list(outs_.data.values())

### Nuestro string
vec_ = ''
### Recorremos la lista de outputs
for vec in vec_out:
    vec = str(vec)
    ### Quitamos configuración de Plot.ly
    if vec.startswith("{'config':"):
        pass
    else:
        ### Quitamos el str de datos de tabla sin formato
        if vec.startswith("            "):
            pass
        else:
            ### Quitamos el str del tipo de variable
            vec = vec.replace('<IPython.core.display.Markdown object>', '')
            ### Damos formato a tablas
            if vec.startswith('<div>\n<style scoped>'):
                vec = vec.replace('NaN', 'Sin datos')
                vec = BeautifulSoup(vec, 'html.parser').find('table')
                vec['class'] = vec.get('class', []) + [' table table-dark table-striped table-hover table-sm']
                vec = '<div class="table-responsive">' + str(vec) + '</div>'
            ### Finalmente, a√Īadimos al vector
            vec_ += '<div class="row"><br><div class="col text-light">' + vec + '</div></div>'

### Abrimos y modificamos el HTML
with open('../../_build/html/dinamic/balance.html', 'w', encoding='UTF-8') as f:
    f.write('''<html>
    <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous">
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous">
    </script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous">
    </script>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://cdn.plot.ly/plotly-latest.min.js">
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js">
    </script>
    <link rel="icon" href="./favicon.ico" type="image/x-icon"/>
    <meta charset="UTF-8">
    <title>Balance hist√≥rico ūüďä &#8212; La pandemia por la ventana</title>
    </head>
    <body>
    <nav class="navbar nnavbar-expand-lg navbar-dark bg-dark">
      <a class="navbar-brand" href="https://pandemiaventana.github.io/pandemiaventana/">
    <img src="./logo.png" width="35" height="35" class="d-inline-block align-top" alt="">
    Numeral.lab
      </a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
    <div class="navbar-nav">
      <a class="nav-item nav-link active" href="#">Reporte diario <span class="sr-only">(current)</span></a>
      <a class="nav-item nav-link" href="https://pandemiaventana.github.io/pandemiaventana/dinamic/indicadorfase.html">Indicador de fase</a>
      <a class="nav-item nav-link" href="https://pandemiaventana.github.io/pandemiaventana/">La pandemia por la ventana</a>
    </div>
    </div>
    </nav>
    <div class="container-fluid bg-dark text-light">''' 
    + vec_ + 
    '''</div>
    </body>
    </html>''')

Información de sesión¶

session_info.show(cpu=True, jupyter=True, std_lib=True, write_req_file=True, dependencies=True, req_file_name='3_requeriments.txt')
Click to view session information
            -----
            bs4                 4.10.0
            datetime            NA
            json                2.0.9
            locale              NA
            math                NA
            numpy               1.19.5
            os                  NA
            pandas              1.2.3
            plotly              4.8.1
            session_info        1.0.0
            time                NA
            -----
            
Click to view modules imported as dependencies
            PIL                         8.2.0
            abc                         NA
            argparse                    1.1
            array                       NA
            ast                         NA
            asyncio                     NA
            atexit                      NA
            attr                        20.3.0
            backcall                    0.2.0
            base64                      NA
            bdb                         NA
            binascii                    NA
            bisect                      NA
            bz2                         NA
            cProfile                    NA
            calendar                    NA
            chardet                     4.0.0
            cmath                       NA
            cmd                         NA
            code                        NA
            codecs                      NA
            codeop                      NA
            collections                 NA
            colorama                    0.4.4
            colorsys                    NA
            concurrent                  NA
            configparser                NA
            contextlib                  NA
            contextvars                 NA
            copy                        NA
            copyreg                     NA
            csv                         1.0
            ctypes                      1.1.0
            curses                      NA
            cython_runtime              NA
            dataclasses                 NA
            dateutil                    2.8.2
            debugpy                     1.4.3
            decimal                     1.70
            decorator                   5.1.0
            difflib                     NA
            dis                         NA
            distutils                   3.7.12
            email                       NA
            encodings                   NA
            entrypoints                 0.3
            enum                        NA
            errno                       NA
            faulthandler                NA
            fcntl                       NA
            filecmp                     NA
            fnmatch                     NA
            functools                   NA
            gc                          NA
            genericpath                 NA
            getopt                      NA
            getpass                     NA
            gettext                     NA
            glob                        NA
            grp                         NA
            gzip                        NA
            hashlib                     NA
            heapq                       NA
            hmac                        NA
            html                        NA
            http                        NA
            idna                        2.10
            imp                         NA
            importlib                   NA
            importlib_metadata          NA
            inspect                     NA
            io                          NA
            ipykernel                   6.4.1
            ipython_genutils            0.2.0
            itertools                   NA
            jedi                        0.18.0
            jsonschema                  3.2.0
            keyword                     NA
            linecache                   NA
            logging                     0.5.1.2
            lzma                        NA
            marshal                     4
            mimetypes                   NA
            mmap                        NA
            mpl_toolkits                NA
            multiprocessing             NA
            nbformat                    5.1.3
            ntpath                      NA
            numbers                     NA
            opcode                      NA
            operator                    NA
            parso                       0.8.2
            pathlib                     NA
            pdb                         NA
            pexpect                     4.8.0
            pickle                      NA
            pickleshare                 0.7.5
            pkg_resources               NA
            pkgutil                     NA
            platform                    1.0.8
            plistlib                    NA
            posix                       NA
            posixpath                   NA
            pprint                      NA
            profile                     NA
            prompt_toolkit              3.0.20
            pstats                      NA
            pty                         NA
            ptyprocess                  0.7.0
            pvectorc                    NA
            pwd                         NA
            pydev_ipython               NA
            pydevconsole                NA
            pydevd                      2.4.1
            pydevd_concurrency_analyser NA
            pydevd_file_utils           NA
            pydevd_plugins              NA
            pydevd_tracing              NA
            pydoc                       NA
            pydoc_data                  NA
            pyexpat                     NA
            pygments                    2.10.0
            pyrsistent                  NA
            pytz                        2021.1
            queue                       NA
            quopri                      NA
            random                      NA
            re                          2.2.1
            reprlib                     NA
            resource                    NA
            retrying                    NA
            runpy                       NA
            secrets                     NA
            select                      NA
            selectors                   NA
            shlex                       NA
            shutil                      NA
            signal                      NA
            site                        NA
            six                         1.16.0
            socket                      NA
            socketserver                0.4
            soupsieve                   2.2.1
            sphinxcontrib               NA
            sqlite3                     2.6.0
            sre_compile                 NA
            sre_constants               NA
            sre_parse                   NA
            ssl                         NA
            stat                        NA
            storemagic                  NA
            string                      NA
            stringprep                  NA
            struct                      NA
            subprocess                  NA
            sys                         3.7.12 (default, Sep  6 2021, 07:19:30) 
[GCC 9.3.0]
            sysconfig                   NA
            tarfile                     0.9.0
            tempfile                    NA
            termios                     NA
            textwrap                    NA
            threading                   NA
            timeit                      NA
            token                       NA
            tokenize                    NA
            tornado                     6.1
            traceback                   NA
            traitlets                   5.1.0
            tty                         NA
            types                       NA
            typing                      NA
            typing_extensions           NA
            unicodedata                 NA
            urllib                      NA
            uu                          NA
            uuid                        NA
            warnings                    NA
            wcwidth                     0.2.5
            weakref                     NA
            webbrowser                  NA
            xml                         NA
            xmlrpc                      NA
            zipfile                     NA
            zipimport                   NA
            zipp                        NA
            zlib                        1.0
            zmq                         22.3.0
            
            -----
            IPython             7.27.0
            jupyter_client      7.0.3
            jupyter_core        4.7.1
            notebook            6.4.4
            -----
            Python 3.7.12 (default, Sep  6 2021, 07:19:30) [GCC 9.3.0]
            Linux-5.8.0-1041-azure-x86_64-with-debian-bullseye-sid
            2 logical CPU cores, x86_64
            -----
            Session information updated at 2021-09-16 23:46
            

Bibliografía de esta página¶

1

BenVida. Jupyter magic to handle notebook exceptions. 2020. URL: https://stackoverflow.com/a/64495269/13746427.

2

Bootstrap. Get started with Bootstrap. 2020. URL: https://getbootstrap.com/docs/4.3/getting-started/introduction/.