Plotly — Ingenuity

Mis à jour le 16/10/2023

Cet exemple de suivi des évolutions du petit hélicoptère Ingenuity sur Mars montre comment lire du contenu HTML avec Pandas et afficher des axes multiples avec Plotly.

Ingenuity est le petit hélicoptère qui accompagne le robot Perseverance sur Mars. La NASA propose une page dédiée aux exploits d'Ingenuity, avec en particulier son journal de vol :

On se propose ici de récupérer le tableau du journal de vol avec la fonction read_html de Pandas.

Puis d'afficher pour chaque vol, sur un même graphique, à la fois la distance horizontale parcourue et l'altitude maximale atteinte. Pour cela on utilisera les fonctionnalités d'affichage d'axes multiples de Plotly.

Code source

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import locale

# récupération de la table HTML
url = "https://mars.nasa.gov/technology/helicopter/#Flight-Log"

df_list = pd.read_html(
    url,
    attrs={"id": "flight-log-table"},
    header=1,
)

# dataframe
df = df_list[0]

# noms des colonnes
df.columns = [
    "Flight",
    "Sol",
    "Date",
    "Horizontal Distance m",
    "Horizontal Distance ft",
    "Max. Altitude m",
    "Max. Altitude ft",
    "Max. Groundspeed m/s",
    "Max. Groundspeed mph",
    "Duration seconds",
    "Route of Flight From",
    "Route of Flight To",
]

# traitement des dates
df.replace("Jan.", "January", regex=True, inplace=True)
df.replace("Feb.", "February", regex=True, inplace=True)
df.replace("Aug.", "August", regex=True, inplace=True)
df.replace("Sept.", "September", regex=True, inplace=True)
df.replace("Oct.", "October", regex=True, inplace=True)
df.replace("Nov.", "November", regex=True, inplace=True)
df.replace("Dec.", "December", regex=True, inplace=True)

locale.setlocale(locale.LC_TIME, "en_US.utf8")
df["Date"] = pd.to_datetime(df["Date"], format="%B %d, %Y")

# création du diagramme
fig = make_subplots(specs=[[{"secondary_y": True}]])

# axe y primaire : distance horizontale parcourue
fig.add_trace(
    go.Scatter(
        mode="lines+markers",
        x=df["Date"],
        y=df["Horizontal Distance m"],
        name="Horiz. dist. in m",
    ),
    secondary_y=False,
)

# axe y secondaire : altitude maximale atteinte
fig.add_trace(
    go.Scatter(
        mode="lines+markers",
        x=df["Date"],
        y=df["Max. Altitude m"],
        name="Max. alt. in m",
    ),
    secondary_y=True,
)

# centrage du titre, arrière plan, style du hover label, position de la légende
fig.update_layout(
    title_text="<b>Ingenuity - "
    + str(df.shape[0])
    + " flights</b><br>"
    + 'Data from <a href="https://mars.nasa.gov/technology/helicopter#Flight-Log">'
    + "https://mars.nasa.gov/technology/helicopter/</a><br>",
    title_x=0.5,
    hovermode="x unified",
    hoverlabel=dict(
        bgcolor="white",
        bordercolor="red",
        namelength=500,
    ),
    showlegend=True,
    legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
)

# modebar toujours visible
config = {"displayModeBar": True}

# légende axe x
fig.update_xaxes(
    title_text="<b>Date</b>",
    showgrid=False,
    linecolor="black",
)

# légende axe y primaire
fig.update_yaxes(
    title_text="<b>Horizontal distance in meters</b>",
    secondary_y=False,
    showgrid=False,
    rangemode="tozero",
    color="blue",
    linecolor="blue",
)

# légende axe y secondaire
fig.update_yaxes(
    title_text="<b>Maximum altitude in meters</b>",
    secondary_y=True,
    showgrid=False,
    rangemode="tozero",
    color="red",
    linecolor="red",
)

# affichage et sauvegarde
fig.show(config=config)
fig.write_html("plotly-ingenuity.html")

Diagramme généré