NIBR data analysis

Hello. I am going through a project systematically to learn Pumas.
This is the dataprep step. As part of the dataprep step, I would like to run the read_nca, read_pumas to ensure data is properly formatted. read_nca runs fine. However I encounter an error with read_pumas. I am unable to decipher the following error. I am providing the code and the data link below. Any help is greatly appreciated.
Bob

###############################
"ERROR: LoadError: MethodError: no method matching preprocess_data(::DataFrame, ::Symbol, ::Symbol, ::Nothing, ::Symbol, ::Bool)
Closest candidates are:
  preprocess_data(::AbstractDataFrame, ::Vector{Symbol}, ::Symbol, ::Union{Nothing, Symbol}, ::Union{Nothing, Symbol}, ::Bool) at /builds/PumasAI/PumasSystemImages-jl/.julia/packages/Pumas/HDuXQ/src/data_parsing/io.jl:1182
Stacktrace:
 [1] read_pumas(df::DataFrame; observations::Symbol, covariates::Vector{Symbol}, id::Symbol, time::Symbol, evid::Symbol, amt::Symbol, addl::Symbol, ii::Symbol, cmt::Symbol, rate::Symbol, ss::Symbol, route::Symbol, mdv::Nothing, event_data::Bool, covariates_direction::Symbol, check::Bool, adjust_evid34::Bool)
   @ Pumas /builds/PumasAI/PumasSystemImages-jl/.julia/packages/Pumas/HDuXQ/src/data_parsing/io.jl:1131
 [2] top-level scope
   @ ~/data/code/practice/nibr/data/dataprep.jl:57"

The code is here:

using Random
using CSV
using Pumas
using PumasUtilities
using Chain
using CategoricalArrays
using StatsBase
using Bioequivalence.GLM: lm, @formula
using DataFramesMeta

############################
#Data specifications
##Column name	Description
##ID	        Unique subject id (numeric)
##TIME	        Time relative to first drug administration
##NOMTIME	Nominal time
##TIMEUNIT	unit of TIME
##AMT	        Dosing amount (for dosing events) in mg (numeric)
##LIDV	        Observation on a linear scale (Observation type determined by CMT),
##EVENTU        Units (numeric)
##CMT	        Compartment number (determines observation type) (integer)
##CMT 1         Dosing event
##CMT 2         PK concentration
##NAME	        description of event
##EVENTU	unit for observation
##CENS	        censored values (0 = not censored, 1 = censored) (integer)
##EVID	        event ID (0 = observation, 1 = dosing event) (integer)
##WEIGHTB	baseline bodyweight (kg)
##SEX	        sex
##TRTACT	Treatment group label (string)
##DOSE	        randomized dose in mg (numeric)

#Data Source    https://opensource.nibr.com/xgx/Data/Single_Ascending_Dose_Dataset2.csv 
#Details        https://opensource.nibr.com/xgx/Datasets.html#single_ascending_dose_dataset2 
###########################


pkdata              = CSV.File("../code/practice/nibr/data/Single_Ascending_Dose_Dataset2.csv"; header=1, missingstring="NA") |> DataFrame
pkdata[!,:ROUTE]   .= "ev"
pkdata[!,:AMT_UG]  .= pkdata.AMT .* 1000

pkdata              = @chain pkdata begin
                        @select :ID :TIME :NOMTIME :AMT_UG :CMT :EVID :LIDV :ROUTE :WEIGHTB :SEX :DOSE
                        @subset :TIME .>= 0
                      end
###### NCA ########
pop_nca   = read_nca(pkdata;
                          observations   =   :LIDV,
                          id             =   :ID,
                          time           =   :TIME,
                          route          =   :ROUTE,
                          amt            =   :AMT_UG,
                          covariates     =   [:WEIGHTB, :SEX, :DOSE]
                    )
###################
pop_nmle  = read_pumas(pkdata;
                          observations   =   :LIDV,
                          id             =   :ID,
                          time           =   :TIME,
                          route          =   :ROUTE,
                          amt            =   :AMT_UG,
                          cmt            =   :CMT,
                          evid           =   :EVID,
                          covariates     =   [:WEIGHTB, :SEX, :DOSE]
                      )

#############

hi Bob -

We have the following heuristic -

  1. in NCA we analyze one dependent variable at a time
  2. in poppk we can analyze more than one dependent variable at a time.

As a consequence of this, the observations = in read_nca accepts a symbol, e.g. observations = :LIDV, wheras in poppk, read_pumas access observations as vector of dependent variables, even if it is just one dependent variable. So, you should change the observations in read_pumas to observations = [:LIDV].

The error message presented to the user could be a little more informative, but does provide the necessary message that read_pumas does not have method preprocess_data(::DataFrame, ::Symbol, ::Symbol, ::Nothing, ::Symbol, ::Bool), instead it is expecting Closest candidates are: preprocess_data(::AbstractDataFrame, ::Vector{Symbol}, ::Symbol, ::Union{Nothing, Symbol} focusing on ::Vector{Symbol} which is expecting a vector of observations.

hope that helps?

Thank you Dr. @vijay

  1. Your suggestion worked.
  2. How to recognize that @select expects variables without commas; and observations or covariates arguments expect variables separated by commas?
  3. For semi-log plot, how to change the yaxis from scientific notation (10E-1) to decimal (0.1).
  4. Is there a way to export the plots as a PDF file? It is easier to review the plots as a separate file.
    Bob
  1. Great
  2. We have a new tutorial series on Data Wrangling being launched this week. In that tutorial series, we will explain the differences.
  3. The default is the use of ln when you use log. you could try using other forms such as log10, or log2, as stated in the documentation Axis
  4. yes, any plot can be saved as follows
    save("path to save plot", plotobject)
    If you are however interested in saving all plots that you are creating in a session, that functionality is coming up.

I am trying to make an interpretable semi-log plot. The yaxis numbers are in a scientific notation format. How can I change them 0.01, 0.1…? The suggestion to use log2 instead did not help. It only changed the base. Thank you.

save() command as suggested above gives the following error.
ERROR: LoadError: UndefVarError: save not defined
Stacktrace:
[1] top-level scope
@ ~/data/code/practice/nibr/data/dataprep.jl:94

Make sure to load

using CairoMakie

Loading this pkg renders a new error.
save("…/code/practice/nibr/data/linear_pk_plots", pk_plots)
#Also tried save("…/code/practice/nibr/data/linear_pk_plots.pdf", pk_plots)

ERROR: LoadError: No applicable_savers found for UNKNOWN
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:33
[2] applicable_savers
@ /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:11 [inlined]
[3] save(file::String, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:128
[4] save(file::String, args::Vector{Figure})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:126
[5] top-level scope
@ ~/data/code/practice/nibr/data/dataprep.jl:110
in expression starting at /home/jrun/data/code/practice/nibr/data/dataprep.jl:110

I’m not sure what’s going on with the saving error - save("…/code/practice/nibr/data/linear_pk_plots.pdf", pk_plots) looks fine to me.

For the number formatting of your y-axis, I think you should be able to supply ytickformat to the axis attribute. A small example:

using CairoMakie
plt = lines(1:10, exp.((1:10) .- 8); axis=(;yscale=log10, ytickformat=x->string.(x)))
save("tmp.pdf", plt)
1 Like

Thank you. It did something, but the yaxis is still unpresentable.

Perhaps one of the younger folks can use the dataset I provided and tweak my code to produce the exact graphs listed in PK - Single Ascending Dose. OR may be we are all corrupted by ggplot.
Bob

I’ve ran all of your code.

Try this, almost similar to @korsbo:

using CairoMakie
observations_vs_time(
               pop_nca;
               axis=(;yscale=log10, ytickformat=x -> string.(round.(x; digits=1)))
           )
save("tmp.pdf", plt)

You can tweak the digits= something if you want more digits.

And there it is:

@storopoli Thank you so much.

  1. Your suggestion allowed me to control the number of sig digits for the yaxis. But its not quite what is considered standard PK plot. See below: I am trying to reproduce this exactly (except for individual profiles). The ribbon is grey, the yaxis ticks are every order of magnitude, there are gridlines, font size of panel label (ribbon) is smaller than the axes labels.

@storopoli save() gives a different error now. I feel very bad @storopoli to keep bugging you all; if there is another resource that I should be reviewing before I shoot off my silly questions, I can save you all some heart burn. What I tried is to add ImageMagic - that did not pan out well either. I received a unresolved error. sorry.

Errors encountered while save FileIO.File{FileIO.DataFormat{:PDF}, String}(β€œtmp.pdf”).
All errors:

ArgumentError: Package ImageMagick [6218d12a-5da1-5696-b52f-db25d2ecc6d1] is required but does not seem to be installed:

  • Run Pkg.instantiate() to install all recorded dependencies.

===========================================
ArgumentError: Argument does not support conversion to pdf.

Fatal error:
ERROR: LoadError: ArgumentError: Package ImageMagick [6218d12a-5da1-5696-b52f-db25d2ecc6d1] is required but does not seem to be installed:

  • Run Pkg.instantiate() to install all recorded dependencies.

Stacktrace:
[1] _require(pkg::Base.PkgId)
@ Base ./loading.jl:990
[2] require(uuidkey::Base.PkgId)
@ Base ./loading.jl:914
[3] #34
@ /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:203 [inlined]
[4] lock(f::FileIO.var"#34#35"{Base.PkgId}, l::ReentrantLock)
@ Base ./lock.jl:187
[5] action(call::Symbol, libraries::Vector{Union{Base.PkgId, Module}}, file::FileIO.Formatted, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:203
[6] action
@ /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:197 [inlined]
[7] action(call::Symbol, libraries::Vector{Union{Base.PkgId, Module}}, sym::Symbol, file::String, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:185
[8] action
@ /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:185 [inlined]
[9] save(file::String, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:129
[10] save(file::String, args::Vector{Figure})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:126
[11] top-level scope
@ ~/data/code/practice/nibr/data/dataprep.jl:100
[12] eval
@ ./boot.jl:360 [inlined]
[13] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
[14] invokelatest(::Any, ::Any, ::Vararg{Any, N} where N; kwargs::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ Base ./essentials.jl:708
[15] invokelatest(::Any, ::Any, ::Vararg{Any, N} where N)
@ Base ./essentials.jl:706
[16] inlineeval(m::Module, code::String, code_line::Int64, code_column::Int64, file::String; softscope::Bool)
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/eval.jl:204
[17] (::VSCodeServer.var"#58#62"{Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})()
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/eval.jl:156
[18] withpath(f::VSCodeServer.var"#58#62"{Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams}, path::String)
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/repl.jl:177
[19] (::VSCodeServer.var"#57#61"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})()
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/eval.jl:154
[20] hideprompt(f::VSCodeServer.var"#57#61"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/repl.jl:36
[21] (::VSCodeServer.var"#56#60"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})()
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/eval.jl:122
[22] with_logstate(f::Function, logstate::Any)
@ Base.CoreLogging ./logging.jl:491
[23] with_logger
@ ./logging.jl:603 [inlined]
[24] (::VSCodeServer.var"#55#59"{VSCodeServer.ReplRunCodeRequestParams})()
@ VSCodeServer ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/eval.jl:194
[25] #invokelatest#2
@ ./essentials.jl:708 [inlined]
[26] invokelatest(::Any)
@ Base ./essentials.jl:706
[27] macro expansion
@ ~/data/.code-server-extensions/julialang.language-julia-1.2.8/scripts/packages/VSCodeServer/src/eval.jl:34 [inlined]
[28] (::VSCodeServer.var"#53#54")()
@ VSCodeServer ./task.jl:411
Stacktrace:
[1] handle_error(e::ArgumentError, q::Base.PkgId, bt::Vector{Union{Ptr{Nothing}, Base.InterpreterIP}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/error_handling.jl:61
[2] handle_exceptions(exceptions::Vector{Tuple{Any, Union{Base.PkgId, Module}, Vector{T} where T}}, action::String)
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/error_handling.jl:56
[3] action(call::Symbol, libraries::Vector{Union{Base.PkgId, Module}}, file::FileIO.Formatted, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:226
[4] action
@ /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:197 [inlined]
[5] action(call::Symbol, libraries::Vector{Union{Base.PkgId, Module}}, sym::Symbol, file::String, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:185
[6] action
@ /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:185 [inlined]
[7] save(file::String, args::Vector{Figure}; options::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:129
[8] save(file::String, args::Vector{Figure})
@ FileIO /builds/PumasAI/PumasSystemImages-jl/.julia/packages/FileIO/JA3Vl/src/loadsave.jl:126
[9] top-level scope
@ ~/data/code/practice/nibr/data/dataprep.jl:100

@bobbrown can you instead try saving as a png file?

save(""…/code/practice/nibr/data/linear_pk_plots.png", pk_plots)

Did not help. Of course there is a very long new error msg - could not even scroll all the way to the top.
LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Combined{Makie.poly, Tuple{Vector{Vector{Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Combined{Makie.poly, Tuple{Vector{Vector{Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Combined{Makie.poly, Tuple{Vector{Vector{Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ LineSegments{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{String}}
β”œ Lines{Tuple{Vector{Point{2, Float32}}}}
β”œ MakieCore.Text{Tuple{Vector{Tuple{AbstractString, Point{2, Float32}}}}}

As you can see we are able to generate the exact plot as you requested, with a lot more customization options.

here is the code that generated the plot you requested based on the exact same dataset that you are trying. Does this come close to what you need?

ot = observations_vs_time(ncapop, 
                                axis = (xlabel = "Time (hours)", 
                                        ylabel = "CTMX Concentration (ΞΌg/mL)",
                                        yscale=log10, ytickformat=x -> string.(round.(x; digits=1)), 
                                        yticks = [0.1, 1, 10],
                                        ygridwidth = 3, 
                                        yminorticksvisible = true,
                                        yminorgridvisible = true,
                                        yminorticks = IntervalsBetween(10),
                                        xminorticksvisible = true,
                                        xminorgridvisible = true,
                                        xminorticks = IntervalsBetween(5),
                                        limits = (nothing, nothing, nothing, 20),
                                        spinewidth = 2),
                                        columns = 4, rows = 3,
                                        facet = ( combinelabels = true,))

Sorry, this was the plot you requested to be replicated without the gray overlay of individual profiles @bobbrown

here is the code

sp = summary_observations_vs_time(ncapop, 
                                  color = :black, linewidth = 2, whiskerwidth = 8,
                                  columns = 5, rows = 1,
                                  axis = (xlabel = "Time (hours)", 
                                            ylabel = "Concentration (ΞΌg/mL)",
                                            yscale=log10, ytickformat=x -> string.(round.(x; digits=1)), 
                                            yticks = [0.1, 1, 10],
                                            ygridwidth = 3, 
                                            yminorgridcolor = :darkgrey,
                                            yminorticksvisible = true,
                                            yminorgridvisible = true,
                                            yminorticks = IntervalsBetween(10),
                                            xminorticksvisible = true,
                                            xminorgridvisible = true,
                                            xminorticks = IntervalsBetween(5),
                                            limits = (nothing, nothing, nothing, 30),
                                            spinewidth = 2),
                                    facet = ( combinelabels = true,),
                                    figure = (resolution = (1800,600),
                                                        fontsize = 22))

here is my code to save a pdf file

julia> save("./example1.pdf", sp)
CairoScreen{Cairo.CairoSurfaceIOStream{UInt32}} with surface:
Cairo.CairoSurfaceIOStream{UInt32}(Ptr{Nothing} @0x000000000b714490, 1350.0, 450.0, IOContext(IOStream(<file /mnt/myvolume/code/example1.pdf>)))

Hi @vijay. Thanks for posting that. Very nice!

The original error message contains save(file::String, args::Vector{Figure}) which indicates that pk_plots is a Vector{Figure} rather than just a single Figure object. Can you try calling save("filename.pdf", pk_plots[1]) and see whether that saves a plot out?

ytickformat can also be set to a β€œformat string” such as ytickformat = "{:.1f}" instead of a formatting function, which is sometimes a better option.

1 Like