4 Ways To Improve Your Plotly Graphs | by Dylan Castillo | Towards Data Science

Note: after publishing, I noticed that there are some issues with Plotly’s embeds in Medium. So you’ll need to click on the links to see how the graphs look. For a better reading experience, see the original version on my site.
These last weeks I've been working on an application using Dash and Plotly. These tools are great if you want to get something out quickly. But, as usual, there's no magical make_beautiful_graphs parameter you can set to True by default.
If you want to have beautiful and customized visualizations in your application, you'll need to spend some time playing around Plotly's extensive list of figures' attributes. I wanted to have something better than the default looks, so I went through Plotly's documentation, old code snippets I had, and Stack Overflow questions.
This is not the first time I found myself doing that. However, this time I decided to keep track of the things I frequently do when making graphs with Plotly. That way, I wouldn't need to read the same documentation or browse the same Stack Overflow questions next time.
In this article, I'm compiling a list of things I frequently do when building data visualizations using Plotly. Rest assured, I've been working in data-related positions for a while, so you will not find outrageous things like how to make 3D pie charts. Although these improvements are based on a sample of one, I've frequently seen others applying similar ideas.
I'm focusing on practical and simple improvements that apply to most of the basic charts: scatter plots, line charts, bar charts, and some statistical charts. Here you will find things like removing gridlines and not things like selecting the best colors for your 4D contour plot.
First, I'll do a brief introduction on how you build graphs using Plotly. Next, I'll provide a list of improvements and the reasoning behind them. Last, I'll give additional recommendations I've found useful when working with Plotly and other plotting libraries.
There are three things you need to know about the inner workings of Plotly:
First, making a graph in Plotly is essentially populating a Python dictionary. This dictionary is usually referred to as figure.
Second, there are two keys in the figure dictionary: layout and data. In layout, you define the looks of your graph like the typography, titles, and axes. In the data key, you set the values and information of the traces you'll be plotting. That could be something like [1, 2, 3] for X, [5, 3, 9] for Y and bar chart type.
Finally, once you populate the figure dictionary, it is serialized (transformed) into a JSON structure. This resulting data structure is then used by the Plotly JavaScript library to plot your chart.
That's it.
So, how do you make a figure?
There are multiple ways to do it. The lowest-level approach is to use Python dictionaries, and the highest-level one is using the Plotly Express interface. I tend to use a mid-level interface called Figure Constructor. It's easier to debug than using Python dictionaries, and it's more flexible than Plotly Express.
The code for making a graph using the Figure Constructor looks as follows:
import plotly.graph_objects as goimport numpy as npnp.random.seed(42)# Simulate datareturns = np.random.normal(0.01, 0.2, 100)price = 100 * np.exp(returns.cumsum())time = np.arange(100)# Generate graph using Figure Constructorlayout = go.Layout(    title="Historic Prices",    xaxis_title="time",    yaxis_title="price")fig = go.Figure(    data=go.Scatter(x=time, y=price),    layout=layout)   fig.show()
This is the resulting graph:
chart-studio.plotly.com
Here's the list of things I usually do to improve Plotly graphs:
#1: Remove gridlines and background color
#2: Keep consistent colors across graphs
#3: Use spikelines to compare data points
#4: Remove floating menu, disable zoom and adjust click behavior
Gridlines are lines that cross the chart to show axis divisions. They help the viewer visualize the value represented by an unlabeled data point fast. However, gridlines are not very useful when working with interactive graphs. You can hover over a data point and see its value. So more often than not, I remove gridlines when working with Plotly.‌‌
Here's how you can do it:
import plotly.graph_objects as goimport numpy as npnp.random.seed(42)# Simulate datareturns = np.random.normal(0.01, 0.2, 100)price = 100 * np.exp(returns.cumsum())time = np.arange(100)layout = go.Layout(    title="Historic Prices",    plot_bgcolor="#FFF",  # Sets background color to white    xaxis=dict(        title="time",        linecolor="#BCCCDC",  # Sets color of X-axis line        showgrid=False  # Removes X-axis grid lines    ),    yaxis=dict(        title="price",          linecolor="#BCCCDC",  # Sets color of Y-axis line        showgrid=False,  # Removes Y-axis grid lines        ))fig = go.Figure(    data=go.Scatter(x=time, y=price),    layout=layout)   fig.show()
And this is how it looks:‌
plotly.com
When working with categories, there are two things people usually like to do. First, they want to assign some specific colors to each group. For instance, if you are analyzing electoral results in the US, you probably want to use particular blue and red variations to identify the Democratic and Republican parties.
Second, you want this color to remain consistent across all the graphs you do. For example, if you are analyzing some real-world companies, you may want to use their distinctive colors to plot their prices but also when you analyze their returns.
Here's how you can do that using Plotly:
import plotly.graph_objects as goimport numpy as npnp.random.seed(42)# Simulate datareturns_A = np.random.normal(0.01, 0.2, 100)returns_B = np.random.normal(0.01, 0.2, 100)returns = np.append(returns_A, returns_B)prices_A = 100 * np.exp(returns_A.cumsum())prices_B = 100 * np.exp(returns_B.cumsum())prices = np.append(prices_A, prices_B)companies = ["A"] * 100 + ["B"] * 100time = np.append(np.arange(100), np.arange(100))df = pd.DataFrame({    "company": companies,    "time": time,    "price": prices,    "returns": returns})# Build graphCOLORS_MAPPER = {    "A": "#38BEC9",    "B": "#D64545"}layout = go.Layout(    title="Performance of A vs. B",        plot_bgcolor="#FFFFFF",    barmode="stack",    xaxis=dict(        domain=[0, 0.5],        title="time",        linecolor="#BCCCDC",    ),    yaxis=dict(        title="price",        linecolor="#BCCCDC"    ),    xaxis2=dict(        domain=[0.6, 1],        title="returns",        linecolor="#BCCCDC",    ),    yaxis2=dict(        anchor="x2",        linecolor="#BCCCDC"    ))data = []for company,col in COLORS_MAPPER.items():    time = df.loc[df.company == company, "time"]    price = df.loc[df.company == company, "price"]    returns = df.loc[df.company == company, "returns"]    line_chart = go.Scatter(        x=time,        y=price,        marker_color=col,  # Defines specific color for a trace        legendgroup=company,  # Groups traces belonging to the same group in the legend         name=company    )    histogram = go.Histogram(        x=returns,        marker_color=col,  # Defines specific color for a trace        legendgroup=company,  # Groups traces belonging to the same group in the legend         xaxis="x2",         yaxis="y2",        showlegend=False    )    data.append(line_chart)    data.append(histogram)fig = go.Figure(data=data, layout=layout)fig.show()
The snippet above allows you to keep consistent colors when working with multiple graphs that share the same categories. The critical part is the COLOR_MAPPER dictionary and its use when adding new traces. This dictionary is the mapping of the categories and colors you'll be using across your charts.
Whenever you add a trace to a graph, you can assign the right color to the marker_color attribute by getting it from the COLOR_MAPPER dictionary.
The resulting graph looks as follows:‌
plotly.com
A spike line is a vertical or horizontal line that appears when hovering on data. It's useful for comparing values in line charts and scatter plots. This is how you can add those using Plotly:
import plotly.graph_objects as goimport numpy as npnp.random.seed(42)# Simulate datareturns_A = np.random.normal(0.01, 0.2, 100)returns_B = np.random.normal(0.01, 0.2, 100)returns = np.append(returns_A, returns_B)prices_A = 100 * np.exp(returns_A.cumsum())prices_B = 100 * np.exp(returns_B.cumsum())prices = np.append(prices_A, prices_B)companies = ["A"] * 100 + ["B"] * 100time = np.append(np.arange(100), np.arange(100))df = pd.DataFrame({    "company": companies,    "time": time,    "price": prices,    "returns": returns})# Build graphlayout = go.Layout(    title="Performance of A vs. B",        plot_bgcolor="#FFFFFF",    hovermode="x",    hoverdistance=100, # Distance to show hover label of data point    spikedistance=1000, # Distance to show spike    xaxis=dict(        title="time",        linecolor="#BCCCDC",        showspikes=True, # Show spike line for X-axis        # Format spike        spikethickness=2,        spikedash="dot",        spikecolor="#999999",        spikemode="across",    ),    yaxis=dict(        title="price",        linecolor="#BCCCDC"    ))data = []for company in ["A", "B"]:    time = df.loc[df.company == company, "time"]    price = df.loc[df.company == company, "price"]    returns = df.loc[df.company == company, "returns"]    line_chart = go.Scatter(        x=time,        y=price,        name=company    )    data.append(line_chart)fig = go.Figure(data=data, layout=layout)fig.show()
This is the resulting graph:
plotly.com
I'm not too fond of the floating menu that Plotly adds to your chart by default. It makes graphs look cool, but I've rarely seen people using it. It has so many options that it's just confusing for someone looking at a graph for the first time. Usually, I remove it.
Also, I like to re-define two other user interaction parameters. I prefer to limit the users' ability to zoom in and change the behavior of clicking on a trace in the legend. In Plotly, by default, if you want to inspect a trace on its own, you have to double-click on the trace, instead of just clicking on it. That's not very intuitive, so I tend to invert that behavior.
This is how you can apply those changes:
import plotly.graph_objects as goimport numpy as npnp.random.seed(42)# Simulate datareturns_A = np.random.normal(0.01, 0.2, 100)returns_B = np.random.normal(0.01, 0.2, 100)returns = np.append(returns_A, returns_B)prices_A = 100 * np.exp(returns_A.cumsum())prices_B = 100 * np.exp(returns_B.cumsum())prices = np.append(prices_A, prices_B)companies = ["A"] * 100 + ["B"] * 100time = np.append(np.arange(100), np.arange(100))df = pd.DataFrame({    "company": companies,    "time": time,    "price": prices,    "returns": returns})# Build graphlayout = go.Layout(    title="Performance of A vs. B",        plot_bgcolor="#FFFFFF",    legend=dict(        # Adjust click behavior        itemclick="toggleothers",        itemdoubleclick="toggle",    ),    xaxis=dict(        title="time",        linecolor="#BCCCDC",    ),    yaxis=dict(        title="price",        linecolor="#BCCCDC"    ))data = []for company in ["A", "B"]:    time = df.loc[df.company == company, "time"]    price = df.loc[df.company == company, "price"]    returns = df.loc[df.company == company, "returns"]    line_chart = go.Scatter(        x=time,        y=price,        name=company    )    data.append(line_chart)fig = go.Figure(data=data, layout=layout)fig.show(config={"displayModeBar": False, "showTips": False}) # Remove floating menu and unnecesary dialog box
This is the resulting graph:
plotly.com
There are three things that I've found useful for learning how to make better data visualizations:
Get feedback from your audience: This is not always possible. But if you can do it, always prioritize getting input from those who will use your data visualizations. If you are working on a dashboard, the first thing you should do is understand what problem your dashboard solves. Then see users interacting with it. That has the highest ROI for your time.Check out Storytelling with Data by Cole Knaflic: It's a great book if you want to level-up your data visualization design skills. It provides a lot of practical advice and compelling use cases.Ploty's Figure Reference: Get used to Plotly's Figure Reference and Plotly's documentation. You'll be using it a lot. In general, Plotly's documentation is excellent.
Get feedback from your audience: This is not always possible. But if you can do it, always prioritize getting input from those who will use your data visualizations. If you are working on a dashboard, the first thing you should do is understand what problem your dashboard solves. Then see users interacting with it. That has the highest ROI for your time.
Check out Storytelling with Data by Cole Knaflic: It's a great book if you want to level-up your data visualization design skills. It provides a lot of practical advice and compelling use cases.
Ploty's Figure Reference: Get used to Plotly's Figure Reference and Plotly's documentation. You'll be using it a lot. In general, Plotly's documentation is excellent.
I hope you found these ideas useful. There might be some things that do not resonate with you, or others that you feel are missing. If that's the case, please let me know in the comments below. I'll be happy to update this and add other valuable advice.
You can find other articles I’ve authored here Or if you want to keep up-to-date with what I’m doing you can follow me on Twitter or LinkedIn.
This article was originally published in my blog.