Converting to/from arrays
Many MCMC libraries return samples in the form of 3D arrays. FlexiChains therefore provides methods to convert from and to 3D arrays, to maximise interoperability with other libraries.
FlexiChains' representation of MCMC samples is richer than a plain array: it includes parameter/extra names, extra information about the structure of each parameter, metadata, and so on. This means that when converting from an array, you need to provide some additional information. Likewise, when converting to an array, some information will inevitably be lost. However, the conversion methods are designed to bridge this gap as far as possible.
FlexiChains works with arrays that have dimensions iters x chains x parameters.
Converting from arrays
You can use a FlexiChain constructor to convert a 3D array to a FlexiChain. It is easiest to explain by example (the docstring, which has all the necessary detail, is at the bottom of this page). Here is a simple example where each column in the array corresponds to a scalar parameter:
using FlexiChains: FlexiChain, Parameter, Extra
arr = rand(10, 4, 3) # 10 iterations, 4 chains, 3 parameters
names = (Parameter(:x), Parameter(:y), Extra(:lp)) # note: must be tuple
FlexiChain{Symbol}(arr, names)╭─FlexiChain (10 iterations, 4 chains) ────────────────────────────────────────╮
│ ↓ iter = 1:10 │
│ → chain = 1:4 │
│ │
│ Parameters (2) ── Symbol │
│ Float64 x, y │
│ │
│ Extras (1) │
│ Float64 lp │
╰──────────────────────────────────────────────────────────────────────────────╯For convenience, this constructor also allows you to specify parameters that are array-valued, by specifying their sizes. In this example, the last four columns of arr will be interpreted as a single parameter z that is a 2x2 matrix:
arr = rand(10, 4, 6) # 10 iterations, 4 chains, 6 (scalar) parameters
names = (
Parameter(:x), # Scalar
Parameter(:y), # Scalar
Parameter(:z) => (2, 2), # Matrix
)
chn = FlexiChain{Symbol}(arr, names)╭─FlexiChain (10 iterations, 4 chains) ────────────────────────────────────────╮
│ ↓ iter = 1:10 │
│ → chain = 1:4 │
│ │
│ Parameters (3) ── Symbol │
│ Float64 x, y │
│ Matrix{Float64} z (2, 2) │
│ │
│ Extras (0) │
│ (none) │
╰──────────────────────────────────────────────────────────────────────────────╯You can see the reshaped parameter z here
chn[:z, iter=1, chain=1]2×2 Matrix{Float64}:
0.0304574 0.0891564
0.97052 0.337392is derived from
arr[1, 1, 3:6]4-element Vector{Float64}:
0.03045736238552821
0.9705202634091659
0.08915638592099817
0.33739160181260175Converting to arrays
When flattening a FlexiChain to an array, the structure of each parameter is lost: array-valued parameters, for example, are flattened to consecutive columns.
To retain parameter names, you can convert to a DimArray, which will have a :param dimension that stores the names:
using FlexiChains: DimArray
# Create a chain from an array, much like above.
arr = randn(10, 4, 6) # 10 iterations, 4 chains, 6 parameters
chn = FlexiChain{Symbol}(arr, (
Parameter(:x), # Scalar
Parameter(:y), # Scalar
Parameter(:z) => (2, 2), # Matrix
))
# Convert it back to an array.
da = DimArray(chn)┌ 10×4×6 DimArray{Float64, 3} ┐
├─────────────────────────────┴─────────────────────────────── dims ┐
↓ iter Sampled{Int64} 1:10 ForwardOrdered Regular Points,
→ chain Sampled{Int64} 1:4 ForwardOrdered Regular Points,
↗ param Categorical{Symbol} [:x, …, Symbol("z[2, 2]")] Unordered
└───────────────────────────────────────────────────────────────────┘
[:, :, 1]
↓ → 1 2 3 4
1 -0.93057 0.454146 0.209709 -0.624576
2 -1.61122 -0.356126 1.9816 -1.37569
3 1.1473 -0.561642 0.302883 0.17872
⋮
7 1.76495 -0.00921678 -0.862957 0.654674
8 0.57371 0.377983 -1.63554 0.0445849
9 1.05506 3.25213 -1.34106 1.2526
10 -1.10347 -1.13215 -0.677702 -0.325783This should recover the data in the original array:
da == arrtrueIf the parameter names are not needed, you can convert to a plain Array as well.
Array(chn)10×4×6 Array{Float64, 3}:
[:, :, 1] =
-0.93057 0.454146 0.209709 -0.624576
-1.61122 -0.356126 1.9816 -1.37569
1.1473 -0.561642 0.302883 0.17872
-1.28338 -2.60106 -0.464125 -1.5026
0.377899 -0.893374 -0.582755 -0.59483
0.146197 1.34406 0.519275 1.65902
1.76495 -0.00921678 -0.862957 0.654674
0.57371 0.377983 -1.63554 0.0445849
1.05506 3.25213 -1.34106 1.2526
-1.10347 -1.13215 -0.677702 -0.325783
[:, :, 2] =
-0.837929 -0.351329 0.425574 0.725059
-0.749622 0.819754 -0.337751 -0.391817
0.0263175 -0.637177 -0.0154375 -1.16879
1.27739 -0.776257 -0.115863 0.71849
0.19923 2.35114 0.159815 -0.367267
1.12749 -1.46552 -0.246817 0.582407
0.348526 -0.125338 1.55074 0.520806
-0.206725 1.08126 1.38046 -0.137353
-0.221475 0.590854 1.03675 -0.574869
-0.367688 0.432868 -0.777105 1.28777
[:, :, 3] =
1.18533 -0.676257 -1.77263 -0.883844
-0.728832 1.10477 -1.24142 -0.297441
1.16863 -0.270356 -0.0544595 0.749629
-0.1363 -0.671751 -1.96545 0.17361
0.381196 1.14852 -0.0930028 2.64443
-1.27214 -0.0692261 -0.212654 0.487966
0.513662 0.206267 0.494501 -1.25653
0.874679 -2.30304 2.12684 -0.0732078
-0.306215 -1.57314 2.24674 1.16194
-1.02222 0.821988 -2.90471 1.35062
[:, :, 4] =
-1.07603 -0.443693 0.113618 1.11489
0.805753 -1.77454 1.13219 1.03837
-0.13291 -0.251584 0.558701 -0.77103
-0.146865 0.237384 0.661887 0.132487
-0.591554 -0.364557 -0.0735388 -2.02246
0.238571 0.817726 0.422437 0.068275
0.932657 0.451488 0.65693 -1.38375
-0.032301 1.31516 1.10286 0.140142
-0.101417 -1.02677 -1.02349 -0.234308
0.574205 -0.225116 0.260113 0.459645
[:, :, 5] =
1.5166 1.55023 1.28047 -1.00364
0.550747 0.213433 0.22617 -0.233766
0.35864 -0.879332 -0.183855 -0.0179167
-1.36815 -0.874273 -1.65386 1.12702
-0.635456 0.0398833 -0.00735498 0.865219
-0.30324 0.331783 -0.841369 0.0132944
-1.07926 -0.31209 0.773172 -1.08439
-1.03672 0.63551 -0.683979 -0.544128
0.305569 -1.40717 0.618581 -1.39204
-0.43583 0.122502 -0.585825 -1.45137
[:, :, 6] =
-1.90072 -1.15179 0.11611 0.66412
0.992383 0.245467 0.80996 0.451921
0.484779 0.113891 -1.41486 -1.80406
-0.812059 -1.45825 -0.864962 1.58748
1.08226 -1.14328 -2.41857 1.18867
-0.680932 0.42332 0.900629 0.22542
-0.2276 -1.18436 0.303536 0.238211
-1.57724 -0.241012 0.221378 -0.4486
1.9791 0.101183 1.50223 -1.16815
-0.952357 0.452725 -1.17619 -0.128513Summaries
FlexiSummary objects can also be converted to arrays: depending on how many of their dimensions were collapsed, the resulting array will have different dimensions. However, there is presently no way to convert an array into a FlexiSummary.
using FlexiChains: summarystats
fs = summarystats(chn)
da = DimArray(fs)┌ 9×6 DimArray{Float64, 2} ┐
├──────────────────────────┴────────────────────────────────── dims ┐
↓ stat Categorical{Symbol} [:mean, …, :q95] Unordered,
→ param Categorical{Symbol} [:x, …, Symbol("z[2, 2]")] Unordered
└───────────────────────────────────────────────────────────────────┘
↓ → :x :y … Symbol("z[2, 2]")
:mean -0.0793336 0.169264 -0.166718
:std 1.18193 0.821347 1.05553
:mcse 0.201618 0.102602 0.131856
:ess_bulk 33.4133 58.3697 64.0824
:ess_tail 51.0067 51.0067 … 47.7987
:rhat 1.00453 0.980287 0.982158
:q5 -1.61244 -0.854473 -1.80889
:q50 -0.1675 0.00544 0.107537
:q95 1.77578 1.38898 1.50649Docstrings
FlexiChains.FlexiChain Method
FlexiChain{TKey}(
arr::AbstractArray{T,3},
key_spec::Union{TKey,Tuple};
iter_indices = 1:size(arr, 1),
chain_indices = 1:size(arr, 2),
sampling_time = fill(missing, size(arr, 2)),
last_sampler_state = fill(missing, size(arr, 2)),
) where {T}Construct a FlexiChain from a 3D array with dimensions (iters, chains, params).
Key specification
The key_spec argument can be a single parameter::TKey, in which case the resulting FlexiChain will have a single vector-valued parameter with that name.
Alternatively, you can pass a tuple which specifies how columns in the third dimension are mapped to Parameter or Extra keys. Columns are consumed left-to-right. Each element of key_spec must either be:
key::ParameterOrExtra{<:TKey}: consumes one column and stores it as a scalar value with the givenkey; orkey::ParameterOrExtra => (dims...): consumesprod(dims)columns, reshapes each sample todims, and stores it with the givenkey.
The total number of columns consumed must equal size(arr, 3).
Metadata
iter_indices and chain_indices can be used to specify the iteration and chain indices, respectively. By default, these are 1:niters and 1:nchains, but can be any vector of integers of the appropriate length. sampling_time and last_sampler_state are used to store metadata about each chain. They should be given as vectors of length nchains (even if there is only one chain).
Example usage
using FlexiChains: FlexiChain, Parameter, Extra
arr = randn(100, 4, 6) # iters x chains x params
# `x` and `lp` are scalars; but `z` is a 2x2 matrix, so it consumes 4 columns.
chn = FlexiChain{Symbol}(arr, (Parameter(:x), Parameter(:z) => (2,2), Extra(:lp)))DimensionalData.DimArray Method
DimensionalData.DimArray(
chain::FlexiChain{TKey};
warn::Bool=true,
eltype_filter=Any,
parameters_only::Bool=true,
split_varnames::Bool=true,
) where {TKey}Convert a FlexiChain into a 3-dimensional DimArray with dimensions (:iter, :chain, :param).
This proceeds by first splitting array-valued parameters into scalar leaves (if split_varnames is true), then extracting all parameters whose element type subtypes eltype_filter and stacking them into a 3D array.
Keys whose values do not subtype eltype_filter are skipped (with a warning if warn=true).
If parameters_only=true (the default), then two things happen:
Only parameters (not extras) are included in the
DimArray. Otherwise, both parameters and extras are included.The keys in the
:paramdimension of the resultingDimArrayare justTKey, i.e., theParameterwrapper is removed. Otherwise, the keys will beUnion{Parameter{<:TKey},Extra}.
DimensionalData.DimArray(
summary::FlexiSummary{TKey};
warn::Bool=true,
eltype_filter=Any,
parameters_only::Bool=true,
split_varnames::Bool=true,
) where {TKey}Convert a FlexiSummary into a DimArray with a :param dimension appended after the non-collapsed dimensions of the summary. For example:
| Summary produced via | Dimensions of resulting DimArray |
|---|---|
mean(chn) | (:param) |
mean(chn; dims=:iter) | (:chain, :param) |
mean(chn; dims=:chain) | (:iter, :param) |
summarystats(chn) | (:stat, :param) |
collapse(chn, [mean, std]; dims=:iter) | (:chain, :stat, :param) |
Keyword arguments
split_varnames::Bool=true: whether to split array-valued statistics into scalar leaves. Iftrue, then array-valued parameters are split into scalar leaves, e.g. a vector-valued statistic forxwould be split intox[1],x[2], etc. Note: FlexiChains has no way of knowing whether the vector elements correspond to different parts of the parameter (e.g., the mean ofx[i]for a vector-valued parameterx) or whether they correspond to different parts of the same statistic (e.g., when calculatingquantilewith multiple probabilities, for a scalar parameterxthe result will be a vector). Whensplit_varnames=trueFlexiChains will always assume the former. If you need the latter behaviour you should setsplit_varnames=falseand then manually perform any data processing you need to do.eltype_filter::Any: retain only parameters whose values subtypeeltype_filter. For example, ifeltype_filter=Float64, then integer-valued parameters are dropped.parameters_only::Bool=true: whether to include only parameters (not extras) in the resultingDimArray.warn::Bool=true: whether to issue a warning if any keys are skipped due to their values not subtypingeltype_filter.
Core.Array Method
Base.Array(
chain::FlexiChain;
kwargs...
)Convert a FlexiChain into a standard Array with dimensions (iter, chain, param). This is the same as the conversion to DimensionalData.DimArray, except that the dimension metadata is discarded.
See DimensionalData.DimArray(::FlexiChains.FlexiChain) for more details on the conversion process and available keyword arguments.
Base.Array(
summary::FlexiSummary;
kwargs...
)Convert a FlexiSummary into a standard Array. This is the same as the conversion to DimensionalData.DimArray, except that the dimension metadata is discarded.
See DimensionalData.DimArray(::FlexiChains.FlexiSummary) for details.
DimensionalData.DimArray Method
DimensionalData.DimArray(
chain::FlexiChain{TKey};
warn::Bool=true,
eltype_filter=Any,
parameters_only::Bool=true,
split_varnames::Bool=true,
) where {TKey}Convert a FlexiChain into a 3-dimensional DimArray with dimensions (:iter, :chain, :param).
This proceeds by first splitting array-valued parameters into scalar leaves (if split_varnames is true), then extracting all parameters whose element type subtypes eltype_filter and stacking them into a 3D array.
Keys whose values do not subtype eltype_filter are skipped (with a warning if warn=true).
If parameters_only=true (the default), then two things happen:
Only parameters (not extras) are included in the
DimArray. Otherwise, both parameters and extras are included.The keys in the
:paramdimension of the resultingDimArrayare justTKey, i.e., theParameterwrapper is removed. Otherwise, the keys will beUnion{Parameter{<:TKey},Extra}.
DimensionalData.DimArray(
summary::FlexiSummary{TKey};
warn::Bool=true,
eltype_filter=Any,
parameters_only::Bool=true,
split_varnames::Bool=true,
) where {TKey}Convert a FlexiSummary into a DimArray with a :param dimension appended after the non-collapsed dimensions of the summary. For example:
| Summary produced via | Dimensions of resulting DimArray |
|---|---|
mean(chn) | (:param) |
mean(chn; dims=:iter) | (:chain, :param) |
mean(chn; dims=:chain) | (:iter, :param) |
summarystats(chn) | (:stat, :param) |
collapse(chn, [mean, std]; dims=:iter) | (:chain, :stat, :param) |
Keyword arguments
split_varnames::Bool=true: whether to split array-valued statistics into scalar leaves. Iftrue, then array-valued parameters are split into scalar leaves, e.g. a vector-valued statistic forxwould be split intox[1],x[2], etc. Note: FlexiChains has no way of knowing whether the vector elements correspond to different parts of the parameter (e.g., the mean ofx[i]for a vector-valued parameterx) or whether they correspond to different parts of the same statistic (e.g., when calculatingquantilewith multiple probabilities, for a scalar parameterxthe result will be a vector). Whensplit_varnames=trueFlexiChains will always assume the former. If you need the latter behaviour you should setsplit_varnames=falseand then manually perform any data processing you need to do.eltype_filter::Any: retain only parameters whose values subtypeeltype_filter. For example, ifeltype_filter=Float64, then integer-valued parameters are dropped.parameters_only::Bool=true: whether to include only parameters (not extras) in the resultingDimArray.warn::Bool=true: whether to issue a warning if any keys are skipped due to their values not subtypingeltype_filter.
Core.Array Method
Base.Array(
chain::FlexiChain;
kwargs...
)Convert a FlexiChain into a standard Array with dimensions (iter, chain, param). This is the same as the conversion to DimensionalData.DimArray, except that the dimension metadata is discarded.
See DimensionalData.DimArray(::FlexiChains.FlexiChain) for more details on the conversion process and available keyword arguments.
Base.Array(
summary::FlexiSummary;
kwargs...
)Convert a FlexiSummary into a standard Array. This is the same as the conversion to DimensionalData.DimArray, except that the dimension metadata is discarded.
See DimensionalData.DimArray(::FlexiChains.FlexiSummary) for details.