Question: I'd like to visualize a 20x20 matrix, where top left point is (-10, 9) and lower right point is (9, -10). So the x is increasing from left to right and y is decreasing from top to bottom. So my idea was to pass x labels as a list: [-10, -9 ... 9, 9] and y labels as [9, 8 ... -9, -10]. This worked as intended in seaborn (matplotlib), however doing so in plotly just reverses the image vertically. Here's the code: import numpy as np import plotly.express as px img = np.arange(20**2).reshape((20, 20)) fig = px.imshow(img, x=list(range(-10, 10)), y=list(range(-10, 10)), ) fig.show()   import numpy as np import plotly.express as px img = np.arange(20**2).reshape((20, 20)) fig = px.imshow(img, x=list(range(-10, 10)), y=list(reversed(range(-10, 10))), ) fig.show()   Why is this happening and how can I fix it? EDIT: Adding seaborn code to see the difference. As you can see, reversing the range for labels only changes the labels and has no effect on the image whatsoever, this is the effect I want in plotly. import seaborn as sns import numpy as np img = np.arange(20**2).reshape((20, 20)) sns.heatmap(img, xticklabels=list(range(-10, 10)), yticklabels=list(range(-10, 10)) )   import seaborn as sns import numpy as np img = np.arange(20**2).reshape((20, 20)) sns.heatmap(img, xticklabels=list(range(-10, 10)), yticklabels=list(reversed(range(-10, 10))) )  

Answer: You should use origin :  origin (str, 'upper' or 'lower' (default 'upper')) – position of the [0, 0] pixel of the image array, in the upper left or lower left corner. The convention ‘upper’ is typically used for matrices and images.  import numpy as np import plotly.express as px img = np.arange(20**2).reshape((20, 20)) x = list(range(-10, 10)) y = list(reversed(range(-10, 10))) fig = px.imshow(img, x=x, y=y, origin='lower') fig.show()   Why reversing the labels doesn't work as expected ? For single-channel arrays (2d, not rgb), px.imshow() builds a heatmap trace with by default the parameter autorange='reversed' on the yaxis, as "the convention ‘upper’ is typically used for matrices". This takes form with the following line : autorange = True if origin == "lower" else "reversed"  The thing is that the autorange feature forces the orientation of the axis : when "reversed", the y's should be increasing from top to bottom (increasing vs decreasing inferred according to input data, not the same behavior with non-numeric strings), which means the yaxis is flipped iff the range of y's is not already reversed, in which case the whole is flipped vertically (any point (x, y) keeps its value). This is what you started with. Now, if you reverse the labels in y, it actually reverses the yaxis coordinates against the data (values should decrease as y increases), but by doing this, since the y range is now already inverted, the autorange doesn't have to flip the image, so it results in the yaxis being reversed (expected) and the image being not flipped, hence compared to what you started with, the image goes from flipped to unflipped (unexpected).  Alternatives In this situation, to avoid any confusion, an alternative to the solution above would be to define a specific range : fig = px.imshow(img, x=x, y=y) fig.update_yaxes(autorange=False, range=[-10.5, 9.5])  Or to have the data reoriented beforehand, in which case there is no need to reverse the y's (and even less to specify tickvals/ticktext, unless by preference) : img = img.tolist()[::-1] # reverse data x = list(range(-10, 10)) y = list(range(-10, 10)) # instead of y fig = px.imshow(img, x=x, y=y, origin='lower')   Output (same for the 3 code snippets)