Text and Colorbar Alignment in Plotly Subplots with Python

Hello,

I have started using Plotly literally yesterday. I have worked quite a bit with matplotlib in the past but started working on a project where it does not meet my needs anymore. I am struggeling to figure out some pretty basic concepts which are quite intuitive to me in matplotlib at this point:

Say I have a figure with 2x4 subplots. Each subplot contains a surface plot, in this case a colored 3D sphere. Here’s what I would like to do (Full code example and output below):

  1. Each column get’s a shared colorbar aligned with the current subplot axes. In matplotlib this can easily be achieved with the cax argument in colorbar() and using e.g. inset axes (my personal favorit). Also it gives me the ability to customize the location of the color bar relative to the subplot by using different transforms. I have not been able to figure out how to do this in Plotly. I can adjust the locations of the colorbars in absolute figure coordinates but depending on how complex the layout is, this is extremely tedious. So is there a way to set the xy-coordinates of the colorbar to a value relative to the xy-coordinate of the current subplot? Or is there even something similar to inset_axes in matplotlib?

  2. I would like to have vertical colorbar labels next to the ticks. In matplotlib this can be achieved by chosing either the y or x axis of the colorbar axis. What would the proper way to do this in Plotly?

  3. I want to add some textboxes (in this example “Midday” and “Midnight” at the left). Similar to 1) I would like to align them relative to the subplots so that they are centered with respect to the upper and lower rows.

  4. I do not understand why Plotly adds an additional colorbar at the right and have not been able to figure out how to remove it. I have tried fig.update(layout_showlegend = False), fig.update_coloraxes(showscale=False) and fig.update_layout(coloraxis_showscale = False) but to no effect.

Any help is appreciated. Thanks.

import dash
import dash_core_components as dcc
import dash_html_components as html
import webbrowser
from threading import Timer
import numpy as np
import plotly.graph_objs as go
import plotly
import copy


port = 5000
def open_browser():
	webbrowser.open_new("http://localhost:{}".format(port)
                     )

app = dash.Dash(__name__)

fnts = [10, 12, 14, 20]
res = 4
phi = np.linspace(0, np.pi, 2**res + 1)
theta = np.linspace(0, 2*np.pi, 2**res + 1)
phi, theta = np.meshgrid(phi, theta)

# The Cartesian coordinates of the unit sphere
x = np.sin(phi) * np.cos(theta)
y = np.sin(phi) * np.sin(theta)
z = np.cos(phi) 

surfacecolor = copy.deepcopy(np.abs(phi - np.pi / 2)
                             )
#Create figure with subplots
nCols = 4
fig = plotly.subplots.make_subplots(rows=2, cols=nCols,
                        specs = [[{'type':'surface'
                                  } 
                                  for i in range(nCols)
                                  ],
                                 [{'type':'surface'
                                  } 
                                  for i in range(nCols)
                                  ]
                                 ],
                        horizontal_spacing=.0,
                        subplot_titles = ('Temperature', 
                                          'Net LW Flux', 
                                          'Net SW Flux', 
                                          'Net Integrated Flux', 
                                          )
                        )
                        
fig.add_annotation(x=0,
                   y=1,
                   text="Midday",
                   showarrow=False,
                   textangle=-90,
                   font=dict(size=fnts[2]
                             )
                   )
fig.add_annotation(x=0,
                   y=0,
                   text="Midnight",
                   showarrow=False,
                   textangle=-90,
                   font=dict(size=fnts[2]
                             )
                   )


cmaps = ['jet', 'plasma', 'inferno', 'cividis']
#Plot to individual subplots
for i in range(2):
    for j in range(nCols):
        #Only add colorbar once for each row
        if i == 0:
            cbar = dict(len=1,
                        thickness=5.,
                    x=j / nCols + .2, #<--How to specify coordinates with respect to current suplot?
                    y=.5,
                    title='title'
                    )
        
        else:
            cbar = dict()
        
        cmap = cmaps[j]
        
        #Plot data
        fig.add_trace(
            go.Surface(
                z=z, 
                x=x, 
                y=y, 
                colorscale=cmap,
                surfacecolor = surfacecolor,
                colorbar = cbar,
            ),
            row = i+1,
            col = j+1
            )

#Remove xyz axes
fig.update_scenes({'xaxis':{'visible':False},
             'yaxis':{'visible':False},
             'zaxis':{'visible':False}
             }
                  )

#Attempt to remove redundant colorbar
fig.update(layout_showlegend=False
           )
fig.update_layout(coloraxis_showscale=False
                  )

app.layout = html.Div(children=[
    html.H1(children='Planets'
            ),

    html.Div(children='''
        Subtitle
    '''),

    dcc.Graph(
        id='example-graph',
        figure=fig
    )
]
             )

if __name__ == '__main__':
    Timer(1, open_browser).start();
    app.run_server(debug=True, use_reloader=False,
                   port=port
                   )

1 Like

Any movement on this? There seems to be no way to standardize colorbar range among subplots.