summaryrefslogtreecommitdiff
path: root/R/simulate_data.R
blob: 23388056ef7aff467e54922e747c6c4fba159f7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#' Simulate Multivariate Normal Data
#'
#' Given a set of parameters controlling the properties
#' of the true covariance matrix, simulate data
#' with the intention of evaluating the performance
#' of covariance or correlation matrix estimators.
#'
#' @param n Number of observations to generate.
#' @param p Number of features to simulate.
#' @param rho The AR(1) decay parameter (default 0.8). Setting rho = 0 uses the identity matrix as the true covariance (0 <= rho < 1).
#' @param contamination The proportions of rows to contaminate as outlier observations (default 0, 0 <= contamination <= 1).
#' @param block_fraction The fraction of the covariance matrix to form as AR(1) structure (default 0.5, 0 < block_fraction <= 1).
#' @param t_df The degrees of freedom to use for scaling observations into heavy tails (default Inf, 0 <= t_df < 2).
#'
#' @return A list with X, S and a vector identifying the contaminated rows.
#'
#' @examples
#' X_obj <- generate_data(n = 10, p = 10)
#' X <- X_obj$X
#' S_true <- X_obj$S
#'
#' @importFrom MASS mvrnorm
#'
#' @export
simulate_data <- function(
	n,
	p,
	rho = 0.8,
	contamination = 0,
	block_fraction = 0.5,
	t_df = Inf,
	exact_contamination = FALSE
){
	if(any(
		rho < 0,
		rho >= 1,
		contamination < 0,
		contamination > 1,
		block_fraction <= 0,
		block_fraction > 1,
		t_df <= 2
	)){
		stop('Incompatible settings')
	}

	# True correlation matrix
	Sigma <- diag(p)
	if(rho > 0){
		b <- floor(block_fraction * p)
		if(b >= 2){
			idx <- seq_len(b)
			Sigma[idx, idx] <- rho ^ abs(outer(idx, idx, `-`))
		}
	}

	# Regular clean observations
	Z <- MASS::mvrnorm(n = n, mu = numeric(p), Sigma = Sigma)
	if(is.finite(t_df)){
		w <- rchisq(n, df = t_df)
		X <- Z / sqrt(w / t_df)
	}else{
		X <- Z
	}

	# Contamination - by row
	outlier_rows <- integer(0)
	if(contamination > 0){
		if(exact_contamination){
			outlier_rows <- sample.int(n, ceiling(contamination * n))
		}else{
			outlier_rows <- which(runif(n) < contamination)
		}

		if(length(outlier_rows) > 0){
			X[outlier_rows, ] <- matrix(
				rchisq(length(outlier_rows) * p, df = 1),
				nrow = length(outlier_rows),
				ncol = p
			)
		}
	}
	list(
		X = X,
		S = Sigma,
		contaminated = sort(outlier_rows)
	)
}