Plotly és una llibreria Opensource per generar gràfics interactius amb JavaScript.

Introducció

Fins ara, hem vist com crear gràfics de dades amb una imatge estàtica amb Python i llibreries com Matplotlib, o Seaborn a la sessió Estadística.

A continuació veurem la llibreria Plotly, que ens permet crear gràfics que canvien dinàmicament sense necessitat de carregar la pàgina web sencera.

També ens aporten més varietat de gràfics, entre els quals podem destacar mapes de territoris (a nivell de província, municipi, país, …).

L'usuari també pot interactuar amb aquests gràfics des del navegador, per obtenir informació addicional que seria més difícil presentar en una imatge fixe, i en general la experiència és més atractiva.

Hi ha dues formes de treballar amb Plotly.

La més clàssica és el projecte de Python Plotly Express. Aquesta encara és prou comú degut a que va ser la primera que es va crear, i també perquè el seu ús resulta més senzill per a perfils professionals que no tenen prou coneixements de creació de desenvolupament web.

En canvi, a nosaltres com a especialistes en desenvolupament web ens interessa Plotly Javascript.

Ens interessa delegar la creació del gràfic al client (al navegador, que usa Javascript), ja que pel servidor és molt costós dibuixar gràfics, el servidor ja té prou feina gestionant grans volums de dades! 😀`

React

TODO https://plotly.com/javascript/react/

Primer exemple.

Per importar plotly.js només necessites el següent link al CDN que ens proporciona Fastly:

<head>
    <script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
</head>

Dins del cos del teu document HTML crea un div buit per a dibuixar-hi el gràfic:

    <div class="container m-5">
        <div id="plot" />
    </div>

Ara ja pots crear el gràfic interactiu amb la funció Plotly.newPlot():

    <script>
        Plotly.newPlot(
        // id
        "plot", 
        // data
        [{
            x: [1999, 2000, 2001, 2002],
            y: [10, 15, 13, 17],
        }], 
        //layout
        {
            xaxis: {
                title: {
                    text: "Year"
                }
            },
            yaxis: {
                title: {
                    text: "Percent"
                }
            }
        });
    </script>
</body>

</html>

Aquest codi posa el gràfic creat mitjançant Plotly.newPlot() dins el div amb id=tester.

Podeu comprovar que quan passeu el ratolí per damunt podreu veure els valors numèrics, i aquesta interactivitat és la que marca la diferència.

1.-Prova el gràfic

S'ha de veure de la següent manera:

<html>
<head>
    <title>Plotly Demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href=" https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css">
    <script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
</head>
<body>
    <h2>Plotly Demo</h2>
    <div class="container m-5">
        <div id="plot" />
    </div>
    <script>
        Plotly.newPlot(
        // id
        "plot", 
        // data
        [{
            x: [1999, 2000, 2001, 2002],
            y: [10, 15, 13, 17],
        }], 
        //layout
        {
            xaxis: {
                title: {
                    text: "Year"
                }
            },
            yaxis: {
                title: {
                    text: "Percent"
                }
            }
        });
    </script>
</body>
</html>

Atributs dels gràfics

Els gràfics plotly.js es descriuen declarativament com a objectes JSON. Cada aspecte d'un gràfic de trama (els colors, les quadrícules, les dades, etc.) té un atribut JSON corresponent.

Els atributs dels gràfics de Plotly en les categories:

  1. id És l'id del contenidor d'html (normalment un div) on posar el gràfic.

  2. traces Són objectes que agafen les dades del gràfic: els valors dels eixos x, y i z; així com els marcadors. Normalment agafen llistes i trossos de diccionaris.

  3. layout Són els atributs visuals que apliquen a la resta del gràfic, com ara el títol, l'eix, les anotacions i la distribució.

  4. config Altres configuracions. Opcional.

Les traces es classifiquen per tipus de gràfic (per exemple, de barres, línies, dispersió, mapa de calor, mapes de cloropetes...)


Tipus de gràfics.

Com a altres llibreries, el tipus de gràfic predefinit és el de línies.

Gràfics de barres.

Si afegiu aquest script al body, es dibuixarà un gràfic de barres dins del div amb id='mydiv'.

    <script>
    var trace1 = {
        type: 'bar',
        x: [1, 2, 3, 4],
        y: [5, 10, 2, 8],
        marker: {
            color: '#C8A2C8',
            line: {
                width: 2.5
            }
        }
    };
    
    var data = [ trace1 ];
    
    var layout = {
        title: {
        text: 'Responsive to window\'s size!' },
        font: {size: 18}
    };
    
    var config = {responsive: true}
  
  Plotly.newPlot('myDiv', data, layout, config );

També pots crear gràfics de barres apilades. Aquí tens une exmple amb les dones i homes d'un estudi de l'Alzeimer que es realitzarà en diversos hospitals.

<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Plotly - Stacked bar charts.</title>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
    <div id="div2" style="width: 100%; height: 600px;"></div>
    <h3>Plotly - Stacked bar charts.</h3>
    <script>

    var trace1 = {
        type: 'bar',
        x: ['Bellvitge', 'Clinic', 'Vall Hebron', 'Del Mar'],
        y: [7, 10, 8, 12],
        name: 'Homes'
    };
    var trace2 = {
        type: 'bar',
        x: ['Bellvitge', 'Clinic', 'Vall Hebron', 'Del Mar'],
        y: [8, 11, 6, 9],
        name: 'Dones'
    };
    
    var data = [ trace1, trace2 ];
    
    var layout = {
        title: {
        text: 'Pacients estudi Alzeimer' },
        font: {size: 20}
    };
    var config = {responsive: true}
    Plotly.newPlot('div2', data, layout, config );

</script>
</body>
</html>

Plotbox.

Per ara no són massa impressionants els exemples, però aquest segur que et sorprendrà, sobretot després de repassar a estadística Estadística els conceptes fonamentals i com obtenir-los en Pyhon.

Un dels gràfics més útils en estadística són els plotbox o diagrames de caixes i cues. Són tant útils com les corbes gausianes.

Ens permeten veure a un cop d'ull mesures centrals i de dispersió d'una mostra.

Creem un gràfic a partir d'aquest codi:

var trace1 = {
  x: [3, 4, 4, 4, 5, 6, 8, 8, 9, 10],
  type: 'box',
  name: 'Set 1'
};

var data = [trace1];

var layout = {
  title: {
    text: 'Horizontal Box Plot'
  },
};

Plotly.newPlot('myDiv', data, layout);

Fixa't 👓 tota la informació estadística que surt et poses sobre de la caixa 😉


Mapa de cloropetes.

Plotly també destaca per ser una llibreria que et permet crear gràfics amb mapes fàcilment.

Comencem en un exemple senzill.

Imaginem-nos que tenim les taxes d'atur 5 països d'Europa de l'agost del 2024.

    const countries = ['Spain', 'France', 'Germany', 'Portugal', 'Italy'];
    const unemp_rate = [11.5, 7.5, 3.5, 6.4, 6.2];

Doncs per dibuixar el codi és tant senzill com organitzar les dades al mapa de la següent manera.

  • locationmode Permet 'country names' (noms de països).
  • locations Llista de noms de països vàlids.
  • z La informació numèrica de cada païs (taxa atur en el nostre cas)
  • text El text de la llegenda quan ens posem a sobre de cada país.
        const data = [{
            type: 'choropleth',
            locationmode: 'country names',
            locations: countries,
            z: unemp_rate,
            text: countries,
            autocolorscale: true
        }];

        const layout = {
            title: { text: 'Taxa atur (agost 2024)' },
            geo: {
                scope: 'europe', 
                projection: { type: 'robinson' }
            }
        };

        Plotly.newPlot("myDiv", data, layout, { showLink: false });

⚠ Limitacions ⚠ Aquesta nomenclatura només funciona per a països o per estats d'USA. Almenys, fins a on hem esbrinat.

3.- Afegeix 3 països més al gràfic i prova'l.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Alcohol Consumption Map</title>
    <script src="https://cdn.plot.ly/plotly-2.25.2.min.js"></script>
</head>
<body>
    <div id="myDiv" style="width: 100%; height: 600px;"></div>
    <script>
        const countries = ['Spain', 'France', 'Germany', 'Portugal', 'Italy'];
        const unemp_rate = [11.5, 7.5, 3.5, 6.4, 6.2];

        const data = [{
            type: 'choropleth',
            locationmode: 'country names',
            locations: countries,
            z: unemp_rate,
            text: countries,
            autocolorscale: true
        }];

        const layout = {
            title: { text: 'Taxa atur (agost 2024)' },
            geo: {
                scope: 'europe', 
                projection: { type: 'robinson' }
            }
        };

        Plotly.newPlot("myDiv", data, layout, { showLink: false });
    </script>
</body>
</html>

Mapes de bombolles.

Per solventar les limitacions del cloropetes, tenim molts altres mapes, que us animem a investigar:

https://plotly.com/javascript/maps/

En molts casos, només necessitareu la latitud i longitud.

TODO

Gràfics animats.

TODO.


Exemple integrat amb FastAPI

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import JSONResponse, FileResponse
from fastapi.middleware.cors import CORSMiddleware
# import polars as pl 
# amb polars no he vist com va.
import pandas as pd
import os

app = FastAPI()

app.add_middleware(CORSMiddleware,
    allow_origins=["http://localhost:8000"],
    allow_credentials=True,
    allow_methods=["GET"],
    allow_headers=["Get-Contents"],
)

# Serveix els arxius estàtics des de la carpeta "static"
app.mount("/static", StaticFiles(directory="app/static"), name="static")

# Serveix el fitxer "index.html" a la ruta principal "/"
@app.get("/")
def serve_html():
    return FileResponse("app/static/index.html")

@app.get("/data")
def get_chart_data():
    df = pd.DataFrame({
        "Categoria": ["A", "B", "C", "D"],
        "Valors": [10, 20, 15, 25]
    })
    data = df.to_dict(orient="records")
    return JSONResponse(content=data)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gràfica amb Plotly.js</title>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
    <h1>Gràfica Dinàmica amb Plotly.js</h1>
    <div id="grafica" style="width: 60%; height: 100%"></div>
    <script>
        // Obté dades del backend
        fetch("http://127.0.0.1:8000/data")
            .then(response => response.json())
            .then(data => {
                // Construeix la gràfica
                const categories = data.map(item => item.Categoria);
                const values = data.map(item => item.Valors);

                const dades = [{
                    x: categories,
                    y: values,
                    type: 'bar'
                }];

                Plotly.newPlot('grafica', dades);
            })
            .catch(error => console.error('Error carregant dades:', error));
    </script>
</body>
</html>

TODO

  • Traspàs de mapes.

Google Docs - Plotly