Marker_color does not change for entire dataframe

Greetings

This is a question that resulted from my previous post (Hiding two graphs when legend clicked on - #5 by RenaudLN), which was of great help but also gave me more questions which I cannot explain.

Below, is an excerpt of a dataframe I have for formula1 data, where each driver has laps of 1- 56, and the sequence of tyres are used differently.

              Driver  Lap  Position                   Time  Year                Race        Tyre   Constructor
0     Max Verstappen    1         1 0 days 00:01:42.632000  2021  Bahrain Grand Prix  MEDIUM C3u  Red Bull-VER
1     Max Verstappen    2         1 0 days 00:01:42.632000  2021  Bahrain Grand Prix  MEDIUM C3u  Red Bull-VER
2     Max Verstappen    3         1 0 days 00:01:42.632000  2021  Bahrain Grand Prix  MEDIUM C3u  Red Bull-VER
3     Max Verstappen    4         1 0 days 00:01:42.632000  2021  Bahrain Grand Prix  MEDIUM C3u  Red Bull-VER
...
56   Charles Leclerc    1         3 0 days 00:01:43.873000  2021  Bahrain Grand Prix    SOFT C4u   Ferrari-LEC
57   Charles Leclerc    2         3 0 days 00:01:43.873000  2021  Bahrain Grand Prix    SOFT C4u   Ferrari-LEC
58   Charles Leclerc    3         3 0 days 00:01:43.873000  2021  Bahrain Grand Prix    SOFT C4u   Ferrari-LEC
59   Charles Leclerc    4         3 0 days 00:01:43.873000  2021  Bahrain Grand Prix    SOFT C4u   Ferrari-LEC

Below, is my code to generate a line graph for their race times and using markers, indicate what tyres they used.

#dict to assign marker symbols and colours to tyres used. For example, from the dataframe, a used
#soft tyre (SOFT C4u) would have a red circle-cross
tyres = {"S": "red", "M": "yellow", "H": "white"}
usage = {"u": "circle-cross", "n": "circle"}

fig = px.line(
    df,
    x="Lap",
    y="Time",
    color="Constructor",
    color_discrete_sequence = sql_helper.driver_legend(df)["colours"],
    line_dash="Constructor",
    line_dash_sequence=sql_helper.driver_legend(df)["lines"],
    hover_name=df["Time"].view("int64").apply(sql_helper.strfdelta)
)
fig.update_traces(
               mode = "lines+markers",
                marker_color = list(df["Tyre"].apply(lambda x: tyres.get(x[0]) if x else None)),
                marker_size = 8,
                marker_line_width = 1,
                marker_symbol = list(df["Tyre"].apply(lambda x: usage.get(x[-1]) if x else None))
                )

The plot above indicates that both drivers, change tyres on the same lap, to the same tyre (example, observe LEC changes and takes on a yellow marker around lap (x-axis) 13 and so does VER, which is impossible.

This is grossly incorrect, for which I am confused. This shows that the marker colour and symbol is not being assigned correctly in my update_traces function, and does not change for each unique driver or constructor in the dataframe. Can someone please help me with this, It would be greatly appreciated.

Yep so basically plotly express can take a long format dataframe but what it does then is it separates it in different traces (in your case, 1 per โ€œConstructorโ€). Then when you do update_traces you are actually working on all the traces at the same time and updating them with the same values.

What you want to do is update individual traces with the proper color and symbol. You could go 2 ways about it:

  1. Either loop through the traces in fig.data and update each one appropriately
  2. You can still use update_traces but you need to use some trace selectors to ensure that you are updating the right trace the right way

Hereโ€™s the selector way:

# Transform long format to several traces
fig = px.line(
    df,
    x="lap",
    y="time",
    color="constructor",
)

# Update traces with common traits
fig.update_traces(mode="lines+markers", marker_line_width=1, marker_size=8)

# Update individual traces
tyres = {"S": "red", "M": "yellow", "H": "white"}
for constructor in df["constructor"].unique():
    fig.update_traces(
        selector={"name": constructor},
        marker_color=df.query("constructor == @constructor")["tyre"].map(tyres)
    )
fig.show()

1 Like

Once again, thank you for the immense help.