18

How do I utilize plotly.express to plot multiple lines on two yaxis out of one Pandas dataframe?

I find this very useful to plot all columns containing a specific substring:

    fig = px.line(df, y=df.filter(regex="Linear").columns, render_mode="webgl")

as I don't want to loop over all my filtered columns and use something like:

    fig.add_trace(go.Scattergl(x=df["Time"], y=df["Linear-"]))

in each iteration.

vestland
  • 43,687
  • 29
  • 146
  • 249
derflo
  • 701
  • 1
  • 5
  • 8
  • Thanks vestland for the suggestion but in the question and answer plotly express isn't utilized at all. Instead it uses multiple times go.Scatter( x=df["x"], y=df["y"]). What I wanted through plotly express is px.line(df, y=) to plot all specific columns at once. Plotly express does not support adding lines straight to secondary_y by now. Therefore the indirect approach as shown in my answer below. I will clarify this in the question soon. For one very familiar with plotly this might be a nobrainer. For me it took some time :) – derflo Jul 12 '20 at 08:17
  • You seem to be right! – vestland Jul 12 '20 at 09:13

1 Answers1

37

It took me some time to fiddle this out, but I feel this could be useful to some people.

# import some stuff
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np

# create some data
df = pd.DataFrame()
n = 50
df["Time"] = np.arange(n)
df["Linear-"] = np.arange(n)+np.random.rand(n)
df["Linear+"] = np.arange(n)+np.random.rand(n)
df["Log-"] = np.arange(n)+np.random.rand(n)
df["Log+"] = np.arange(n)+np.random.rand(n)
df.set_index("Time", inplace=True)

subfig = make_subplots(specs=[[{"secondary_y": True}]])

# create two independent figures with px.line each containing data from multiple columns
fig = px.line(df, y=df.filter(regex="Linear").columns, render_mode="webgl",)
fig2 = px.line(df, y=df.filter(regex="Log").columns, render_mode="webgl",)

fig2.update_traces(yaxis="y2")

subfig.add_traces(fig.data + fig2.data)
subfig.layout.xaxis.title="Time"
subfig.layout.yaxis.title="Linear Y"
subfig.layout.yaxis2.type="log"
subfig.layout.yaxis2.title="Log Y"
# recoloring is necessary otherwise lines from fig und fig2 would share each color
# e.g. Linear-, Log- = blue; Linear+, Log+ = red... we don't want this
subfig.for_each_trace(lambda t: t.update(line=dict(color=t.marker.color)))
subfig.show()

Finished graph

The trick with

subfig.for_each_trace(lambda t: t.update(line=dict(color=t.marker.color)))

I got from nicolaskruchten here: https://stackoverflow.com/a/60031260

derflo
  • 701
  • 1
  • 5
  • 8