plotly.graph_object.Scatter plot fill color map

Dear Help,

I am very new to plotly and I have spent a lot of time searching for a solution to my hopefully simple problem.

I want to fill the area between two lines using a continuous color map. In the example below, I have two lists for the low and high temperature limits, and want to fill between the two limits with a continuous colormap instead of just one solid color, using the red color to represent the highest temperature (ie 94) and blue for the lowest temperature (32). Any suggestions/comments are much appreciated.

import plotly.graph_objects as go
x = [1, 2, 3, 4, 5, 6, 7]
lowT = [35, 32, 41, 70, 79, 60, 38]
hiT = [48, 51, 48, 82, 94, 77, 51]
fig=go.Figure()
fig.add_trace(go.Scatter(x=x, y=hiT, mode=โ€˜linesโ€™))
fig.add_trace(go.Scatter(x=x, y=lowT, fill=โ€˜tonextyโ€™, mode=โ€˜linesโ€™))
fig.show()

Hi @jimy, welcome to the forums.

Unfortunately I donโ€™t think that this is possible. It might be possible to use a surface plot with a color gradient and add the lineplot as trace. This is going to be quite some work.

Abother thing you might do is add a scatterplot to โ€œfillโ€ the area between the two lines in a discrete manner.

Thank you so much for your response. This explains why I could not see anything relevant in my searches.

Yes, your suggestion of having discrete fills is a good idea. Alternatively, I am also thinking that I may be able to plot a rectangle and see if I can fill it with a continuous color map, and then use scatter plots to draw the two lines on top of the rectangle. If this can be done, I can then fill the upper line to the upper edge with white and the lower line to the x axis with white, leaving the color map in the desired band between the two lines displayed. Some quick searches donโ€™t even show this is promising though. I can use add_shape or hrect etc to draw the rectangle, but filling it with a colormap still seems difficult. I will keep trying and will post my codes if I can get it to work.

Hi @jimy, you could use the px.imshow for the rectangle:

import plotly.express as px
import numpy as np

arr = np.ones(shape=(50, 100))
y = np.arange(1, 51).reshape(50, 1)
arr = np.multiply(arr, y)

fig = px.imshow(arr, color_continuous_scale='bluered', origin='lower')
fig.show()

creates:
newplot(33)

OK, I played around a bit, needs some tweaking of the colors, though- and get rid of the rest of the array around the axes.

import plotly.graph_objects as go
import plotly.express as px

# data for overlay
lowT = np.asarray([35, 32, 41, 70, 79, 60, 38])
hiT = np.asarray([48, 51, 48, 82, 94, 77, 51])

# scaling the x- axis with 100
x = np.arange(0, len(lowT)) * 100

# create rectangle
arr = np.ones(shape=(hiT.max(), x.max()))
y = np.arange(1, hiT.max() + 1).reshape(hiT.max(), 1)
arr = np.multiply(arr, y)

# base figure
fig = px.imshow(arr, color_continuous_scale='bluered', origin='lower')


# add traces. fig=go.Figure()
fig.add_trace(
    go.Scatter(
        x=x, 
        y=hiT,
        fill='none', 
        mode='lines', 
        marker=dict(color='white'),
        showlegend=False
    )
)
fig.add_trace(
    go.Scatter(
        x=x, 
        y=[y.max()]*len(hiT),
        fill='tonexty',
        fillcolor='white',
        mode='lines', 
        marker=dict(color='white'),
        showlegend=False
    )
)
fig.add_trace(
    go.Scatter(
        x=x, 
        y=lowT, 
        fill='tozeroy', 
        fillcolor='white',
        mode='lines',
        marker=dict(color='white'),
        showlegend=False
    )
)

fig.update_layout(plot_bgcolor="white")

creates:

1 Like

Wow, this is great. Thank you so much, @AIMPED

Your idea of having a surface plot is good one too and I am just about to try.

Many thanks again.

The problem still ist that the colors are set to an absolute value, so that around x=400 almost all the surface is red- you might want the colors relative between the min and max temperature lines.

Hi @AIMPED, this is fine, not a big deal. it is already much better than what I can do. My problem now is to put this in a subplot and also create frames so that I can do animation. Not sure if plotly express figure can be easily added to the graph_object Frame, and plot it inside one of the subplots. I will spend some time myself to play with this. Thanks again for your big help.

1 Like

@AIMPED Thanks again for your help. Uisng a contour plot and borrowing your codes, I am able to make a plot very similar to what you did above:

import plotly.graph_objects as go
import numpy as np

# data for overlay
lowT = np.asarray([35, 32, 41, 70, 79, 60, 38])
hiT = np.asarray([48, 51, 48, 82, 94, 77, 51])

# scaling the x- axis with 100
x = np.arange(0, len(lowT)) * 100

# create rectangle
arr = np.ones((100, x.max()))
y = np.arange(1, 101).reshape(100, 1)
arr = np.multiply(arr, y)

fig = go.Figure(data = go.Contour(z=arr, colorscale='bluered', 
                                  contours_coloring='heatmap', line_width=0))

fig.add_trace(
    go.Scatter(
        x=x, 
        y=hiT,
        fill='none', 
        mode='lines', 
        marker=dict(color='white'),
        showlegend=False
    )
)
fig.add_trace(
    go.Scatter(
        x=x, 
        y=[y.max()]*len(hiT),
        fill='tonexty',
        fillcolor='white',
        mode='lines', 
        marker=dict(color='white'),
        showlegend=False
    )
)
fig.add_trace(
    go.Scatter(
        x=x, 
        y=lowT, 
        fill='tozeroy', 
        fillcolor='white',
        mode='lines',
        marker=dict(color='white'),
        showlegend=False
    )
)

fig.update_layout(plot_bgcolor="white")

fig.show()

2 Likes