Results and Simulations
Why OneDimGrid
objects?
The model generator requires a OneDimGrid
objects from AdaptiveDensityApproximation.jl
(see Models). For this, the actual weights of the grid do not matter. The model generator returns a new parameter array and a corresponding model function. It is possible to set parameter values in this array and to simulate dose-response curves with the corresponding, returned model function.
But AdaptiveDensityApproximation.jl
offers additional methods, e.g. the name-giving adaptive approximation of densities or additional density conversion / analysis tools. For this reason, internal methods use OneDimGrid
objects and construct the model functions from scratch for the calculation of dose-response curves. This is, among others, the reason why most functions (e.g. the convenience functions of AntibodyMethodsDoseResponseConvenience
) return or expect those grids.
Although not necessary for all analyses, it is highly recommended to use the OneDimGrid
workflow, as advanced methods of this package will expect grids.
Simulating the $K_\tau$-density
To simulate a dose-response curve with a binding model, a density $g(K_\tau)$ needs to be defined. This can be done with any Julia function, e.g. a probability density function:
using AntibodyMethodsDoseResponseConvenience
using Distributions
p(x) = pdf(Normal(1e-5,3e-6),x)
plot(p, xaxis = :log, xlims = [1e-10,1e-2], fill = 0, label = "density")
The plot
function can be used without importing Plots.jl
here, as AntibodyMethodsDoseResponseConvenience
does this in the background automatically.
Next, a OndDimGrid
needs to be defined to approximate the density:
grid = create_grid(LogRange(1e-10,1e-2,50))
approximate_density!(grid, p, volume_normalization = true)
AdaptiveDensityApproximation.OneDimGrid{Dict{String, AdaptiveDensityApproximation.OneDimBlock}}(Dict{String, AdaptiveDensityApproximation.OneDimBlock}("Py0BNchFQf" => AdaptiveDensityApproximation.OneDimBlock("Py0BNchFQf", AdaptiveDensityApproximation.Interval{Float64, Float64}(5.428675439323859e-6, 7.906043210907702e-6), ["jfTeBXDoEg", "8z4dGbONb5"], 0.17774921175278285), "Q8fAIep6Jc" => AdaptiveDensityApproximation.OneDimBlock("Q8fAIep6Jc", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.088843596477472e-10, 4.4984326689694345e-10), ["AYTiIryE1y", "cmxkzIZuRR"], 7.249653871503817e-8), "y2TGbJxgAu" => AdaptiveDensityApproximation.OneDimBlock("y2TGbJxgAu", AdaptiveDensityApproximation.Interval{Float64, Float64}(6.250551925273976e-9, 9.102981779915227e-9), ["AQyHI5Vwg7", "0xFgvaA3oG"], 1.4789710064812532e-6), "8z4dGbONb5" => AdaptiveDensityApproximation.OneDimBlock("8z4dGbONb5", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.906043210907702e-6, 1.1513953993264481e-5), ["Py0BNchFQf", "4osC0nsuWv"], 0.47754627394444393), "t6ewiPuZZp" => AdaptiveDensityApproximation.OneDimBlock("t6ewiPuZZp", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.543120063354622e-5, 0.00010985411419875583), ["ZIIh34wveR", "n8zp8Ukqm8"], 7.49239388000171e-165), "1biI23x2xg" => AdaptiveDensityApproximation.OneDimBlock("1biI23x2xg", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.5564803062231284e-5, 5.1794746792312233e-5), ["LUpqOc7Q4V", "ZIIh34wveR"], 9.239703011997424e-28), "n4okjrhlJN" => AdaptiveDensityApproximation.OneDimBlock("n4okjrhlJN", AdaptiveDensityApproximation.Interval{Float64, Float64}(1.6768329368110066e-5, 2.4420530945486548e-5), ["4osC0nsuWv", "LUpqOc7Q4V"], 0.0019927865651641007), "AQyHI5Vwg7" => AdaptiveDensityApproximation.OneDimBlock("AQyHI5Vwg7", AdaptiveDensityApproximation.Interval{Float64, Float64}(4.291934260128778e-9, 6.250551925273976e-9), ["rQqe6GYIwS", "y2TGbJxgAu"], 1.0128247873519783e-6), "UeJO6UXx60" => AdaptiveDensityApproximation.OneDimBlock("UeJO6UXx60", AdaptiveDensityApproximation.Interval{Float64, Float64}(0.0004941713361323844, 0.0007196856730011522), ["JgJ6VHiAEr", "yxnatgV1ZU"], 0.0), "m5iatcC06R" => AdaptiveDensityApproximation.OneDimBlock("m5iatcC06R", AdaptiveDensityApproximation.Interval{Float64, Float64}(2.8117686979742307e-8, 4.094915062380419e-8), ["ooC6C4AHo7", "lAgRxvt15w"], 6.854143539396169e-6)…))
Without volume_normalization = true
(the default), the density function itself is approximated (e.g. by using the function values at the centers of the respective intervals). The volume normalization approximates the area under the curve by using function value × interval length
, which in this case corresponds to the number of epitopes with $K_\tau$ in the respective interval. This is the correct choice for simulations, as the weights of grids are assumed ot be the numbers of epitopes and not the density values.
To inspect the grid, the plotting recipe from AdaptiveDensityApproximationRecipes.jl
can be used (as described in Models):
using AdaptiveDensityApproximationRecipes
plot(grid, xaxis = :log)
Since the grid approximates the area under the density curve (weights correspond to the height of the bars), the scale of the $y$-axis is different, compared to the density plot.
Simulating the dose-response curve
With the OneDimeGrid
object grid
, a dose-response can be simulated, using DoseResponseResult
:
concentrations = LogRange(1e-8,1e-2,16)
simulation_result = DoseResponseResult(grid,concentrations;
model = accumulation_model,
offset = 0
)
scatter(simulation_result, xaxis = :log)
DoseResponseResult
objects are used to construct and store dose-response curves, resulting from grids. This can be either because a grid was used to simulate a $K_\tau$-density or because the result of a model-fit was stored in a grid. The package AntibodyMethodsDoseResponseRecipes.jl
provides a plotting recipe for DoseResponseResult
objects.
The same filter_zeros
keyword that is used for FittingData
plots can also be used for DoseResponseResult
plots (see Measurement Data plotting).
Importing fitting results
Instead of simulating a dose-response curve, the OneDimGrid
and DoseResponseResult
types can also be used to create dose-response curves from a fitting result. Since no data has been fitted yet, we construct a result from the weights of the simulation grid above:
parameters = export_weights(grid)
push!(parameters, 0.2) # Add offset parameter
50-element Vector{Float64}:
2.346375744346489e-8
3.417353548949335e-8
4.977309020637278e-8
7.249653871503817e-8
1.0560053397755286e-7
1.5383426307070923e-7
2.2412770711543273e-7
3.2660201444611645e-7
4.76058019338558e-7
6.941803541492311e-7
⋮
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.2
We assume now, that the gird
was used to obtain the model function and that the parameters
are the result of the model fit. In this case, the offset
keyword was used. To import the result into the grid, the import_weights!
functions can be used:
import_weights!(grid, parameters[1:end-1])
AdaptiveDensityApproximation.OneDimGrid{Dict{String, AdaptiveDensityApproximation.OneDimBlock}}(Dict{String, AdaptiveDensityApproximation.OneDimBlock}("Py0BNchFQf" => AdaptiveDensityApproximation.OneDimBlock("Py0BNchFQf", AdaptiveDensityApproximation.Interval{Float64, Float64}(5.428675439323859e-6, 7.906043210907702e-6), ["jfTeBXDoEg", "8z4dGbONb5"], 0.17774921175278285), "Q8fAIep6Jc" => AdaptiveDensityApproximation.OneDimBlock("Q8fAIep6Jc", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.088843596477472e-10, 4.4984326689694345e-10), ["AYTiIryE1y", "cmxkzIZuRR"], 7.249653871503817e-8), "y2TGbJxgAu" => AdaptiveDensityApproximation.OneDimBlock("y2TGbJxgAu", AdaptiveDensityApproximation.Interval{Float64, Float64}(6.250551925273976e-9, 9.102981779915227e-9), ["AQyHI5Vwg7", "0xFgvaA3oG"], 1.4789710064812532e-6), "8z4dGbONb5" => AdaptiveDensityApproximation.OneDimBlock("8z4dGbONb5", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.906043210907702e-6, 1.1513953993264481e-5), ["Py0BNchFQf", "4osC0nsuWv"], 0.47754627394444393), "t6ewiPuZZp" => AdaptiveDensityApproximation.OneDimBlock("t6ewiPuZZp", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.543120063354622e-5, 0.00010985411419875583), ["ZIIh34wveR", "n8zp8Ukqm8"], 7.49239388000171e-165), "1biI23x2xg" => AdaptiveDensityApproximation.OneDimBlock("1biI23x2xg", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.5564803062231284e-5, 5.1794746792312233e-5), ["LUpqOc7Q4V", "ZIIh34wveR"], 9.239703011997424e-28), "n4okjrhlJN" => AdaptiveDensityApproximation.OneDimBlock("n4okjrhlJN", AdaptiveDensityApproximation.Interval{Float64, Float64}(1.6768329368110066e-5, 2.4420530945486548e-5), ["4osC0nsuWv", "LUpqOc7Q4V"], 0.0019927865651641007), "AQyHI5Vwg7" => AdaptiveDensityApproximation.OneDimBlock("AQyHI5Vwg7", AdaptiveDensityApproximation.Interval{Float64, Float64}(4.291934260128778e-9, 6.250551925273976e-9), ["rQqe6GYIwS", "y2TGbJxgAu"], 1.0128247873519783e-6), "UeJO6UXx60" => AdaptiveDensityApproximation.OneDimBlock("UeJO6UXx60", AdaptiveDensityApproximation.Interval{Float64, Float64}(0.0004941713361323844, 0.0007196856730011522), ["JgJ6VHiAEr", "yxnatgV1ZU"], 0.0), "m5iatcC06R" => AdaptiveDensityApproximation.OneDimBlock("m5iatcC06R", AdaptiveDensityApproximation.Interval{Float64, Float64}(2.8117686979742307e-8, 4.094915062380419e-8), ["ooC6C4AHo7", "lAgRxvt15w"], 6.854143539396169e-6)…))
Since the offset
keyword was used for the creation of model functions (in our scenario), the parameters
array has one additional element at the end, the offset, which does not belong to the grid. If no offset
is used, all parameters should be imported:
import_weights!(grid, parameters)
In fact, using the wrong selection of parameters usually leads to a DimensionMismatch
error, as the number of parameters and the number of intervals in the grid do not match.
julia> import_weights!(grid, parameters) # Wrong parameter selection should fail.
ERROR: DimensionMismatch: The number of blocks in the grid and the number of weights to be imported do not match
After importing the parameters
into the grid, the dose-response curve can be constructed as before (for any concentrations, not only the data concentrations):
concentrations = LogRange(1e-8,1e-2,16)
result = DoseResponseResult(grid,concentrations;
model = accumulation_model,
offset = parameters[end]
)
scatter(result, xaxis = :log, ylims = [0,1.4])
Observe the offset = parameters[end]
part at the end of the DoseResponseResult
call. In our scenario, the parameters
array contains the offset as last element.