Figure datatype badly factored for multiple traces of different data

I have a list of physical facilities mapped to lat/lon coordinates. I want to create multiple layers on a map, where each layer can show some subset of those facilities mapped according to some function of metrics present in my dataset. For example, imagine 100 houses in a square mile of land, each having their own risk factor for flooding. I’d like to color one partition of the houses using color_continuous_scale=“Blues” and another using “Reds”. And I’d like the two partitions to be part of the same figure, using the idiom

fig = px.scatter_mapbox(df, color=‘X’, color_continuous_scale=“Blues”, …)
fig.add_trace(px.scatter_mapbox(df, color=‘Not_X’, color_continuous_scale=“Reds”, …).data[0])

And I’d like it to be a general solution, meaning it should be able to work for N partitions (where N is on the order of 6-12). That is, don’t just think about using a color scale that’s white in the middle, blue on one end, red on the other, and mapping the two partitions as the negative and positive sides of a 0-based middle.

The problem, as I see it today, is that color and color scales are a function of LAYOUT, which means that when a Figure contains multiple traces (len(data)>1), they all share the same color scale. Which is most definitely NOT what I want.

Maybe there’s a way to tweak the expression of mapbox layers so that I can put figures on multiple layers and let each figure have one dataset and one color scale. That would work for me.

The other way would involved adding some additional parameters to the DATA side of the figure, which would override the parameters given in the LAYOUT side of the figure.

Thoughts or advice? Thanks!

Following up on my own post…it looks like I need to associate a different coloraxis with each trace, and instantiate those coloraxes in the layout. It’s not very intuitive to me how to do that nicely, however. Links to sample code welcome!

The coloraxis in the layout is mostly useful for the case when you want multiple traces to share a color scale. If you want each trace to use their own, then you can unset/not set trace.coloraxis and instead configure it in trace.marker.colorscale and trace.marker.colorbar.

1 Like

Can you say more about the best way to address and modify individual color traces in a Figure Widget? The scatter_mapbox constructors don’t take nearly all the parameters one would want to pass (not to mention the ambiguity of trying to pass down some parameters that could either be destined for the Trace side or the Layout side). But putting that aside, how does one go about setting a Name parameter properly so that it can be used in a selector? When I read scattermapbox Traces | Python | Plotly I see update Traces, plural, which means if I have two traces that simply represent two different dataframes, any attempt to name one will name the other forthwith! That’s not what I want.

I want a single figurewidget that will update in my Jupyter Notebook when data changes, and I want lots of scatter_mapbox layers. But the current factorization has me flummoxed. Right now I’m blowing off all the abstractions and just manipulating structures directly. But that’s my C background showing.

For what you’re trying to do, I would skip using plotly.express and manually assemble a figure out of plotly.graph_objects components, indeed. px.scatter_mapbox() is not at all designed to do what you’re trying :slight_smile:

A bit of extra information now that I’m back at my desk:

  • plotly.express provides a number of functions that build entire figures for you out of the pieces defined in plotly.graph_objects and then the resulting object has a set of .update_* methods meant to make it easy to tweak things. These updaters operate on collections like traces (plural) and axes (plural) and these can be targeted with the selector attribute. The idea here is that the px function sets enough in the trace that it can be targeted, so in the case of the name parameter, if you inspect the output of a px function, you can see the values of name that have been set for you, but in general you cannot force px to set them in a particular way.
  • the alternative to using px is to assemble a figure out of graph_objects explicitly, which is not a bad thing, it’s just more work in exchange for more control. In this case, you rarely need to use the .update_* methods because your code is constructing the trace objects directly from scratch.
  • here’s a toy example of how you might construct a two-trace map with each trace having its own color scale:
import plotly.graph_objects as go
import plotly.express.data as data

df = data.carshare()

#explicitly split the data
evens = df[::2]
odds = df[1::2]

fig = go.Figure(
    data=[
        # one trace per split: odds
        go.Scattermapbox(lat=evens["centroid_lat"], lon=evens["centroid_lon"], showlegend=False,
                         marker=dict(color=evens["peak_hour"], showscale=True, colorscale="reds",
                                    colorbar=dict(x=1.0))), #explicitly position color bar
        # one trace per split: evens
        go.Scattermapbox(lat=odds["centroid_lat"], lon=odds["centroid_lon"], showlegend=False,
                         marker=dict(color=odds["peak_hour"], showscale=True, colorscale="blues",
                                    colorbar=dict(x=1.05))) #explicitly position color bar
    ], 
    layout=dict(
        # explicitly configure map
        mapbox=dict(
            style="open-street-map", zoom=10, 
            center=dict(lat=45.52, lon=-73.59)
        )
    )
)
fig.show()

1 Like

That was such a great answer! It not only answered my instant question, it also gave me insights into other questions I’ve been struggling with. And it taught me a spiffy new python idiom as well. Really appreciate it!

:pray:

1 Like