0

How do I assign a color to individual line segments using plotly?

Examle: Plot a parabola where the line color indicates its slope.

import numpy as np
from plotly import graph_objects as go

x = np.linspace(-1, 1, 101)
y = x**2
# Slope in-between of the knots
dy = (x[1:] + x[:-1])
 
fig = go.Figure()
fig.add_scatter(x=x, y=y)
Progman
  • 16,827
  • 6
  • 33
  • 48
MauiMuc
  • 50
  • 6

2 Answers2

2
  • you need a trace for each color segment
  • Plotly Express approach is simple to use for this
import numpy as np
import plotly.express as px

x = np.linspace(-1, 1, 101)
y = x**2
# Slope in-between of the knots
dy = (x[1:] + x[:-1])

# all arrays need to be same length
fig = px.line(x=x, y=y, color=np.pad(dy, (1,0), mode="edge")>0)
fig.update_layout(showlegend=False)

enter image description here

no gaps - generate multi-trace figure using list for y

px.line(x=x, y=[y, np.where(np.pad(dy, (1,0), mode="edge")>0,y,np.nan)]).update_layout(showlegend=False)

multiple segments (25) no gaps

Per comments, you are looking for multiple segments (not just 2). This is really discrete bins pd.cut(), plus filling gaps pandas ffill()

df = pd.DataFrame({"x":x,"y":y,"dy":np.pad(dy, (1,0), mode="edge")})
df["color"] = pd.cut(df["dy"], bins=25, labels=False)
df = df.set_index(["x","color"]).drop(columns=["dy"]).unstack("color").droplevel(0,1)
px.line(df.ffill(limit=1, axis=1), x=df.index, y=df.columns)

enter image description here

Rob Raymond
  • 29,118
  • 3
  • 14
  • 30
  • That approach unfortunately introduces a gap when the color changes. The documentation says: Values from this column or array_like are used to assign color to **marks**. Thanks for pointing my attention to Numpy's `pda()`. – MauiMuc Nov 07 '21 at 07:27
  • really this is continuous data so possibly better as a scatter `fig = px.scatter(x=x, y=y, color=np.pad(dy, (1,0), mode="edge"), color_continuous_scale="Viridis")` this also uses a colorscale as per you comment on other answer – Rob Raymond Nov 07 '21 at 09:19
  • updated for no gaps... – Rob Raymond Nov 08 '21 at 14:50
  • My data isn't dense enough that a scatter-plot could do the job. Does plotly offer functionality to convert scalars into color-codes? Is it possible to keep a single legend entry providing that on/off toggling? – MauiMuc Nov 08 '21 at 20:08
  • you really need to think about weather your data is continuous or discrete and then use which ever capability you want. it appears you are stating discrete, but not using terminology that's consistent with this – Rob Raymond Nov 08 '21 at 20:33
  • I have a time-series in mind. The _true_ signal is real valued and continuous over time. That signal is discretized over time but every record gets a value from the real line assigned. The first time-derivative of that series is approximated by a first order finite difference which is again a real number. The color of each line segment shall refer to the value of the derivative. – MauiMuc Nov 10 '21 at 19:50
0

I think you have to add it piecewise:

import numpy as np
from plotly import graph_objects as go

x = np.linspace(-1, 1, 101)
y = x**2
# Slope in-between of the knots
dy = (x[1:] + x[:-1])
line_cols = np.where(dy > 0,"#F2F013","#2F86A6")

fig = go.Figure()
for i, col in enumerate(line_cols):
    fig.add_scatter(x=x[i:(i+2)], y=y[i:(i+2)],line=dict(color=col),
                    mode='lines',showlegend=False)

fig.show()

enter image description here

StupidWolf
  • 45,075
  • 17
  • 40
  • 72
  • Is there the possibility of choosing a continuous color-map e.g. viridis? – MauiMuc Nov 06 '21 at 19:47
  • One more question: Is it possible to have a single legend entry such that the neat toggling functionality is preserved? – MauiMuc Nov 08 '21 at 19:59