I have plotly 4.1.0 installed and this code (in a Jupyter Notebook):
import numpy as np
import plotly.offline as py
import plotly.graph_objs as go
matrix = np.random.randn(10, 10)
ticks = ["tick %i" % i for i in range(10)]
data = [go.Heatmap(z=matrix, x=ticks, y=ticks, colorscale='Viridis')]
layout = go.Layout(
hovermode='closest',
autosize=True,
xaxis=dict(zeroline=False),
yaxis=dict(zeroline=False, autorange='reversed', scaleanchor="x", scaleratio=1)
)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)
Produces this plot:

I think there really shouldn't be this space between the y ticks and colorbar and the heatmap. Without the yaxis scaleanchor="x" it looks fine, but I really want a square heatmap... Is there any setting I'm missing or is this a bug?
Thanks!
@cod3licious To get the right position for ticklabels, remove scaleanchor='x' and scaleratio=1 in yaxis definition (see here https://plot.ly/python/reference/#layout-xaxis-scaleanchor, details on these axis attributes).
and set the plot sizes, width=600, height=600 or any equal number of pixels.
matrix = np.random.randn(10, 10)
ticks = ["tick %i" % i for i in range(10)]
data = [go.Heatmap(z=matrix, x=ticks, y=ticks, colorscale='Viridis')]
layout = go.Layout(width=500, height=500,
hovermode='closest',
autosize=True,
xaxis=dict(zeroline=False),
yaxis=dict(zeroline=False, autorange='reversed')
)
fig = go.Figure(data=data, layout=layout)
Thanks, this works to create the plot I want. I'm still surprised though: is the original plot really the expected & desired behavior of the scaleanchor setting?! Also, the width and height setting fix only really works for square matrices, while ideally I'd also want square individual cells when displaying other rectangular matrices...
@cod3licious In your original plot the two attributes acted on the default plot size: width=700, and height=450. Setting a custom size as well as the attributes scaleanchor, and scaleratio, you can control and get a suitable plot window. Users usually set only width and height, which shouldn't be always equal. For a good understanding of scaleanchor and scaleratio role see https://plot.ly/python/axes/, section Fixed Ratio Axes, that illustrates the basic path for Hilbert curve.
I'm not quite sure I understand what you're getting at... scaleanchor and scaleratio are doing their job in the sense that they help create a heatmap plot with square individual cells. However, without manually adjusting the height or width, the plot will have these ugly gray shadings between the ticks and the colorbar instead of the ticks just being right at the heatmap. Of course, I could, for each matrix shape I want to plot, try to figure out the optimal height and width of the figure to minimize the gray areas, but this can't even be done by setting these parameters relative to the matrix shape since the colorbar also takes additional space that has to be accounted for. So I'm no seeing a clean way to create a heatmap plot with square cells that also doesn't have these gray spaces between heatmap and ticks for arbitrarily sized matrices. Is there maybe some additional tight setting, that makes the figure use only the space it needs to display the data instead of filling all the space it's given by creating those gray areas? Or can I only give either height or width and the other is adapted to fit the data that is being displayed?
@cod3licious scaleanchor='x', and scaleratio=1 (for the latter key, 1 is the default value) have the role to make a heatmap of squares in a window of given size, width= a, height=b minus margins. If width and height are not chosen such that the plot window to be exactly overlayed by the heatmap you are seeing those artifacts you are complaining of.
If you like to see only the heatmap and no gridlines, and a grey plot_bgcolor (i.e. the color of
plotting area in-between x and y axes), then disable the default plotly template, as follows:
from plotly import io as pio
pio.templates.default = "none"
Now run the following code, and the successive layout updates to understand better what is going on, when you don't set width, height, scaleanchor, and scaleratio (i.e you are plotting a heatmap in full layout), and then setting succesively some or all of these layout attributes:
import plotly.io as pio
pio.templates.default = 'none'
import numpy as np
import plotly.graph_objs as go
matrix = np.random.randn(10, 10)
ticks = ["tick %i" % i for i in range(10)]
data = [go.Heatmap(z=matrix, x=ticks, y=ticks, colorscale='Viridis')]
layout = go.Layout(
hovermode='closest',
autosize=True,
xaxis=dict(zeroline=False),
yaxis=dict(zeroline=False, autorange='reversed')
)
fig = go.Figure(data=data, layout=layout)
fig.show()
The succesive updates, just one in a notebook cell (or with fig.show() after each one):
fig.update_layout(yaxis_scaleanchor='x')
fig.update_layout(width=600, height=500)
fig.update_layout(height=600)# Now width=height
fig.update_layout(width=450, height=450)
Hence, with scaleanchor='x', and scaleratio=1, we get a heatmap of squares. The ticklabels for yaxis, are displayed in a desired/right position
only for width=height>=500.
For a smaller size the yaxis ticklabels are misplaced again (why? perhaps a plotly.js setup is leading to this mispositioning; @alexcjohnson or @etpinard could shed a light on this behaviour).
tight is matplotlib key, but
if you don't set the two yaxis attributes, and you are setting only suitable width and height, you can get a right heatmap.
I can get this to work if I constrain the xaxis to y and set constrain to "domain"... @alexcjohnson is this meant to work the other way too?
import numpy as np
import plotly.offline as py
import plotly.graph_objs as go
matrix = np.random.randn(10, 10)
ticks = ["tick %i" % i for i in range(10)]
data = [go.Heatmap(z=matrix, x=ticks, y=ticks, colorscale='Viridis')]
layout = go.Layout(
hovermode='closest',
autosize=True,
xaxis=dict(zeroline=False, scaleanchor="y", constrain="domain"),
yaxis=dict(zeroline=False, autorange='reversed')
)
fig = go.Figure(data=data, layout=layout)
py.iplot(fig)

ah, hah that's funny: when I exported it showed that in fact it just gives the same problem the other way :P
That said, for interactive usage my version works well:

@nicolaskruchten ... but the colorbar is too far from the heatmap.
OK, then just set height=width:

The underlying mechanism is that the x and y axes' ranges are mapped to some domain within the figure, and then that domain is subject to padding which comes from the tick labels and the colorbar if present. The domains are then mapped onto the figure proper which makes them subject to the aspect ratio of the height and width. As a result, the horizontal domain is always a bit squished compared to the vertical one, given the vertical colorbar and horizontal Y tick labels. This is why constraining the X axis works a bit better than constraining the Y axis IMO.
My version "scales down" to quite small values of height and width:



Let me also point out the xaxis.constraintoward: 'right' option, that - when used with xaxis.constrain: 'domain' - pushes the whole x axis (and the y-axis ticks with it) over to the right. We never alter the provided height and width (except autosize but that only considers external css/page sizing, not the plot contents). So without adjusting the width/height explicitly to match the data there's no way to satisfy the constraints and automatically end up with no excess whitespace anywhere - But at least with constraintoward you'll have all the plot elements aligned correctly, you just have to remove the large left margin to get the plot you want.
Also, be aware that you may specify constrain and constraintoward for BOTH axes. This is useful in the event you don't know the aspect ratio of your matrix ahead of time but you always want both x and y tick labels adjacent to the data.
https://codepen.io/alexcjohnson/pen/wvwJrqN?editors=0010

ah, hah that's funny: when I exported it showed that in fact it just gives the same problem the other way :P
That's weird, that part looks like a bug - in any given situation only one of the two axes should have ended up with a different range than it would have had without the constraint. Perhaps there's some funny interaction between the constraint and the auto tick rotation that we're not always dealing with correctly? If you can reproduce it let's file it over at plotly.js.
With @alexcjohnson's axes definitions we get a good looking heatmap only with width=height.
For width > height the fig title is located at the middle of width (see the second image below), while the heatmap is moved to right, due to constraintoward= 'right'.
If height>width the default colorbar height is equal to layout height, and hence a suitable colorbar len should be set in this case (the third image):
import numpy as np
import plotly.graph_objs as go
matrix = np.random.randn(10, 10)
ticks = ["tick %i" % i for i in range(10)]
data = [go.Heatmap(z=matrix, x=ticks, y=ticks, colorscale='Viridis', colorbar=dict(thickness=20))]
width = 400
height = 400
layout = go.Layout(title_text=f"width={width}, height={height}", title_x=0.5,
width = width, height = height,
xaxis =dict(scaleanchor= 'y', constrain= 'domain', constraintoward= 'right'),
yaxis=dict(zeroline=False, autorange='reversed', constrain='domain')
)
fig = go.Figure(data=data, layout=layout)



Thanks guys! The xaxis settings scaleanchor="y", constrain="domain", constraintoward="right" together with an equal width and height work well enough for my cases (incl. for rectangular and not only square matrices), so I'm closing the issue.
Just an update to my first version above... adding constrain="domain" to both axes ensures there's never plot area overflow in either direction.