# Wrong heatmap orientation with Plots.jl

**URL:** <https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822>\
**Category:** Visualization\
**Tags:** plots, makie\
**Created:** [January 16, 2025, 10:00am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822 "2025-01-16T10:00:18Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [January 16, 2025, 10:00am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/1 "2025-01-16T10:00:18Z")

</div>

I do not understand what I am doing wrong plotting a 2D function. The resulted heatmap is rotated 90 deg.

```Julia
using Plots

rosenbrock(x, y; a=1, b=100) = (a - x)^2 + b*(y - x^2)^2

# Define the range for x and y
x = -2:0.01:2
y = -1:0.01:3

# Create a grid of (x, y) points
z = [rosenbrock(xi, yi) for xi in x, yi in y];

# Plot the function with contour lines and log scale heatmap using Plots.jl
heatmap(x, y, log10.(z), xlabel="x", ylabel="y", title="Rosenbrock Function (Log Scale)", color=:viridis)

```

I get the following image

 ![rosenbrock_function](https://global.discourse-cdn.com/julialang/original/3X/c/4/c408558a2729c64a2d888ec749a2bd3df67c91b8.png)  
which is wrong. With Makie I get the right orientation:

```Julia
fig = Figure(resolution = (800, 600))
Axis(fig[1, 1], title = "Rosenbrock Function (Log Scale)", xlabel = "x", ylabel = "y")
heatmap!(x, y, log10.(z), colormap = :viridis)

```

 ![rosenbrock_function2](https://global.discourse-cdn.com/julialang/original/3X/2/8/2874d2fb1d898deede8b9594a5749f9b03d45563.png)

---

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [January 16, 2025, 10:14am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/2 "2025-01-16T10:14:16Z")

</div>

“which is wrong” - based on what?  
Makie and Plots simply have two different ways of defining the default transposition of a heatmat.  
Makie uses the idea that a heatmap is a matrix, so uses the default way of displaying matrices with the first dimension being rows going down.  
Plots uses the idea that any plot always plots x values on the x axis, y values on the y axis, with values rising upwards and towards the left.

None of these concepts are inherently more correct. And in fact plotting packages in many languages all do this differently. The Plots way has the advantage of intuitively adding e.g. x,y scatter points on top of a heatmap in the correct location.

You can always transpose the matrix and possibly set `yflip = true` to get the other behaviour.

---

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [January 16, 2025, 10:29am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/4 "2025-01-16T10:29:54Z")

</div>

> [@mkborregaard](#):
>
> always transpose the matrix

Yes

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [January 16, 2025, 10:31am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/5 "2025-01-16T10:31:16Z")

</div>

Thanks. Wrong in the sense that I didn’t get the expected result, and I order to get it I need to explicitly ‘transpose’ the matrix.

---

<div class="post-metadata">

**Author:** ![JM\_Beckers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jm_beckers/32/22482_2.png) [@JM\_Beckers](https://discourse.julialang.org/u/JM_Beckers)\
**Post date:** [January 16, 2025, 10:31am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/6 "2025-01-16T10:31:25Z")

</div>

I actually hate plotting interfaces for heatmaps, pcolor, contours etc where the coordinates can be specified by vectors for exactly that reason: there is no commonly accepted standard and particularly if you have the same vector length (and a square matrix) you will not even see if your interpretation is wrong in a lot of cases. I would really have preferred (even if that costs some memory) that plotting tools accept only coordinates x, and y which are also matrices and of the same size as the matrix to plot. Then you are sure that any discrete point [i,j] will be at position x[i,j],y[i,j] with value z[i,j]. And it also very naturally opens the way for curvilinear plots etc.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 16, 2025, 10:32am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/7 "2025-01-16T10:32:05Z")

</div>

On the other hand, images/photos do not require transposing and in Plots.jl we can do: `heatmap(rgb_image, framestyle=:none)`

---

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [January 16, 2025, 10:36am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/8 "2025-01-16T10:36:21Z")

</div>

Instead of transposing the matrix you also have a `transpose = true` keyword. It seems reasonable to have a keyword flick between different conventions.  
This is also discussed in [Axes swapped in heatmap · Issue #273 · JuliaPlots/Plots.jl · GitHub](https://github.com/JuliaPlots/Plots.jl/issues/273) and in [Resolved: Heatmap transposes matrices · Issue #205 · MakieOrg/Makie.jl · GitHub](https://github.com/MakieOrg/Makie.jl/issues/205)

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 16, 2025, 10:56am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/9 "2025-01-16T10:56:50Z")

</div>

> [@mkborregaard](#):
>
> Makie uses the idea that a heatmap is a matrix, so uses the default way of displaying matrices with the first dimension being rows going down.

That’s not true, Makie uses first dim → x, second dim → y for everything, which is why images are not correctly oriented by default.

```julia
julia> heatmap([
           1 2
           3 4
       ], colormap = [:red, :green, :blue, :orange])

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/5/b/5bff9a44bf8aeb6005e6275b39119f801cb63678.png)

To get the expected orientation for images, you have to do something like this:

```julia
julia> heatmap([
           1 2
           3 4
       ]', colormap = [:red, :green, :blue, :orange], axis = (; yreversed = true))

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/c/0/c0e6ed0ea32b70fc641d67d64806a49d623a0b3f.png)

---

<div class="post-metadata">

**Author:** ![kellertuer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kellertuer/32/220707_2.png) [@kellertuer](https://discourse.julialang.org/u/kellertuer)\
**Post date:** [January 16, 2025, 11:14am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/10 "2025-01-16T11:14:00Z")

</div>

To summarise as to _why_ you have to transpose:

If you look at the axes in the plots you see that the x-axis has “increasing while going left”, y is “incurring while going up”. We usually would put these coordinates as (x,y)

But if you consider a matrix (which contains the pixels/ samples for your contour map colors)

A = \begin{pmatrix} a\_{11} & a\_{12} \\ a\_{21} & a\_{22} \end{pmatrix}

You notice:

- the first index is the rows, but that is the vertical one (previously y)
- the second one is the horizontal one
- the vertical axis is the opposite direction as the y above, it is “increasing while going down”

So, we have to (1) transport the matrix, which switches the roles of the indeces, so that the first one is the horizontal one, the second the vertical, and “flip” the vertical one.

---

<div class="post-metadata">

**Author:** ![mkborregaard](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkborregaard/32/556_2.png) [@mkborregaard](https://discourse.julialang.org/u/mkborregaard)\
**Post date:** [January 16, 2025, 11:55am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/11 "2025-01-16T11:55:14Z")

</div>

> [@jules](#):
>
> That’s not true

Sorry, I must have misremembered the discussion then. I just quoted it from memory, which was not careful enough. I can also read in the issue I linked that I actually changed my mind on this after thinking about it, and suggested changing Plots’ behaviour to match Makie’s.

Apparently 6 years is enough to forget something I felt strongly about at the time…

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 16, 2025, 11:58am UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/12 "2025-01-16T11:58:16Z")

</div>

No problem, many people are actually confused when they plot images with Makie because they expect the right orientation by default, but it’s tricky as explained above and I think the current default makes the most sense overall. Maybe in the future there’ll be an alternative for easier image plotting

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [January 16, 2025, 12:04pm UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/13 "2025-01-16T12:04:28Z")

</div>

> [@JM\_Beckers](#):
>
> there is no commonly accepted standard and particularly if you have the same vector length (and a square matrix) you will not even see if your interpretation is wrong in a lot of cases.

On the other hand, if we include the function in the heatmap, this problem disappears (i.e. both x and y are respected, and there is no need to transpose):

```julia
heatmap(x, y, log10 ∘ rosenbrock, xlabel="x", ylabel="y", color=:viridis)

```

---

<div class="post-metadata">

**Author:** ![peremato](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/peremato/32/29128_2.png) [@peremato](https://discourse.julialang.org/u/peremato)\
**Post date:** [January 16, 2025, 12:13pm UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/14 "2025-01-16T12:13:00Z")

</div>

Thanks, very good suggestion.

---

<div class="post-metadata">

**Author:** ![JM\_Beckers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jm_beckers/32/22482_2.png) [@JM\_Beckers](https://discourse.julialang.org/u/JM_Beckers)\
**Post date:** [January 16, 2025, 12:20pm UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/15 "2025-01-16T12:20:59Z")

</div>

Yes, if you have a function then this is nice and unambiguous.

---

<div class="post-metadata">

**Author:** ![sdanisch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sdanisch/32/1406_2.png) [@sdanisch](https://discourse.julialang.org/u/sdanisch)\
**Post date:** [January 16, 2025, 1:10pm UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/16 "2025-01-16T13:10:14Z")

</div>

> [@jules](#):
>
> Maybe in the future there’ll be an alternative for easier image plotting

That future is almost upon us:

```julia
image(Makie.logo())

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/1/f/1fa1c69f00768b6f94b2ddc18740dc094905c10c.png)

```julia
image(Makie.logo(), uv_transform=:transpose)

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/9/6/96180bf69a0da26a63992565ab2a3640486f31c7.png)

We added the `uv_transform` keyword as part of a patch release, so it’s not the default yet, but we should make it the default soon!

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 16, 2025, 1:31pm UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/17 "2025-01-16T13:31:41Z")

</div>

The problem is that this is actually not a transpose, I discussed this with @ffreyer the other day. It’s because the uvs of the underlying Rect are weird, probably chosen such that images are shown in correct orientation more easily.

Plus, even if it wasn’t buggy, for most images you would still want to count pixels from top to bottom, so you can’t really get around the `yreversed = true`.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [January 16, 2025, 2:53pm UTC](https://discourse.julialang.org/t/wrong-heatmap-orientation-with-plots-jl/124822/18 "2025-01-16T14:53:12Z")

</div>

To deal with this issue, the GMT.jl [grid](https://www.generic-mapping-tools.org/GMTjl_doc/documentation/general/types/index.html#grid_type) and [image](https://www.generic-mapping-tools.org/GMTjl_doc/documentation/general/types/index.html#image_type) types have a `layout` field that describe the memory layout. It’s a three (or four for images) chars string with `T(op)|B(ot) R(ow)|C(ol) B(and)|P(ixel)`, like `TRB`m saying if the array is Top-Bot, Row or Column major, and Band or Pixel interleaved. This last makes sense only to images.
