# How to make multi-panel plots?

**URL:** <https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541>\
**Category:** Uncategorized\
**Created:** [October 28, 2021, 2:19pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541 "2021-10-28T14:19:11Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rahulub3r](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/rahulub3r/32/182_2.png) [@Rahulub3r](https://discourse.pumas.ai/u/Rahulub3r)\
**Post date:** [October 28, 2021, 2:19pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/1 "2021-10-28T14:19:11Z")

</div>

I want to make multi-panel plots in Pumas, but I am looking for something simple that can be reproduced by someone else on a different dataset. The dataframe can be thought of as having three columns: id, time, dv\_ipred; where id is the faceting column, time is the x-axis and dv\_ipred is the y-axis.  
I looked at the AlgebraOfGraphics piece of code:

```auto
f = data(df) * mapping(:time, :dv_ipred, layout=:id => nonnumeric)
draw(f)

```

However, if there are any missing rows in dv\_ipred it just prints the column out like a string in the plot even though it is type of Float64. AoG is very constrained and does not provide lot of flexibility.

So, I tried something in CairoMakie but the code (attached) got long very quickly. Would appreciate if you have any better ideas of doing this.

```auto
unique_subjects = unique(df.id)

num_subjects = length(unique_subjects)
num_columns = Int(ceil(sqrt(num_subjects)))
num_rows = Int(ceil(num_subjects/num_columns))

resolution = (1000,1000)
f = Figure(; resolution)
id_counter = 1
for (row, col) in Base.product(1:num_rows, 1:num_columns)
    if id_counter <= num_subjects
        a1 = Axis(f[row, col], title="ID = " * string(unique_subjects[id_counter]))
        subject_df = filter(x -> x.id == unique_subjects[id_counter], df)
        lines!(a1, subject_df.time, subject_df.dv_ipred, color=:black)
        id_counter += 1
    end
end
plottitle = Label(f[0, :], "PK Profiles", textsize = 30)
yaxis_lab = Label(f[:, 0], "Concentrations", rotation = pi/2, textsize=20)
xaxis_lab = Label(f[(num_rows+1):(num_rows+2), :], "Time", textsize=20,
                padding = (0, 0, 0, resolution[1]/20))
f

```

---

<div class="post-metadata">

**Author:** ![Michael](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/michael/32/818_2.png) [@Michael](https://discourse.pumas.ai/u/Michael)\
**Post date:** [October 28, 2021, 2:48pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/2 "2021-10-28T14:48:07Z")

</div>

Hi @Rahulub3r, AlgebraOfGraphics doesn’t automatically handle `missing` values and so will treat the column as nonnumeric data when plotting. Using `dropmissing` to remove the missing values from that column with `data(dropmissing(df, :dv_ipred))` should be sufficient to treat the column’s data as numeric instead.

---

<div class="post-metadata">

**Author:** ![Rahulub3r](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/rahulub3r/32/182_2.png) [@Rahulub3r](https://discourse.pumas.ai/u/Rahulub3r)\
**Post date:** [October 28, 2021, 2:54pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/3 "2021-10-28T14:54:06Z")

</div>

Thank you for your response @Michael . AoG shows the x-axis and y-axis separately for each plot. I want to do like a scales=“free” or scales=“free\_x” kind of thing to not have each sub-plot show me an axis with title and labels.

---

<div class="post-metadata">

**Author:** ![Michael](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/michael/32/818_2.png) [@Michael](https://discourse.pumas.ai/u/Michael)\
**Post date:** [October 28, 2021, 3:17pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/4 "2021-10-28T15:17:02Z")

</div>

The current version of AlgebraOfGraphics that we provide with Pumas does not provide an out-of-the-box handling of faceted labels. Once we ship a newer version of AlgebraOfGraphics then it will provide that feature automatically. Some amount of what you’re after may be achieved by the following:

```auto
fg = 
	draw(
		data(dropmissing(df, :dv_ipred)) *
		mapping(:time, :dv_ipred; layout = :id => nonnumeric);
		axis = (; xlabel = "", ylabel = ""),
	)
Label(fg.figure[:, 0], "Concentrations"; rotation = pi/2)
Label(fg.figure[end+1, 2:end], "Time")
Label(fg.figure[0, 2:end], "PK Profiles")
fg

```

---

<div class="post-metadata">

**Author:** ![Rahulub3r](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/rahulub3r/32/182_2.png) [@Rahulub3r](https://discourse.pumas.ai/u/Rahulub3r)\
**Post date:** [October 28, 2021, 3:22pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/5 "2021-10-28T15:22:31Z")

</div>

Wonderful. Thanks for your response Michael!

---

<div class="post-metadata">

**Author:** ![MathangiCTM](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/mathangictm/32/142_2.png) [@MathangiCTM](https://discourse.pumas.ai/u/MathangiCTM)\
**Post date:** [October 9, 2023, 9:51pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/6 "2023-10-09T21:51:45Z")

</div>

@Rahulub3r@Michael  
I would like to follow up on this question. In the above code, `fg =...`, is there a way to get the individual plots in multiple pages? like using `paginate = true` option.  
Right now, I have a similar code to get individual fits, however using the `paginate = true` option, gives me this error

```auto
ERROR: MethodError: no method matching draw(::Layers; paginate::Bool, separate::Bool, axis::NamedTuple{(:title, :xlabel, :ylabel, :xticks), Tuple{String, String, String, Vector{Int64}}}, legend::NamedTuple{(:position, :titleposition, :framevisible), Tuple{Symbol, Symbol, Bool}}, figure::NamedTuple{(:fontsize,), Tuple{Int64}})

Closest candidates are:
  draw(::AlgebraOfGraphics.AbstractDrawable; axis, figure, palettes, facet, legend, colorbar) got unsupported keyword arguments "paginate", "separate"
   @ AlgebraOfGraphics C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\AlgebraOfGraphics\yhdjr\src\draw.jl:44
  draw(::AlgebraOfGraphics.PaginatedLayers; kws...)
   @ AlgebraOfGraphics C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\AlgebraOfGraphics\yhdjr\src\paginate.jl:29
  draw(::AlgebraOfGraphics.PaginatedLayers, ::Int64; kws...)
   @ AlgebraOfGraphics C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\AlgebraOfGraphics\yhdjr\src\paginate.jl:39

Stacktrace:
 [1] kwerr(::NamedTuple{(:paginate, :separate, :axis, :legend, :figure), Tuple{Bool, Bool, NamedTuple{(:title, :xlabel, :ylabel, :xticks), Tuple{String, String, String, Vector{Int64}}}, NamedTuple{(:position, :titleposition, :framevisible), Tuple{Symbol, Symbol, Bool}}, NamedTuple{(:fontsize,), Tuple{Int64}}}}, ::Function, ::Layers)
   @ Base .\error.jl:165
 [2] top-level scope

```

---

<div class="post-metadata">

**Author:** ![Rahulub3r](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/rahulub3r/32/182_2.png) [@Rahulub3r](https://discourse.pumas.ai/u/Rahulub3r)\
**Post date:** [October 10, 2023, 1:13pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/7 "2023-10-10T13:13:12Z")

</div>

Hi @MathangiCTM - i don’t believe there’s a way to paginate plots in AoG (although internal Pumas functions, such as `subject_fits` have the option to `paginate`).  
I would suggest to create plots for each ID in a vector and then use the `report` function in Pumas to write the vector of plots to output. The code might looks as follows:

```auto
f_vec = map(unique(df.id)) do id
    @chain df begin
        @rsubset :id == id
        data(_) * mapping(:time, :dv_ipred) * visual(Lines)
        draw(; axis = (title = "ID = $(id)",),)
    end
end

report(f_vec)

```

---

<div class="post-metadata">

**Author:** ![MathangiCTM](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/mathangictm/32/142_2.png) [@MathangiCTM](https://discourse.pumas.ai/u/MathangiCTM)\
**Post date:** [October 12, 2023, 9:41pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/8 "2023-10-12T21:41:14Z")

</div>

Thanks @Rahulub3r  
Any suggestion on how I can add `time vs dv` mapping also in this loop.  
And when I try to run `report(f_vec)` I get this error:

```auto
[ Info: Calculating required `inspect` results.
ERROR: Unknown argument type `AlgebraOfGraphics.FigureGrid`.
Stacktrace:
  [1] error(s::String)
    @ Base .\error.jl:35
  [2] (::PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}})(f::AlgebraOfGraphics.FigureGrid)
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:273
  [3] (::Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}})(#unused#::Nothing, x::AlgebraOfGraphics.FigureGrid)
    @ Base .\tuple.jl:602
  [4] BottomRF
    @ .\reduce.jl:81 [inlined]
  [5] afoldl(op::Base.BottomRF{Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}}, a::Nothing, bs::AlgebraOfGraphics.FigureGrid)
    @ Base .\operators.jl:535
  [6] _foldl_impl(op::Base.BottomRF{Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}}, init::Nothing, itr::Tuple{AlgebraOfGraphics.FigureGrid})
    @ Base .\tuple.jl:329
  [7] foldl_impl(op::Base.BottomRF{Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}}, nt::Nothing, itr::Tuple{AlgebraOfGraphics.FigureGrid})
    @ Base .\reduce.jl:48
  [8] mapfoldl_impl(f::typeof(identity), op::Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}, nt::Nothing, itr::Tuple{AlgebraOfGraphics.FigureGrid})
    @ Base .\reduce.jl:44
  [9] mapfoldl(f::Function, op::Function, itr::Tuple{AlgebraOfGraphics.FigureGrid}; init::Nothing)
    @ Base .\reduce.jl:170
 [10] mapfoldl
    @ .\reduce.jl:170 [inlined]
 [11] #foldl#289
    @ .\reduce.jl:193 [inlined]
 [12] foreach(f::PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}, itr::Tuple{AlgebraOfGraphics.FigureGrid})
    @ Base .\tuple.jl:602
 [13] PumasReports.ModelBundle(args::Tuple{AlgebraOfGraphics.FigureGrid}; inspect::Bool, infer::Bool)
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:274
 [14] PumasReports.ModelBundle(args::AlgebraOfGraphics.FigureGrid; inspect::Bool, infer::Bool)
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:320
 [15] (::PumasReports.var"#14#15"{Base.Pairs{Symbol, Bool, Tuple{Symbol, Symbol}, NamedTuple{(:inspect, :infer), Tuple{Bool, Bool}}}})(::Tuple{Int64, AlgebraOfGraphics.FigureGrid})
    @ PumasReports .\none:0
 [16] iterate
    @ .\generator.jl:47 [inlined]
 [17] collect
    @ .\array.jl:782 [inlined]
 [18] #_named_objects#13
    @ C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:330 [inlined]
 [19] _named_objects
    @ C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:330 [inlined]
 [20] report(fitted_models::Vector{AlgebraOfGraphics.FigureGrid}; output::String, title::String, version::VersionNumber, date::DateTime, author::String, categorical::Vector{Any}, force::Bool, inspect::Bool, infer::Bool, clean::Bool, header::String, footer::String, plot_fontsize::Int64, plot_resolution::Tuple{Int64, Int64})
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:162
 [21] report(fitted_models::Vector{AlgebraOfGraphics.FigureGrid})
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:140

```

---

<div class="post-metadata">

**Author:** ![storopoli](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/storopoli/32/190_2.png) [@storopoli](https://discourse.pumas.ai/u/storopoli)\
**Post date:** [October 13, 2023, 12:33pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/9 "2023-10-13T12:33:36Z")

</div>

> [@MathangiCTM](#):
>
> report(f\_vec)

What happens if you wrap it with a vector?

```julia
report([f_vec])

```

---

<div class="post-metadata">

**Author:** ![MathangiCTM](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/mathangictm/32/142_2.png) [@MathangiCTM](https://discourse.pumas.ai/u/MathangiCTM)\
**Post date:** [October 15, 2023, 10:17pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/10 "2023-10-15T22:17:04Z")

</div>

@storopoli  
`report([f_vec])` returns this error:

```auto
ERROR: Unknown argument type `Vector{AlgebraOfGraphics.FigureGrid}`.
Stacktrace:
  [1] error(s::String)
    @ Base .\error.jl:35
  [2] (::PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}})(f::Vector{AlgebraOfGraphics.FigureGrid})
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:273
  [3] (::Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}})(#unused#::Nothing, x::Vector{AlgebraOfGraphics.FigureGrid})
    @ Base .\tuple.jl:602
  [4] BottomRF
    @ .\reduce.jl:81 [inlined]
  [5] afoldl(op::Base.BottomRF{Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}}, a::Nothing, bs::Vector{AlgebraOfGraphics.FigureGrid})
    @ Base .\operators.jl:535
  [6] _foldl_impl(op::Base.BottomRF{Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}}, init::Nothing, itr::Tuple{Vector{AlgebraOfGraphics.FigureGrid}})
    @ Base .\tuple.jl:329
  [7] foldl_impl(op::Base.BottomRF{Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}}, nt::Nothing, itr::Tuple{Vector{AlgebraOfGraphics.FigureGrid}})
    @ Base .\reduce.jl:48
  [8] mapfoldl_impl(f::typeof(identity), op::Base.var"#59#60"{PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}}, nt::Nothing, itr::Tuple{Vector{AlgebraOfGraphics.FigureGrid}})
    @ Base .\reduce.jl:44
  [9] mapfoldl(f::Function, op::Function, itr::Tuple{Vector{AlgebraOfGraphics.FigureGrid}}; init::Nothing)
    @ Base .\reduce.jl:170
 [10] mapfoldl
    @ .\reduce.jl:170 [inlined]
 [11] #foldl#289
    @ .\reduce.jl:193 [inlined]
 [12] foreach(f::PumasReports.var"#categorise#4"{Vector{Any}, Vector{Any}, Vector{Any}, Vector{Any}}, itr::Tuple{Vector{AlgebraOfGraphics.FigureGrid}})
    @ Base .\tuple.jl:602
 [13] PumasReports.ModelBundle(args::Tuple{Vector{AlgebraOfGraphics.FigureGrid}}; inspect::Bool, infer::Bool)
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:274
 [14] PumasReports.ModelBundle(args::Vector{AlgebraOfGraphics.FigureGrid}; inspect::Bool, infer::Bool)
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:320
 [15] (::PumasReports.var"#14#15"{Base.Pairs{Symbol, Bool, Tuple{Symbol, Symbol}, NamedTuple{(:inspect, :infer), Tuple{Bool, Bool}}}})(::Tuple{Int64, Vector{AlgebraOfGraphics.FigureGrid}})
    @ PumasReports .\none:0
 [16] iterate
    @ .\generator.jl:47 [inlined]
 [17] collect
    @ .\array.jl:782 [inlined]
 [18] #_named_objects#13
    @ C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:330 [inlined]
 [19] _named_objects
    @ C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:330 [inlined]
 [20] report(fitted_models::Vector{Vector{AlgebraOfGraphics.FigureGrid}}; output::String, title::String, version::VersionNumber, date::DateTime, author::String, categorical::Vector{Any}, force::Bool, inspect::Bool, infer::Bool, clean::Bool, header::String, footer::String, plot_fontsize::Int64, plot_resolution::Tuple{Int64, Int64})
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:162
 [21] report(fitted_models::Vector{Vector{AlgebraOfGraphics.FigureGrid}})
    @ PumasReports C:\a\PumasSystemImages\PumasSystemImages\julia_depot\packages\PumasReports\y38l5\src\PumasReports.jl:140
 [22] top-level scope

```

---

<div class="post-metadata">

**Author:** ![vijay](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/vijay/32/96_2.png) [@vijay](https://discourse.pumas.ai/u/vijay)\
**Post date:** [October 15, 2023, 10:38pm UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/11 "2023-10-15T22:38:35Z")

</div>

@MathangiCTM the `report` function in Pumas uses a `Figure` as an input and AoG by default gives a `FigureGrid` here. All you need to do is extract the `Figure` from each element and then create the vector. Here is a full reproducer that you can use.

```julia

# Load the necessary Libraries
using Pumas
using PumasUtilities
using PharmaDatasets
using AlgebraOfGraphics
using DataFramesMeta
using CairoMakie
# Read data
pkdata = dataset("iv_sd_3")

f_vec = map(unique(pkdata.id)) do id
  p = @chain pkdata begin
        dropmissing(:dv)
        @rsubset :id == id
        data(_) * mapping(:time, :dv) * visual(Lines)
        draw(; axis = (title = "ID = $(id)",),)
    end
    p.figure
end

report(f_vec, title = "test")

```

---

<div class="post-metadata">

**Author:** ![MathangiCTM](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.pumas.ai/mathangictm/32/142_2.png) [@MathangiCTM](https://discourse.pumas.ai/u/MathangiCTM)\
**Post date:** [October 16, 2023, 9:04am UTC](https://discourse.pumas.ai/t/how-to-make-multi-panel-plots/541/12 "2023-10-16T09:04:48Z")

</div>

Thank you very much @vijay  
That perfectly worked.
