Search code examples
pythonplotlyplotly-express

How can you label the default ticks in Plotly?


I want to display the x-axis values in my plotly express chart in a standard format, as opposed to having raw labels; while preserving the tick layouts.


Basically, I like the fact there's only a few 'main' ticks and not a tick for every value, but I need to change the text on the labels.

The labels currently are integer values representing milliseconds, I'd like to change these to min:sec:ms, as shown in the image below. Note, the 'Time' column in 'df' represents the milliseconds in the correct format.

I have been trying to figure this out using the 'Array' tickmode and sorting, but this either sorts the chart (which I don't want), or the labels generated are unevenly spaced and aren't rounded to the nearest second.

Current Progress & Code Sample

Here is my original code before I messed it up!

import pandas as pd
import numpy as np
import plotly.express as px
from _plotly_utils.colors import n_colors
from scipy import stats
import streamlit as st


def lap_times_graph(year, d_ids, laps_df, races_df, col=st):
    for d_id in d_ids:
        times = {"x": [], "y": [], "Name": [], "Lap": [], "Time": []}

        # Obtains each race the selected driver was in for the selected year
        # races_df = races_df.loc[races_df["year"] == year, ["race_id", "name"]]
        races_df = races_df[races_df["year"] == year]
        for index, r in races_df.iterrows():
            df_laps = laps_df.loc[
                (laps_df["race_id"] == r["race_id"]) &
                (laps_df["driver_id"] == d_id),
                ["lap", "time", "milliseconds"]
            ]
            df_laps2 = df_laps.sort_values(by="milliseconds")

            for ndx, r1 in df_laps.iterrows():
                times["x"].append(r1["milliseconds"])
                times["y"].append(r["race_id"])
                times["Name"].append(r["name"])
                times["Lap"].append(r1["lap"])
                times["Time"].append(r1["time"])

        df = pd.DataFrame(data=times)

        # Remove outliers
        z = np.abs(stats.zscore(df['x']))
        threshold = 3
        outliers = df[z > threshold]
        df = df.drop(outliers.index)

        fig = px.strip(df, x="x", y="y",
                       title="""Lap Times<br><sub class='subtitle'>"""
                       """Hover over points for more details</sub>""",
                       labels={"x": "Lap time", "y": "Race"},
                       color_discrete_sequence=n_colors(
                           "rgb(255, 75, 75)",
                           "rgb(255,255,255)",
                           3,
                           colortype="rgb"
                       ),
                       color="y",
                       hover_name="Name", hover_data={
                            "x": False,
                            "y": False,
                            "Lap": True, 
                            "Time": True
                        })

        fig.update_layout(
            showlegend=False,
            yaxis=dict(
                side="right",
                showticklabels=False),
            xaxis=dict(
                tickangle=-45)
        )

        col.plotly_chart(fig, use_container_width=False)

Sample Test Run Code

def run():
    laps_data = {
        "race_id": [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
        "driver_id": [101, 101, 101, 101, 101, 102, 102, 102, 102, 102],
        "lap": [1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
        "time": ['1:40.123', '1:38.456',
                 '1:37.789', '1:36.123',
                 '1:35.456', '1:41.789',
                 '1:40.456', '1:39.789',
                 '1:38.123', '1:37.456'],
        "milliseconds": [100123, 98456, 97789, 96123, 95456,
                         101789, 100456, 99789, 98123, 97456]
    }
    races_data = {
        "race_id": [1, 2],
        "name": ['Australian Grand Prix', 'Monaco Grand Prix'],
        "year": [2023, 2023]
    }
    laps_df = pd.DataFrame(laps_data)
    races_df = pd.DataFrame(races_data)
    year = 2023
    driver_ids = [101, 102]

    lap_times_graph(year, driver_ids, laps_df, races_df)

Is there any way to change the labels on the default ticks so I don't have to calculate a way to remake them? df["Time"] Holds values with the correct format I'm looking for.

Expected Output

An example (the x-axis labels) of what I'm looking to achieve (changes in yellow will replace the ticks already there):

enter image description here

Thanks.


Solution

  • UPDATE:

    This has been solved by @r-beginners in the comments of my post.

    Their solution was the following:

    fig.update_xaxes(type='date',tickformat='%M:%S.%f')