# Add row and column labels to grid plot

**URL:** https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750
**Category:** General Usage
**Tags:** question, plotting
**Created:** [May 26, 2024, 11:46am UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750 "2024-05-26T11:46:37Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)
#### Post date: [May 26, 2024, 11:46am UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/1 "2024-05-26T11:46:37Z")

</div>

Hi all,

I have a grid of contour plots, each with the same x and y dimensions. The grid is formed by varying two other dimensions. I would like to add labels r1, r2, …, rn to the rows and c1, c2, …, cn to the columns. [corrplot](https://user-images.githubusercontent.com/8429802/51600126-91059000-1f01-11e9-9d37-f49bee5ff534.png) from StatsPlots.jl is a similar example, but I could not figure out how the row and column labels are added. How can I do that?

Here is a simple example, replacing contours with scatter plots for simplicity.

```julia
using Plots
x = [(rand(10), rand(10)) for _ ∈ 1:9]
scatter(x, leg = false, xlabel = "x", ylabel = "y", layout = (3, 3))

```

---

<div class="post-metadata">

### Author: ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)
#### Post date: [May 27, 2024, 11:41am UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/2 "2024-05-27T11:41:43Z")

</div>

You can use the `subplot` parameter. Here is an example (with 2x2 plots):

```julia
using Plots
x = [(rand(10), rand(10)) for _ ∈ 1:4]
p=plot( layout = (2, 2))
scatter!(p,x[1], leg = false, xlabel = "x1", ylabel = "y1", subplot=1)
scatter!(p,x[2], leg = false, xlabel = "x2", ylabel = "y2", subplot=2)
scatter!(p,x[3], leg = false, xlabel = "x3", ylabel = "y3", subplot=3)
scatter!(p,x[4], leg = false, xlabel = "x4", ylabel = "y4", subplot=4)

```

Result:

![image](https://global.discourse-cdn.com/julialang/original/3X/e/2/e2b49f8eeb9680d34ec8656998b3c41d333c5d65.png)

---

<div class="post-metadata">

### Author: ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)
#### Post date: [May 27, 2024, 1:27pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/3 "2024-05-27T13:27:39Z")

</div>

Thanks for your reply. Unfortunately, your recommendation does not achieve my goal. In my use case, there are 5 dimensions: the x,y and z axes of each contour plot are the same variables. The other two dimensions are the rows and columns of the grid. For example, the top left contour plot shows z as a function of x and y at R= r1 and C = c1. I realize that is a bit abstract. I can illustrate with a concrete example if that helps.

---

<div class="post-metadata">

### Author: ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)
#### Post date: [May 27, 2024, 1:44pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/4 "2024-05-27T13:44:25Z")

</div>

The best solution I have came up with so far is this:

```julia

using Plots
x = [(rand(10), rand(10)) for _ ∈ 1:9]
p = scatter(x, leg = false, xlabel = "x", ylabel = "y", layout = (3, 3))
[ylabel!(p[i,1], "r$i \n y") for i ∈ 1:3]
[title!(p[1,i], "c$i") for i ∈ 1:3]
p

```

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

Is there a way to rotate r1, r2 and r3 90 degrees clockwise and enlarge the font size to differentiate it from the y axes better?

I tried this, but the syntax is not quite right:

```julia
[ylabel!(p[i,1], "$(font("r $i", 12, rotation=90)) \n y") for i ∈ 1:3]

```

Any recommendations?

---

<div class="post-metadata">

### Author: ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)
#### Post date: [May 27, 2024, 2:18pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/5 "2024-05-27T14:18:34Z")

</div>

Sorry for misunderstanding.  
Perhaps `annotate!` is the easiest way to go:

```julia
using Plots
x = [(rand(10), rand(10)) for _ ∈ 1:9]
p = scatter(x, leg = false, xlabel = "x", ylabel = "y", layout = (3, 3))
[ylabel!(p[i,1], "\n y") for i ∈ 1:3]
p

```

I have set ylabel only with a new line for making some room on the left.

Add a single row label to the first row:

```julia
annotate!(p[1,1], -0.35, 0.7, text("r1", 10))

```

It doesn’t work very well, as the coordinates depend on the values in the plot on position (1,1).

---

<div class="post-metadata">

### Author: ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)
#### Post date: [May 27, 2024, 2:31pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/6 "2024-05-27T14:31:59Z")

</div>

No problem. I could have been more clear in my initial post.

I do share your concern about `annotate`. Defining a rule for position might be challenging.

If I can figure out a way to improve the row label, it will be good.

---

<div class="post-metadata">

### Author: ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)
#### Post date: [May 27, 2024, 7:16pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/7 "2024-05-27T19:16:00Z")

</div>

Here is my best attempt:

```julia
using Plots
x = [(rand(10), rand(10)) for _ in 1:9]
l = @layout [
   a{0.1w,0.1h} b{0.3w,0.1h} c{0.3w,0.1h} d{0.3w,0.1h}
   e{0.1w,0.1h} f{0.3w,0.3h} g{0.3w,0.3h} h{0.3w,0.3h}
   i{0.1w,0.1h} j{0.3w,0.3h} k{0.3w,0.3h} l{0.3w,0.3h}
   m{0.1w,0.1h} n{0.3w,0.3h} o{0.3w,0.3h} p{0.3w,0.3h}
]
p = plot(
   layout = l, legend = false
)
headers=[1,2,3,4,5,9,13]
subplots=[6,7,8,10,11,12,14,15,16]
[plot!(p[headers[i]],(0,0),xlimits = (-1,1),ylimits=(-1,1),grid = false, axis=([], false)) for i in 1:7 ];
[scatter!(p[subplots[i]],x[i], leg = false, xlabel = "x", ylabel = "y") for i in 1:9 ];
p
annotate!(p[2], 0.0, 0.0, text("col1", 10, :center, :center))
annotate!(p[3], 0.0, 0.0, text("col2", 10, :center, :center))
annotate!(p[4], 0.0, 0.0, text("col3", 10, :center, :center))
annotate!(p[5], 0.0, 0.0, text("row1", 10, :center, :center))
annotate!(p[9], 0.0, 0.0, text("row2", 10, :center, :center))
annotate!(p[13], 0.0, 0.0, text("row3", 10, :center, :center))

```

Looks like:

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

---

<div class="post-metadata">

### Author: ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)
#### Post date: [May 27, 2024, 8:40pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/8 "2024-05-27T20:40:29Z")

</div>

> [@oheil](#):
>
> ```julia-auto
> l = @layout [
> a{0.1w,0.1h} b{0.3w,0.1h} c{0.3w,0.1h} d{0.3w,0.1h}
> e{0.1w,0.1h} f{0.3w,0.3h} g{0.3w,0.3h} h{0.3w,0.3h}
> i{0.1w,0.1h} j{0.3w,0.3h} k{0.3w,0.3h} l{0.3w,0.3h}
> m{0.1w,0.1h} n{0.3w,0.3h} o{0.3w,0.3h} p{0.3w,0.3h}
> ]
> 
> ```

Thank you. This looks very good!

One thing that is unclear to me is how I might generalize this to an arbitrary grid size, especially the `@layout` part. Do you know if there is a way to generalize the macro?

---

<div class="post-metadata">

### Author: ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)
#### Post date: [May 27, 2024, 9:05pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/9 "2024-05-27T21:05:34Z")

</div>

> [@Christopher\_Fisher](#):
>
> the `@layout` part. Do you know if there is a way to generalize the macro?

No, I tried but failed.  
But you can get the same result in a bit easier fashion without the macro:

```julia
using Plots
x = [(rand(10), rand(10)) for _ in 1:9]
p = plot(
  legend = false, layout = grid(4, 4, heights=[0.1 ,0.3, 0.3, 0.3], widths=[0.1 ,0.3, 0.3, 0.3])
)
headers=[1,2,3,4,5,9,13]
subplots=[6,7,8,10,11,12,14,15,16]
[plot!(p[headers[i]],(0,0),xlimits = (-1,1),ylimits=(-1,1),grid = false, axis=([], false)) for i in 1:7 ];
[scatter!(p[subplots[i]],x[i], leg = false, xlabel = "x", ylabel = "y") for i in 1:9 ];
p
annotate!(p[2], 0.0, 0.0, text("col1", 10, :center, :center))
annotate!(p[3], 0.0, 0.0, text("col2", 10, :center, :center))
annotate!(p[4], 0.0, 0.0, text("col3", 10, :center, :center))
annotate!(p[5], 0.0, 0.0, text("row1", 10, :center, :center))
annotate!(p[9], 0.0, 0.0, text("row2", 10, :center, :center))
annotate!(p[13], 0.0, 0.0, text("row3", 10, :center, :center))

```

---

<div class="post-metadata">

### Author: ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)
#### Post date: [May 27, 2024, 9:54pm UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/10 "2024-05-27T21:54:34Z")

</div>

Thank you for the help. Here is what I have so far. Perhaps there are some improvments that could be made.

```julia
using Plots
n_rows = 5
n_cols = 5
x = [(rand(10), rand(10)) for _ in 1:n_rows, _ in 1:n_cols]

function compute_weights(n)
    w = [0.15 ,fill(.85, n)...]
    return w ./ sum(w)
end
p = plot(
  legend = false, layout = grid(n_rows+1, n_cols+1, 
    heights = compute_weights(n_rows+1), 
    widths = compute_weights(n_cols+1)),
    margin = .15Plots.cm,
    size = (800, 800)
)

[plot!(p[r,1],(0,0),xlimits = (-1,1),ylimits=(-1,1),grid = false, axis=([], false)) for r in 1:n_rows+1];
[plot!(p[1,c],(0,0),xlimits = (-1,1),ylimits=(-1,1),grid = false, axis=([], false)) for c in 1:n_cols+1];
[scatter!(p[r,c], x[r-1,c-1], leg=false, xlabel = "x", ylabel = "y") for r in 2:(n_rows+1), c in 2:(n_cols+1)]

[annotate!(p[i,1], 0.0, 0.0, text("row$i", 10, :center, :center)) for i in 2:n_rows+1]
[annotate!(p[1,i], 0.0, 0.0, text("col$i", 10, :center, :center)) for i in 2:n_cols+1]

p

```

For some values of `n_rows` and `n_cols`, I encounter the following error:

```julia
ERROR: The sum of heights must be 1!
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] Plots.GridLayout(::Int64, ::Vararg{…}; parent::Plots.RootLayout, widths::Vector{…}, heights::Vector{…}, kw::@Kwargs{})
   @ Plots ~/.julia/packages/Plots/ju9dp/src/layouts.jl:221
 [3] grid(::Int64, ::Vararg{Int64}; kw::@Kwargs{heights::Vector{Float64}, widths::Vector{Float64}})
   @ Plots ~/.julia/packages/Plots/ju9dp/src/layouts.jl:209
 [4] top-level scope

```

which is due to numerical error, e.g.,

```julia
julia> sum(compute_weights(7))
1.0000000000000002

```

I suspect the following might be too strict:

```julia
if sum(heights) != 1
    error("The sum of heights must be 1!")
end

```

Do you agree, or is there a better way to handle the problem?

---

<div class="post-metadata">

### Author: ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)
#### Post date: [May 28, 2024, 9:29am UTC](https://discourse.julialang.org/t/add-row-and-column-labels-to-grid-plot/114750/11 "2024-05-28T09:29:49Z")

</div>

Yes, this error is a bit annoying. It could easily be calculated to a sum of 100% range in any case. But perhaps it’s clearer for the user if he has to think the proportions to be 100% in total. I don’t know which is best.

You could change your `compute_weights` function to:

```julia
function compute_weights(n)
    w = [0.15, fill(.85, n)...]
    weights = w ./ sum(w)
	[1.0-sum(weights[2:end]), weights[2:end]...]
end

```

(quick and dirty, no much brain used).
