How to define colors in a figure using Plotly Graph Objects and Plotly Express
Asked Answered
M

1

36

There are many questions and answers that touch upon this topic one way or another. With this contribution I'd like to clearly show why an easy approch such as marker = {'color' : 'red'} will work for plotly.graph_objects (go), but color='red' will not for plotly.express (px) although color is an attribute of both px.Line and px.Scatter. And I'd like to demonstrate why it's awesome that it doesn't.


So, if px is supposed to be the easiest way to make a plotly figure, then why does something as apparently obvious as color='red' return the error

ValueError: Value of 'color' is not the name of a column in 'data_frame'.

To put it short, it's because color in px does not accept an arbitrary color name or code, but rather a variable name in your dataset in order to assign a color cycle to unique values and display them as lines with different colors.

Let me demonstrate by applyig a gapminder dataset and show a scatterplot of Life expectancy versus GDP per capita for all (at least most) countries across the world as of 2007. A basic setup like below will produce the following plot

Figure 1, plot using go:

enter image description here

The color is set by a cycle named plotly but is here specified using marker = {'color' : 'red'}

Figure 2, code:

import plotly.graph_objects as go

df = px.data.gapminder()
df=df.query("year==2007")

fig = go.Figure()
fig.add_traces(go.Scatter(x=df['gdpPercap'], y=df["lifeExp"],
                          mode = 'markers',
                          marker = {'color' : 'red'}
                         ))
fig.show()

So let's try this with px, and assume that color='red' would do the trick:

Code 2, attempt at scatter plot with defined color using px:

# imports
import plotly.express as px
import pandas as pd

# dataframe
df = px.data.gapminder()
df=df.query("year==2007")

# plotly express scatter plot
px.scatter(df, x="gdpPercap", y="lifeExp",
           color = 'red',
          )

Result:

ValueError: Value of 'color' is not the name of a column in 'data_frame'. Expected one of ['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap', 'iso_alpha', 'iso_num'] but received: red

So what's going on here?

Mountainside answered 18/8, 2020 at 0:21 Comment(0)
M
60

First, if an explanation of the broader differences between go and px is required, please take a look here and here. And if absolutely no explanations are needed, you'll find a complete code snippet at the very end of the answer which will reveal many of the powers with colors in plotly.express


Part 1: The Essence:

It might not seem so at first, but there are very good reasons why color='red' does not work as you might expect using px. But first of all, if all you'd like to do is manually set a particular color for all markers you can do so using .update_traces(marker=dict(color='red')) thanks to pythons chaining method. But first, lets look at the deafult settings:

1.1 Plotly express defaults

Figure 1, px default scatterplot using px.Scatter

enter image description here

Code 1, px default scatterplot using px.Scatter

# imports
import plotly.express as px
import pandas as pd

# dataframe
df = px.data.gapminder()
df=df.query("year==2007")

# plotly express scatter plot
px.scatter(df, x="gdpPercap", y="lifeExp")

Here, as already mentioned in the question, the color is set as the first color in the default plotly sequence available through px.colors.qualitative.Plotly:

['#636EFA', # the plotly blue you can see above
 '#EF553B',
 '#00CC96',
 '#AB63FA',
 '#FFA15A',
 '#19D3F3',
 '#FF6692',
 '#B6E880',
 '#FF97FF',
 '#FECB52']

And that looks pretty good. But what if you want to change things and even add more information at the same time?

1.2: How to override the defaults and do exactly what you want with px colors:

As we alread touched upon with px.scatter, the color attribute does not take a color like red as an argument. Rather, you can for example use color='continent' to easily distinguish between different variables in a dataset. But there's so much more to colors in px:


The combination of the six following methods will let you do exactly what you'd like with colors using plotly express. Bear in mind that you do not even have to choose. You can use one, some, or all of the methods below at the same time. And one particular useful approach will reveal itself as a combinatino of 1 and 3. But we'll get to that in a bit. This is what you need to know:

1. Change the color sequence used by px with:

color_discrete_sequence=px.colors.qualitative.Alphabet

2. Assign different colors to different variables with the color argument

color = 'continent'

3. customize one or more variable colors with

color_discrete_map={"Asia": 'red'}

4. Easily group a larger subset of your variables using dict comprehension and color_discrete_map

subset = {"Asia", "Africa", "Oceania"}
group_color = {i: 'red' for i in subset}

5. Set opacity using rgba() color codes.

color_discrete_map={"Asia": 'rgba(255,0,0,0.4)'}

6. Override all settings with:

.update_traces(marker=dict(color='red'))

Part 2: The details and the plots

The following snippet will produce the plot below that shows life expectany for all continents for varying levels of GDP. The size of the markers representes different levels of populations to make things more interesting right from the get go.

Plot 2:

enter image description here

Code 2:

import plotly.express as px
import pandas as pd

# dataframe, input
df = px.data.gapminder()
df=df.query("year==2007")

px.scatter(df, x="gdpPercap", y="lifeExp",
           color = 'continent',
           size='pop',
          )

To illustrate the flexibility of the methods above, lets first just change the color sequence. Since we for starters are only showing one category and one color, you'll have to wait for the subsequent steps to see the real effects. But here's the same plot now with color_discrete_sequence=px.colors.qualitative.Alphabet as per step 1:

1. Change the color sequence used by px with

color_discrete_sequence=px.colors.qualitative.Alphabet

enter image description here

Now, let's apply the colors from the Alphabet color sequence to the different continents:

2. Assign different colors to different variables with the color argument

color = 'continent'

enter image description here

If you, like me, think that this particular color sequence is easy on the eye but perhaps a bit indistinguishable, you can assign a color of your choosing to one or more continents like this:

3. customize one or more variable colors with

color_discrete_map={"Asia": 'red'}

enter image description here

And this is pretty awesome: Now you can change the sequence and choose any color you'd like for particularly interesting variables. But the method above can get a bit tedious if you'd like to assign a particular color to a larger subset. So here's how you can do that too with a dict comprehension:

4. Assign colors to a group using a dict comprehension and color_discrete_map

# imports
import plotly.express as px
import pandas as pd

# dataframe
df = px.data.gapminder()
df=df.query("year==2007")

subset = {"Asia", "Europe", "Oceania"}
group_color = {i: 'red' for i in subset}

# plotly express scatter plot
px.scatter(df, x="gdpPercap", y="lifeExp",
           size='pop',
           color='continent',
           color_discrete_sequence=px.colors.qualitative.Alphabet,
           color_discrete_map=group_color
          )

enter image description here

5. Set opacity using rgba() color codes.

Now let's take one step back. If you think red suits Asia just fine, but is perhaps a bit too strong, you can adjust the opacity using a rgba color like 'rgba(255,0,0,0.4)' to get this:

enter image description here

Complete code for the last plot:

import plotly.express as px
import pandas as pd

# dataframe, input
df = px.data.gapminder()
df=df.query("year==2007")

px.scatter(df, x="gdpPercap", y="lifeExp",
           color_discrete_sequence=px.colors.qualitative.Alphabet,
           color = 'continent',
           size='pop',
           color_discrete_map={"Asia": 'rgba(255,0,0,0.4)'}
          )

And if you think we're getting a bit too complicated by now, you can override all settings like this again:

6. Override all settings with:

.update_traces(marker=dict(color='red'))

enter image description here

And this brings us right back to where we started. I hope you'll find this useful!

Complete code snippet with all options available:

# imports
import plotly.express as px
import pandas as pd

# dataframe
df = px.data.gapminder()
df=df.query("year==2007")

subset = {"Asia", "Europe", "Oceania"}
group_color = {i: 'red' for i in subset}

# plotly express scatter plot
px.scatter(df, x="gdpPercap", y="lifeExp",
           size='pop',
           color='continent',
           color_discrete_sequence=px.colors.qualitative.Alphabet,
           #color_discrete_map=group_color
           color_discrete_map={"Asia": 'rgba(255,0,0,0.4)'}
          )#.update_traces(marker=dict(color='red'))
Mountainside answered 18/8, 2020 at 0:22 Comment(8)
Fantastic answer!Dwarf
@Dr.H.Lecter Glad you liked it! Out of curiosity, what particular challenge was it that made you search for it? Exactly the same as described in the question? Or something a bit different?Mountainside
The more larger question/answer of "How to override the defaults and do exactly what you want with px colors", I could not find easily or quickly the information I was looking for in the documentation.Dwarf
@Dr.H.Lecter Thanks for the feedback! I'll actully consider changing the title to "How to override the defaults and do exactly what you want with px colors".Mountainside
This is a great answer and a nice gist of the manual. I would like to suggest one edit though. I think it would be important to emphasize in your example that discrete coloring will only work with categorical variables or strings (continent in the example). Even if you have a limited set of integers, it will be interpreted as a continuous scale for the colors. So a dictionary like color_map = {1: "red", 2: "white"} will not work, but after converting the coloring variable to strings, color_map = {"1": "red", "2": "white"} will.Bentley
@Bentley Thanks for the feedback! That would certainly be a valuable addition to the post!Mountainside
@vestland, if you are on the plotly team, I would also like to suggest to make this point more prominent in the manual. It could be that I am missing something, but the switch between discrete and continuous scales was not clear to me reading the manual for plotly.express.bar. I used trial and error with your example to figure it out. I eventually also stumbled upon the page called "Discrete Colors in Python" at plotly.com, but would have been nice to find everything in the function's documentation.Bentley
Only plotly express is covered here but in excellent detail. Is it also possible to include options for graph objects? We are not allowed to use pandas. With polars which prevents us from using plotly express, we are looking for options with graph objects.Kiloliter

© 2022 - 2024 — McMap. All rights reserved.