Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Combine studies

Cohorts of one disease ask the same question of different donors, and each answers it with its own samples, genes and names of cell types. To answer it with all of them, align the cohorts’ cubes in a CubeCollection, analyse each cohort on its own, and combine the results gene by gene by meta-analysis, with the forest plots and the heterogeneity that metafor reports Viechtbauer, 2010, or stack the cohorts into one cube for a joint model. Meta-analysis gives the statistics, and Replication across studies asks which changes replicate.

The cohorts are simulated, so that the truth is known: the simulated_cohorts dataset of the Datasets page, 164 donors in six case-control cohorts of 12 to 60 donors, with the pseudobulk counts of four blood cell types over 2,000 genes. The disease changes 100 genes of the monocytes in every cohort, each case by an activity of its own, and fewer genes of the CD4 T cells and B cells; each cohort also changes 10 monocyte genes of its own, in every case alike, as a confounder of that cohort would. python docs/datasets.py prepare simulated_cohorts writes a cube file for every cohort, and the code below opens them by name.

CytePool 0.1.dev55+g941b81fb5, numpy 2.5.4
cohort_1: 60 donors (30 cases), 2,000 genes
    B cells, CD4 T cells, NK cells, CD14 monocytes
cohort_2: 32 donors (16 cases), 2,000 genes
    B, CD4, NK, CD14 Mono
cohort_3: 24 donors (12 cases), 1,800 genes
    B cell, CD4+ T cell, NK cell, CD14+ monocyte
cohort_4: 16 donors (8 cases), 2,000 genes
    B, CD4 naive, CD4 memory, NK, Mono CD14
cohort_5: 12 donors (6 cases), 2,000 genes (1,850 measured)
    Bcell, T_CD4, NK, Mono_CD14
cohort_6: 20 donors (10 cases), 2,000 genes
    B cells, CD4+ T cells, CD14+ monocytes

Align the cohorts

The cubes cannot be compared as they are. Every cohort numbers its donors D01, D02, ..., so donor D01 of one cohort is not donor D01 of another. cohort_3’s annotation lacks 200 genes, and cohort_5’s exporter wrote zeros for 150 genes it did not measure, which its feature column measured marks: a zero there is not a count. And every cohort names its cell types in its own words.

CubeCollection aligns them. It names every sample study::sample_id, aligns the genes by feature_id and records which cohort measured which (CubeCollection.measured), and aligns the cell types by their Cell Ontology ids Diehl et al., 2016, which every cube’s group table holds in cell_ontology_id: CL:0000236 (B cell), CL:0000624 (CD4-positive, alpha-beta T cell), CL:0000623 (natural killer cell) and CL:0001054 (CD14-positive monocyte). cohort_4 annotated its CD4 T cells more finely, as naive (CL:0000895) and memory (CL:0000897) cells, which the ontology places under CL:0000624; group_mapping rolls them up to it, and the collection merges the two, exactly, since a cube’s layers are sums (The cube and its additive layers).

CubeCollection of 6 studies: 164 samples, 4 groups, 2,000 features
  groups aligned by 'cell_ontology_id'
  cohort_1: 60 samples, 4 groups, 2,000 features measured
  cohort_2: 32 samples, 4 groups, 2,000 features measured
  cohort_3: 24 samples, 4 groups, 1,800 features measured
  cohort_4: 16 samples, 4 groups (1 merged), 2,000 features measured
  cohort_5: 12 samples, 4 groups, 1,850 features measured
  cohort_6: 20 samples, 3 groups, 2,000 features measured
  in every study: 3 groups, 1,650 features
  samples: cohort_1::D01, cohort_1::D02, ..., cohort_6::D20

CL:0000624 holds
  cohort_1: CD4 T cells
  cohort_2: CD4
  cohort_3: CD4+ T cell
  cohort_4: CD4 naive, CD4 memory
  cohort_5: T_CD4
  cohort_6: CD4+ T cells

Analyse each cohort on its own

CubeCollection.aligned_cube gives a cohort’s cube on the collection’s axes: its cell types under their aligned ids, merged where several share one, the genes it measured, and its own samples, so that a design built from its own sample table applies. Each cohort is tested by limma_voom, cases against controls, and its results are keyed by the aligned cell types. plot_significant_genes shows what each cohort finds.

Six panels, one per cohort, each with a horizontal bar per aligned cell type for the genes found up (red, to the right) and down (blue, to the left) at an adjusted p-value below 0.05. In CL:0001054, the CD14-positive monocytes, cohort_1 (60 donors) finds 36 genes up and 37 down, cohort_2 (32 donors) 27 and 25, cohort_3 (24 donors) 19 and 11, cohort_4 (16 donors) 14 and 10, cohort_5 (12 donors) 14 and 14, and cohort_6 (20 donors) 15 and 16. In CL:0000624, the CD4 T cells, the cohorts find 5 to 34 genes; in the B cells (CL:0000236) at most 2; in the NK cells (CL:0000623) none, and cohort_6 has no row for them.

The larger cohorts find more: cohort_1, with 60 donors, finds 73 monocyte genes, and the cohorts of 12 to 32 donors 24 to 52. None finds a gene of the NK cells, which the disease does not change, and cohort_6 has no NK cells to test.

align_study_results lines up the results of one cell type and contrast gene by gene, with a row for every cohort and every gene of the collection, and says why a cohort has no result for a gene: it did not measure the gene, or measured it and left it out, as its expression filter does with genes too rarely detected. For the monocytes:

            tested  not tested  not measured
cohort_1     1,973          27             0
cohort_2     1,942          58             0
cohort_3     1,753          47           200
cohort_4     1,960          40             0
cohort_5     1,554         296           150
cohort_6     1,971          29             0

cohort_3 did not measure 200 genes and cohort_5 150; their expression filters left out 47 and 296 more, cohort_5’s the most, with the fewest donors and counts.

Meta-analysis

differential_expression_meta_analysis combines, for every cell type and gene, the log2 fold changes of the cohorts that tested it, each with its standard error, into one estimate. By default it fits the random-effects model, which lets the cohorts’ true effects vary around their mean by the between-study variance τ² DerSimonian & Laird, 1986, estimated by restricted maximum likelihood (REML) as metafor estimates it by default, and tests the mean with a z-test. A gene that two cohorts or more tested is analysed over those cohorts, so the genes that cohort_3 and cohort_5 did not measure are analysed over the other cohorts. plot_volcano draws the monocytes’ table.

Volcano plot of the meta-analysis of the monocytes: the pooled log2 fold change, from about -1.1 to 1.1, against the -log10 p-value, up to 23. 42 genes are significant up (red) and 40 down (blue), with log2 fold changes beyond about 0.3 in either direction; the 1,890 others are grey, around zero. The ten genes with the smallest p-values are named, gene_0481, down, the highest.

One gene across the cohorts

plot_forest draws one gene’s log2 fold change in every cohort with its 95% confidence interval, each square sized by the cohort’s weight, and the pooled estimate as a diamond, as metafor’s forest does Lewis & Clarke, 2001. The upper plot is the monocyte gene the meta-analysis finds most clearly. The lower one is the gene that cohort_1 finds most clearly among those the simulation changed in cohort_1 alone; the fixed-effect model, which assumes one true effect for every cohort, is fitted too (method="fixed").

gene_1312, pooled log2 fold change [95% interval]:
  fixed effect: 0.50 [0.33, 0.66], adjusted p 9e-08
  random effects: 0.16 [-0.43, 0.74], adjusted p 1
Two forest plots of monocyte genes, a row per cohort with its log2 fold change as a square and its 95% confidence interval, and the pooled random-effects estimate as an orange diamond at the bottom. Above, gene_0481, changed in every cohort: five cohorts between -1.19 and -0.84, every interval below zero, and cohort_3 marked not tested; the pooled estimate is -0.96 [-1.15, -0.78], with I-squared 0%. Below, gene_1312, changed in cohort_1 only: cohort_1 at 1.47 [1.20, 1.73] and the other five cohorts between -0.43 and 0.18, their intervals covering zero; the pooled estimate is 0.16 [-0.43, 0.74], with I-squared 91%, a wide diamond across zero.

In the upper plot the cohorts agree: every estimate lies below zero, I² is 0%, and the pooled log2 fold change, -0.96, has a narrow interval; cohort_3 did not measure the gene and keeps its row, marked not tested. In the lower one, cohort_1 changes the gene by 1.47 and the other cohorts’ estimates lie around zero. Cochran’s Q Cochran, 1954 finds the cohorts far more different than their standard errors allow, and I² Higgins & Thompson, 2002 puts 91% of the variation down to heterogeneity: the random-effects model widens the pooled interval to [-0.43, 0.74] and does not find the gene. The fixed-effect model gives cohort_1, the most precise cohort, the largest weight and finds it, with an adjusted p-value of 9e-08.

Stack the cohorts into one cube

CubeCollection.stack puts the cohorts into one cube for a joint model, with every cohort’s samples and the cohort as a column of the sample table. A cube holds no missing values, so the stacked cube keeps the genes that every cohort measured and the cell types that every cohort has; each cohort keeps its own counts and library sizes. CubeCollection.select chooses the cohorts, cell types and genes of an analysis, such as the cohorts that measured every gene. The design ~ study + condition gives every gene a baseline in each cohort and one difference between cases and controls, shared by all.

Cube with 164 samples x 3 groups x 1,650 features
  layers: counts (int64)
  filled: 492 of 492 sample x group entries have cells (100.0%)
  samples: sample_id, study, condition
  groups: group_id, cell_ontology_id
  features: feature_id
  4 cohorts measured every gene: 2,000 genes

The approaches against the truth

The simulation knows which genes each approach should find. The monocyte genes found at a false discovery rate of 5% by cohort_1 alone, by the meta-analyses and by the joint model, split by what the simulation did to them:

genes changed everywhere: 100, in the stack: 82
Stacked horizontal bars of the monocyte genes found at an adjusted p-value below 0.05 by four approaches, split into genes the simulation changed in every cohort (blue), in one cohort only (orange) and in none (grey): cohort_1 alone 62, 10 and 1; the fixed-effect meta-analysis 91, 23 and 2; the random-effects meta-analysis 80, 0 and 2; the stacked joint model 76, 18 and 4.

cohort_1 alone finds 62 of the 100 genes that the disease changes in every cohort. The fixed-effect meta-analysis finds the most of them, 91, and 23 genes of single cohorts with them. The random-effects meta-analysis finds 80 and none of a single cohort: a change in one cohort alone makes the cohorts disagree, which raises τ² and widens the interval. The joint model assumes one change for every cohort, as the fixed effect does, and finds 76 and 18; it tests only the genes that every cohort measured, which hold 82 of the 100.

Choices

  • Meta-analysis or a joint model. A meta-analysis needs only each cohort’s results, keeps the genes some cohorts did not measure, lets each cohort keep its own design, normalization and variances, and measures how far the cohorts disagree. A joint model of the stacked cohorts suits cohorts made alike, with one protocol and platform, that are too small to estimate their variances alone; it estimates one difference for every cohort, so a change in one cohort enters the estimate for all.

  • Fixed or random effects. The fixed-effect model (method="fixed") answers for the cohorts at hand and assumes they estimate one effect; the random-effects model, the default, allows the effect to vary between cohorts. τ² can also be estimated by DerSimonian and Laird’s method ("dl") or Paule and Mandel’s ("pm"), and test="knapp_hartung" gives the t-tests that metafor’s documentation recommends for few studies (Meta-analysis).

  • Genes that some cohorts did not test. min_studies, two by default, sets how many cohorts must test a gene; align_study_results says whether a cohort did not measure a gene or left it out.

  • Cell types and ids. Cell types are aligned by cell_ontology_id when every cohort has it, by group_id otherwise, or by another column (align_groups_by); group_mapping names the aligned id of any group, or None to leave it out. Columns of ids other than sample_id, such as donors with several samples, are kept apart by cohort with within_study.

References
  1. Viechtbauer, W. (2010). Conducting meta-analyses in R with the metafor package. Journal of Statistical Software, 36(3), 1–48. 10.18637/jss.v036.i03
  2. Diehl, A. D., Meehan, T. F., Bradford, Y. M., Brush, M. H., Dahdul, W. M., Dougall, D. S., He, Y., Osumi-Sutherland, D., Ruttenberg, A., Sarntivijai, S., Van Slyke, C. E., Vasilevsky, N. A., Haendel, M. A., Blake, J. A., & Mungall, C. J. (2016). The Cell Ontology 2016: enhanced content, modularization, and ontology interoperability. Journal of Biomedical Semantics, 7(1), 44. 10.1186/s13326-016-0088-7
  3. DerSimonian, R., & Laird, N. (1986). Meta-analysis in clinical trials. Controlled Clinical Trials, 7(3), 177–188. 10.1016/0197-2456(86)90046-2
  4. Lewis, S., & Clarke, M. (2001). Forest plots: trying to see the wood and the trees. BMJ, 322(7300), 1479–1480. 10.1136/bmj.322.7300.1479
  5. Cochran, W. G. (1954). The combination of estimates from different experiments. Biometrics, 10(1), 101–129. 10.2307/3001666
  6. Higgins, J. P. T., & Thompson, S. G. (2002). Quantifying heterogeneity in a meta-analysis. Statistics in Medicine, 21(11), 1539–1558. 10.1002/sim.1186