# Can I align all vertical axes in a multi-row faceted plot done with Plots.jl?

**URL:** https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156
**Category:** General Usage
**Tags:** plotting, plots
**Created:** [August 22, 2022, 9:22pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156 "2022-08-22T21:22:24Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![Soldalma](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/soldalma/32/29388_2.png) [@Soldalma](https://discourse.julialang.org/u/Soldalma)
#### Post date: [August 22, 2022, 9:22pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/1 "2022-08-22T21:22:24Z")

</div>

I tried to do a faceted plot using `Plots.jl`. The code below does almost everything I wanted,  
except for one thing: the vertical axes are aligned on each row, but not across rows. The argument  
`link = :y` in the last `Plots.plot` function was supposed to align all vertical axes, but it looks  
like it is aligning only the vertical axes in each row.

Note: Alignment depends on the specific random numbers generated. Occasionally all vertical axes do align.

Is there a way to align all vertical axes?

```julia
using Plots

dfd = DataFrame(
    :Date => [sum([Date(Dates.now()), Dates.Day(i)]) for i in 1:60],
    :Return => randn(60) ./ 100.0,
    :A => repeat(collect(1:5), inner = 12),
    :B => repeat(collect(1:12), inner = 5)
)

gdf = groupby(dfd, [:B])

n_rows = 3
n_cols = div(length(gdf), 3)

p = [
    Plots.plot(
        gdf[i].Date,
        gdf[i].Return,
        legend_position = false,
        rotation = 45,
        title = "B = $i",
        titlefontsize = 8,
        xtickfontsize = 6,
        yticks = i % n_cols == 1,
        ytickfontsize = 6) for i in 1:12
]

plt_panel = Plots.plot(
    p...,
    link=:y,
    size = (4000 / 4, 700),
    layout = (n_rows, n_cols),
    legend = false,
    plot_title = "Hello"
)

display(plt_panel)

```

---

<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: [August 22, 2022, 11:12pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/2 "2022-08-22T23:12:30Z")

</div>

After having defined `p`, you could do:

```julia
yl = (minimum(first.(ylims.(p))), maximum(last.(ylims.(p))))

```

and then use `plot()` keyword argument `ylims = yl` (instead of `link=:y`), to obtain:

 ![Plots_gr_gridplot_align_all_yaxes](https://global.discourse-cdn.com/julialang/original/3X/1/8/181fd46c5289f9d027685921f5d8b5a6d18b9ec4.jpeg)

---

<div class="post-metadata">

### Author: ![icweaver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/icweaver/32/45764_2.png) [@icweaver](https://discourse.julialang.org/u/icweaver)
#### Post date: [August 23, 2022, 8:48am UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/3 "2022-08-23T08:48:31Z")

</div>

This is a really interesting discussion, and it made me start looking for out-of-the box solutions that might be floating around already. I see that @rafael.guerra also has a [similar post](https://discourse.julialang.org/t/plotting-by-multiple-factors/68237/3) using StatsPlots.jl, but I guess the problem there is that some extra work would still need to be done to [hide shared axis decorations and switch from an auto generated legend to a title](https://github.com/JuliaPlots/StatsPlots.jl/issues/429#issuecomment-798836934)? Trying it out on the above still gets us pretty far I think:

```julia
using Plots, DataFrames, Dates

dfd = DataFrame(
    Date = (today() + Day(1)) : Day(1) : (today() + Day(60)),
    Return = randn(60) / 100.0,
    A = repeat(1:5; inner=12),
    B = repeat(1:12; inner=5),
)

@df dfd Plots.plot(:Date, :Return;
    group = {B = :B},
    layout = 12,
    size = (1_000, 700),
    rotation = 45,
    xtickfontsize = 6,
    ytickfontsize = 6,
    plot_title = "Hello",
    ylims = extrema(:Return),
)

```

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

This made me curious if AlgebraOfGraphics could get us the rest of the way there, but it looks like there are a couple of trade-offs, IIUC:

```julia
using CairoMakie, AlgebraOfGraphics

plt = data(dfd) * mapping(:Date, :Return;
    layout = :B => nonnumeric,
) *
visual(Lines)

fg = draw(plt;
    figure = (; resolution = (1_000, 700)),
    axis = (;
        xticklabelrotation = π/4,
        yticklabelrotation = π/4,
        xticks = datetimeticks(identity, dfd.Date),   
        xticklabelsize = 10,
        yticklabelsize = 10,
    ),
    facet = (; linkxaxes=:none),
)

Label(fg.figure[0, :], "Hello";
    textsize = 28,
    font = AlgebraOfGraphics.firasans("Medium"),
)

fg

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/a/d/ad3d9c829cd35a38f93bcbf5534b121b59465074.png)

A few of the benefits include computing the number of subplots we need automatically, hiding shared axis decorations, handling the global data limits, and applying common axis labels. The remaining downsides I am seeing are that we need to manually specify the xticks with a [helper function for now](https://discourse.julialang.org/t/dateformat-makie/85381/3), and stick to numerical facet labels to get the ordering right (i.e., doing something like `layout = :B => (x -> "B = $x")` would change the ordering according to `sort(string.(1:12))`, instead of following the natural sorting via something like NaturalSort.jl). I could be misunderstanding some of the more technical points here, so maybe @piever might have a better solution?

Anyway, I’m realizing that this thread was originally about Plots.jl, so I’d be happy to split this discussion off if that would be better

---

<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: [August 23, 2022, 12:59pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/4 "2022-08-23T12:59:20Z")

</div>

Something seems to be broken in Plots.jl for this problem.

Following the OP code, if we work with the plot keyword arguments `ylims`, `ytickfontsize` and `y_foreground_color_text`, we can get a decent result:

 ![Plots_gr_gridplot_align_all_yaxes](https://global.discourse-cdn.com/julialang/original/3X/5/d/5d24ae295af334c082defa66bd71d6976a5204f2.jpeg)

> **Plots.jl code**
>
> ```julia
> using Plots, DataFrames, Dates, Measures
> 
> dfd = DataFrame(
> :Date => [sum([Date(Dates.now()), Dates.Day(i)]) for i in 1:60],
> :Return => randn(60) ./100,
> :A => repeat(collect(1:5), inner = 12),
> :B => repeat(collect(1:12), inner = 5)
> )
> 
> gdf = groupby(dfd, [:B])
> 
> n_rows = 3
> n_cols = div(length(gdf), 3)
> 
> p = [Plots.plot(gdf[i].Date, gdf[i].Return, legend_position = false,
> rotation = 45, title = "B = $i", titlefontsize = 8, xtickfontsize = 6,
> ygrid = true, yticks = true, ytickfontsize = (i%n_cols==1) ? 6 : 1,
> y_foreground_color_text = (i%n_cols==1) ? :black : :transparent) for i in 1:12
> ]
> 
> yl = extrema(dfd.Return)
> 
> plt_panel = Plots.plot(p..., ylims = yl,
> size = (4000 / 4, 700), layout = (n_rows, n_cols),
> legend = false, plot_title = "Hello", bottom_margin = 5mm
> )
> 
> ```

---

<div class="post-metadata">

### Author: ![Soldalma](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/soldalma/32/29388_2.png) [@Soldalma](https://discourse.julialang.org/u/Soldalma)
#### Post date: [August 24, 2022, 2:22pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/7 "2022-08-24T14:22:21Z")

</div>

Thanks. I had noticed and was going to delete my post.

---

<div class="post-metadata">

### Author: ![Soldalma](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/soldalma/32/29388_2.png) [@Soldalma](https://discourse.julialang.org/u/Soldalma)
#### Post date: [August 24, 2022, 2:43pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/9 "2022-08-24T14:43:28Z")

</div>

I noticed two small differences between your two solutions:

1. The first one does not have tick marks on the vertical axes (except for the leftmost chart). The second one has tick marks on all vertical axes.

2. The space between the charts in one row is smaller in the first solution.

Ideally the best of the two solutions should be combined: smaller space between charts and tick marks in all axes. Is that possible?

---

<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: [August 24, 2022, 3:33pm UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/10 "2022-08-24T15:33:30Z")

</div>

Yes, it is possible. We need to apply appropriate logic to the `left_margin` and `right_margin` keyword arguments to get:

 ![Plots_gr_gridplot_align_all_yaxes](https://global.discourse-cdn.com/julialang/original/3X/d/1/d16a2021f02da3b6899de3517eb6e38e6f33dbbb.jpeg)

> **Plots.jl code updated**
>
> ```julia
> using Plots, DataFrames, Dates, Measures
> 
> dfd = DataFrame(
> :Date => [sum([Date(Dates.now()), Dates.Day(i)]) for i in 1:60],
> :Return => randn(60) ./100,
> :A => repeat(collect(1:5), inner = 12),
> :B => repeat(collect(1:12), inner = 5)
> )
> 
> gdf = groupby(dfd, [:B])
> 
> n_rows = 3
> n_cols = div(length(gdf), 3)
> 
> p = [Plots.plot(gdf[i].Date, gdf[i].Return, legend_position = false,
> rotation = 45, title = "B = $i", titlefontsize = 8, xtickfontsize = 6,
> ygrid = true, yticks = true, ytickfontsize = (i%n_cols==1) ? 6 : 1,
> y_foreground_color_text = (i%n_cols==1) ? :black : :transparent,
> left_margin = (i%n_cols==1) ? 5mm : -2mm,
> right_margin = (i%n_cols==0) ? 2mm : -2mm) for i in 1:12
> ]
> 
> yl = extrema(dfd.Return)
> 
> plt_panel = Plots.plot(p..., ylims = yl,
> size = (4000 / 4, 700), layout = (n_rows, n_cols),
> legend = false, plot_title = "Hello", bottom_margin = 5mm
> )
> 
> ```

---

<div class="post-metadata">

### Author: ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)
#### Post date: [September 6, 2022, 7:44am UTC](https://discourse.julialang.org/t/can-i-align-all-vertical-axes-in-a-multi-row-faceted-plot-done-with-plots-jl/86156/11 "2022-09-06T07:44:04Z")

</div>

> [@icweaver](#):
>
> The remaining downsides I am seeing are that we need to manually specify the xticks with a [helper function for now](https://discourse.julialang.org/t/dateformat-makie/85381/3), and stick to numerical facet labels to get the ordering right (i.e., doing something like `layout = :B => (x -> "B = $x")` would change the ordering according to `sort(string.(1:12))`, instead of following the natural sorting via something like NaturalSort.jl). I could be misunderstanding some of the more technical points here, so maybe @piever might have a better solution?

A bit late to the party (and sorry for the OT), but I just wanted to mention that there are a couple of [helper functions](http://juliaplots.org/AlgebraOfGraphics.jl/dev/layers/mapping/#Helper-functions) to reorder / customize labels of a grouping variable.
