Generating data from multivariate distributions

knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7,
                      fig.height = 4.5, dpi = 72, out.width = "100%")

Besides learning from real data, SynthesizerPlus can generate data from fully specified multivariate distributions. This is useful for simulation studies, for teaching, and for benchmarking synthesizers against data with a known dependence structure. All generators accept a seed argument and need no additional packages.

library(SynthesizerPlus)

Correlation matrices

make_corr() builds common correlation structures:

make_corr(4, "ar1", rho = 0.6)
make_corr(3, "exchangeable", rho = 0.3, names = c("x", "y", "z"))
R <- make_corr(5, "random")
round(R, 2)

Classical multivariate distributions

| Function | Distribution | |---|---| | r_mvnorm() | multivariate normal | | r_mvt() | multivariate Student-t | | r_mvlnorm() | multivariate log-normal | | r_mvskewnorm() | multivariate skew-normal (Azzalini & Dalla Valle, 1996) | | r_dirichlet() | Dirichlet (compositional data) | | r_mvmixture() | finite mixture of multivariate normals |

S <- make_corr(3, "ar1", 0.7)
x <- r_mvnorm(2000, mean = c(0, 5, 10), sigma = S, seed = 1)
round(colMeans(x), 2)
round(cor(x), 2)

t3 <- r_mvt(2000, df = 3, sigma = S, seed = 1)
c(normal = max(abs(x[, 1])), t3 = max(abs(t3[, 1])))

The skew-normal adds asymmetry through the shape vector alpha:

sn <- r_mvskewnorm(2000, omega = make_corr(2, "exchangeable", 0.5),
                   alpha = c(8, -3), seed = 1)
plot_pairs(as.data.frame(sn))

Mixtures produce multimodal data, e.g. latent clusters:

mix <- r_mvmixture(1500, weights = c(0.5, 0.3, 0.2),
                   means = list(c(0, 0), c(4, 4), c(-3, 5)),
                   sigmas = list(diag(2), make_corr(2, "exchangeable", 0.8),
                                 diag(c(0.5, 2))),
                   seed = 1)
plot_pairs(as.data.frame(mix))

Compositional data (shares that sum to one):

shares <- r_dirichlet(5, alpha = c(agriculture = 2, industry = 5, services = 10), seed = 1)
round(shares, 3)

Copulas

r_copula() samples from Gaussian, t, Clayton, Gumbel and Frank copulas (uniform margins). Archimedean copulas in more than two dimensions use the Marshall–Olkin frailty construction; their Kendall's tau is a known function of theta:

| Family | Kendall's tau | Tail dependence | |---|---|---| | Clayton | (\theta/(\theta+2)) | lower | | Gumbel | (1 - 1/\theta) | upper | | Frank | (no closed form) | none |

u_c <- r_copula(3000, "clayton", dim = 2, theta = 2, seed = 1)
u_g <- r_copula(3000, "gumbel", dim = 2, theta = 2, seed = 1)
c(clayton = cor(u_c, method = "kendall")[1, 2],
  gumbel = cor(u_g, method = "kendall")[1, 2])

Both have tau = 0.5, but extremes cluster in opposite tails:

both <- rbind(data.frame(u1 = u_c[, 1], u2 = u_c[, 2], copula = "Clayton (theta = 2)"),
              data.frame(u1 = u_g[, 1], u2 = u_g[, 2], copula = "Gumbel (theta = 2)"))
ggplot2::ggplot(both, ggplot2::aes(u1, u2)) +
  ggplot2::geom_point(alpha = 0.25, size = 0.6, colour = "#2a78d6") +
  ggplot2::facet_wrap(~copula) +
  ggplot2::coord_equal() +
  ggplot2::theme_minimal()

Arbitrary margins: r_mvdist()

r_mvdist() couples any copula with any marginal distributions, returning a data frame. Margins can be names of distributions with a q* function, lists of parameters, or quantile functions — including categorical margins from margin_categorical() and empirical margins learned from data with margin_empirical().

sim <- r_mvdist(
  1000,
  margins = list(
    income = list(dist = "lnorm", meanlog = 10, sdlog = 0.6),
    age = list(dist = "unif", min = 18, max = 80),
    children = list(dist = "pois", lambda = 1.5),
    sector = margin_categorical(c("agri", "industry", "services"), c(0.1, 0.3, 0.6)),
    eruption = margin_empirical(faithful$eruptions)
  ),
  copula = "t", corr = make_corr(5, "exchangeable", 0.4), df = 4, seed = 1
)
str(sim)
round(association_matrix(sim), 2)

Such data sets are convenient test beds for synthesizers, because the true dependence is known:

syn <- synthesize(sim, seed = 2)
summary(compare_synthetic(sim, syn, seed = 1))

References

Azzalini, A. and Dalla Valle, A. (1996). The multivariate skew-normal distribution. Biometrika, 83(4), 715–726.

Hofert, M. (2008). Sampling Archimedean copulas. Computational Statistics & Data Analysis, 52(12), 5163–5174.

Marshall, A. W. and Olkin, I. (1988). Families of multivariate distributions. Journal of the American Statistical Association, 83(403), 834–841.



Try the SynthesizerPlus package in your browser

Any scripts or data that you put into this service are public.

SynthesizerPlus documentation built on Oct. 16, 2026, 5:10 p.m.