---
title: "Generating data from multivariate distributions"
output:
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Generating data from multivariate distributions}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
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.

```{r}
library(SynthesizerPlus)
```

## Correlation matrices

`make_corr()` builds common correlation structures:

```{r}
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 |

```{r}
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`:

```{r}
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:

```{r}
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):

```{r}
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 |

```{r}
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:

```{r}
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()`.

```{r}
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:

```{r}
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.
