simulate_genotyping <- function(n_samples = 500, n_variants = 4000, seed = 42) {
set.seed(seed)
# samples: one row per sample
pops <- c("AFR", "AMR", "EAS", "EUR", "SAS")
samples <- data.table(
sample_id = sprintf("S%05d", seq_len(n_samples)),
population = sample(pops, n_samples, replace = TRUE,
prob = c(0.22, 0.14, 0.20, 0.30, 0.14)),
sex = sample(c("M", "F"), n_samples, replace = TRUE),
batch = sample(sprintf("batch%02d", 1:8), n_samples, replace = TRUE),
phenotype = rbinom(n_samples, 1, 0.35) # 1 = case, 0 = control
)
# variants: one row per variant
chroms <- c(1:22, "X")
conseq <- c("synonymous", "missense", "stop_gained", "splice_region", "intron", "UTR")
variants <- data.table(
variant_id = sprintf("rs%07d", seq_len(n_variants)),
chrom = sample(chroms, n_variants, replace = TRUE),
pos = sample(1e6:2.5e8, n_variants, replace = TRUE),
ref = sample(c("A", "C", "G", "T"), n_variants, replace = TRUE),
alt = sample(c("A", "C", "G", "T"), n_variants, replace = TRUE),
gene = sprintf("GENE%04d", sample(1:1500, n_variants, replace = TRUE)),
consequence = sample(conseq, n_variants, replace = TRUE,
prob = c(0.30, 0.25, 0.03, 0.07, 0.25, 0.10)),
maf = round(rbeta(n_variants, 0.6, 6), 4) # skewed toward rare
)
flip <- variants$alt == variants$ref
variants[flip, alt := fifelse(ref == "A", "G", "A")]
# genotypes: one row per sample x variant (the tall table)
geno <- CJ(sample_id = samples$sample_id, variant_id = variants$variant_id)
geno[variants, maf := i.maf, on = "variant_id"]
n <- nrow(geno)
p <- geno$maf
u <- runif(n)
geno[, genotype := fifelse(u < (1 - p)^2, 0L,
fifelse(u < (1 - p)^2 + 2 * p * (1 - p), 1L, 2L))]
geno[runif(n) < 0.02, genotype := -1L] # 2% missing
geno[, read_depth := rpois(n, lambda = 30)]
geno[, genotype_quality := pmin(99L, rpois(n, lambda = 45))]
geno[, maf := NULL]
setcolorder(geno, c("sample_id", "variant_id", "genotype", "read_depth", "genotype_quality"))
list(genotypes = geno[], variants = variants[], samples = samples[])
}
dat <- simulate_genotyping(n_samples = 500, n_variants = 4000, seed = 42)
genotypes <- dat$genotypes
variants <- dat$variants
samples <- dat$samples
# Plain data.frame copies. The base R *and* tidyverse code below both use these,
# so each style is measured on its own native input: base R indexing on a
# data.frame, dplyr verbs on a data.frame, and data.table's `[` on a data.table.
genotypes_df <- as.data.frame(genotypes)
variants_df <- as.data.frame(variants)
samples_df <- as.data.frame(samples)
dim(genotypes) # 500 x 4000 = 2,000,000 rows
format(object.size(genotypes), units = "MB")
head(genotypes, 4)