This guide is meant to assist the interpretation of analyses carried out within “Diebold and Gauthier et al. Effect of felzartamab anti-CD38 treatment on the molecular phenotype of antibody-mediated rejection in kidney transplant biopsies. under review. Nature Medicine”. This manuscript is currently under peer-review and has not yet been accepted for publication. A pre-print of the manuscript is available for review via Research Sqaure (https://www.researchsquare.com/article/rs-4870641/v1). The manuscript describes a comprehensive transcriptomics assessment of the effect of felzartamab on gene expression in kidney allograft biopsies taken from patients with antibody-mediated rejection (ABMR). The work described herein was part of a randomized controlled trial (ClinicalTrials.gov; NCT05021484). Biospies were taken at baseline, as well as 24 and 52 weeks post-baseline. The efficacy of felzartamab in resolving antibody-mediated rejection was compared to the progression of molecular activity in biopsies collected from patients receiving a placebo. This guide is a companion to the GitHub repository (https://github.com/TSI-PTG/CD38-effect-of-treatment).
Transcriptomics
assessment of biopsies from kidney allografts enables the interrogation
of a multitude of endpoints beyond individual gene expression. For
example, multiple gene expression-based machine learning classifiers
have been trained to predict the probability of histology lesion scores
indicative of antibody-mediated rejection (ABMR), T cell-mediated
rejection (TCMR), and overall ABMR and TCMR diagnoses. Predicted
probabilities from these classifiers can be used as a continuous
variable to assess the molecular activity of ABMR and TCMR. There are
also numerous gene lists that have been annotated in the transplantation
space to describe molecular profiles of rejection, cellular activity,
and responses to injury (https://www.ualberta.ca/en/medicine/institutes-centres-groups/atagc/research/gene-lists.html).
These gene lists have been translated into pathogenesis-based transcript
set (PBT) scores, and have been widely applied to depict gradients of
molecular activity related to allograft rejection and injury.
Supplementary Table 3. Molecular scores (PBTs, classifiers) | ||
---|---|---|
Category | Abbreviation | Description |
TCMR-related | TCMR-RAT | TCMR-associated (1) |
QCAT | Cytotoxic T cell associated (2) | |
TCB | T-cell burden related (3) | |
i>1Prob | Top 20 transcripts in classifier of probability of histologic kidney i-lesion score > 1 (4) | |
t>1Prob | Top 20 transcripts in classifier of probability of histologic kidney t-lesion score > 1 (4) | |
TCMRProb | Top 20 transcripts in classifier of probability of histologic kidney TCMR diagnosis (4) | |
ABMR-related | NKB | Natural killer (NK) cell burden (5) (only valid if there is no TCMR) |
DSAST | Kidney derived DSA selective transcripts (3) | |
ABMRProb | Top 20 transcripts in classifier of probability of histologic kidney ABMR diagnosis (4) | |
g>0Prob | Top 20 transcripts in classifier of probability of histologic g-lesion score > 0 (4) | |
ptc>0Prob | Top 20 transcripts in classifier of probability of histologic ptc-lesion score > 0 (4) | |
New ABMR-related PBTs | AAG | ABMR-associated activity genes |
IIAAG | IFNG-inducible ABMR-associated activity genes | |
NKAAG | NK cell expressed ABMR-associated activity genes | |
AEG | ABMR-associated endothelial cell genes | |
Macrophage-related | AMAT1 | Alternatively activated macrophage transcripts (6) |
QCMAT | Constitutive macrophage transcripts (7) | |
Recent injury-related | IRITD3 | Injury-repair induced, day 3 (IRITD3) (8) |
IRITD5 | Injury-repair induced, day 5 (IRITD3) (8) | |
IRRAT30 | Injury-repair associated (IRRAT30) (9) | |
Atrophy-fibrosis-related | ci>1Prob | Fibrosis classifier (10) |
ct>1Prob | Atrophy classifier (10) | |
Normal parenchyma | KT1 | Normal kidney transcripts (except solute carriers) (11) |
KT2 | Normal kidney solute carrier transcripts (11) | |
A https://www.ualberta.ca/medicine/institutes-centres-groups/atagc/research/gene-lists. | ||
Reference List | ||
1. Halloran, P.F., Venner, J.M. & Famulski, K.S. Comprehensive Analysis of Transcript Changes Associated With Allograft Rejection: Combining Universal and Selective Features. Am J Transplant 17, 1754-1769 (2017). | ||
2. Hidalgo, L.G., et al. The transcriptome of human cytotoxic T cells: measuring the burden of CTL-associated transcripts in human kidney transplants. Am J Transplant 8, 637-646 (2008). | ||
3. Hidalgo, L.G., et al. NK cell transcripts and NK cells in kidney biopsies from patients with donor-specific antibodies: evidence for NK cell involvement in antibody-mediated rejection. Am J Transplant 10, 1812-1822 (2010). | ||
4. Reeve, J., et al. Assessing rejection-related disease in kidney transplant biopsies based on archetypal analysis of molecular phenotypes. Jci Insight 2, e94197 (2017). | ||
5. Hidalgo, L.G., et al. Interpreting NK cell transcripts versus T cell transcripts in renal transplant biopsies. Am J Transplant 12, 1180-1191 (2012). | ||
6. Famulski, K.S., Sis, B., Billesberger, L. & Halloran, P.F. Interferon-gamma and donor MHC class I control alternative macrophage activation and activin expression in rejecting kidney allografts: a shift in the Th1-Th2 paradigm. Am J Transplant 8, 547-556 (2008). | ||
7. Famulski, K.S., et al. Defining the canonical form of T-cell-mediated rejection in human kidney transplants. Am J Transplant 10, 810-820 (2010). | ||
8. Famulski, K.S., et al. Transcriptome analysis reveals heterogeneity in the injury response of kidney transplants. Am J Transplant 7, 2483-2495 (2007). | ||
9. Famulski, K.S., et al. Molecular phenotypes of acute kidney injury in kidney transplants. J Am Soc Nephrol 23, 948-958 (2012). | ||
10. Halloran, P.F., et al. Molecular phenotype of kidney transplant indication biopsies with inflammation in scarred areas. Am J Transplant 19, 1356-1370 (2019). | ||
11. Einecke, G., Broderick, G., Sis, B. & Halloran, P.F. Early loss of renal transcripts in kidney allografts: relationship to the development of histologic lesions and alloimmune effector mechanisms. Am J Transplant 7, 1121-1130 (2007). | ||
Abbreviations: pathogenesis-based transcript set (PBT); donor-derived cell-free DNA (dd-cfDNA); antibody-mediated rejection (ABMR); T cell mediated rejection (TCMR) |
A primary
analysis in the present work assessed how felzartamab therapy affected
classifier and PBT scores in patients with ABMR. The study design
included two arms, placebo and felzartamab. Patients from both arms were
biopsied prior to receiving placebo or felzartamab, and followed up with
two additional biopsies at 24 and 52 weeks. The goal of the present work
was to determine if felzartamab suppressed the molecular activity of
ABMR. This effect, the effect of treatment, was defined as the
interactive effect between treatment (placebo and felzartamab) and
follow up (baseline, week 24, and week 52). This design was chosen to
account for the confounding effects of time between placebo and
felzartamab. While it is statistically straightforward to measure
interactive effects using parametric models, the assumptions of
normality and heterogeneity of variance were not met across all
molecular scores, and thus, for consistency we simply chose to carry out
non-parametric tests for all scores. This presented a challenge as
standard non-parametric rank-based tests do no allow for testing
interactions among independent variables. Thus, we availed to
aligned-rank transformation, a technique that rank-orders independent
variables while maintaining rankings across independent variables. An
analysis of variance (ANOVA) can then be carried out on the aligned-rank
transformed data to allow for a fully factorial non-parametric ANOVA.
This is powerful in this context because it allows us to test for the
interactive effect of treatment (i.e., placebo vs felzartamab) and
follow-up (i.e., change in scores from biopsy to biopsy). Moreover,
aligned-rank transformation also allows for us to specify random effects
terms in the ANOVA to handle repeated measurements (i.e., biopsies)
across patients.
References:
• Halloran, P.F., Famulski, K.S. & Reeve, J. Molecular assessment of disease states in kidney transplant biopsy samples. Nat Rev Nephrol 12, 534-548 (2016).
• Wobbrock J, F.L., Gergle D, Higgins J The Aligned Rank Transform for Nonparametric Factorial Analyses Using Only ANOVA Procedures. In Proceedings of the ACM Conference on Human Factors in Computing Systems (CHI ’11) 143–146. doi:10.1145/1978942.1978963, https://depts.washington.edu/acelab/proj/art/.(2011).
• Kay M, E.L., Higgins J, Wobbrock J. ARTool: Aligned Rank Transform for Nonparametric Factorial ANOVAs. doi:10.5281/zenodo.594511, R package version 0.11.1, https://github.com/mjskay/ARTool. (2021).
• Elkin, L.A., Kay, M., Higgins, J.J. & Wobbrock, J.O. An Aligned Rank Transform Procedure for Multifactor Contrast Tests. in The 34th Annual ACM Symposium on User Interface Software and Technology 754–768 (Association for Computing Machinery, Virtual Event, USA, 2021).
First we
need to load the necessary R packages and data.
# HOUSEKEEPING ####
# Load necessary libraries
library(tidyverse)
library(broom)
library(rstatix)
library(flextable)
library(officer)
library(emmeans)
library(multcomp)
library(ARTool)
library(rcompanion)
library(Biobase)
library(ggpubr)
library(patchwork)
library(ggprism)
# Custom operators
"%nin%" <- function(a, b) match(a, b, nomatch = 0) == 0
# Suppress dplyr reframe info
options(dplyr.reframe.inform = FALSE)
# Load data
load("C:/R/CD38-effect-of-treatment/data/cd38.RData")
load("C:/R/CD38-effect-of-treatment/results/flextable_artANOVA.RData")
# load plot data
load("C:/R/CD38-effect-of-treatment/results/plots_artANOVA.RData")
# Seed for reproducibility
set.seed(42)
Now we
define the categories of molecular scores for easier organization
downstream.
# Feature categories
vars_cfDNA <- c("cfDNA_cpml")
vars_abmr <- c("ABMRpm", "ggt0", "ptcgt0", "DSAST", "NKB")
vars_tcmr <- c("TCMRt", "tgt1", "igt1", "QCAT", "TCB")
vars_injury <- c("IRRAT30", "IRITD3", "IRITD5", "cigt1", "ctgt1")
vars_parenchyma <- c("KT1", "KT2")
vars_macrophage <- c("AMAT1", "QCMAT")
# DEFINE VARIABLES TO ASSESS ####
vars <- c(vars_cfDNA, vars_abmr, vars_tcmr, vars_macrophage, vars_injury, vars_parenchyma)
Now we
process the data so we can iteratively carry out the analyses on each
score.
# DEFINE THE DATA ####
data <- set %>%
pData() %>%
dplyr::select(Center, Patient, Felzartamab, Group, Followup, Felzartamab_Group, Felzartamab_Followup, all_of(vars)) %>%
dplyr::filter(Patient %nin% c(15, 18)) %>%
left_join(., summarise(., sample_pairs = n(), .by = Felzartamab_Followup), by = "Felzartamab_Followup") %>%
relocate(sample_pairs, .after = Felzartamab_Followup) %>%
arrange(Felzartamab, Patient, Group)
# WRANGLE THE PHENOTYPE DATA ####
data_00 <- data %>%
expand_grid(category = c("cfDNA", "ABMR", "TCMR", "macrophage", "injury", "parenchyma")) %>%
nest(.by = category) %>%
mutate(
features = map(
category,
function(category) {
if (category == "cfDNA") {
vars_cfDNA
} else if (category == "ABMR") {
vars_abmr
} else if (category == "TCMR") {
vars_tcmr
} else if (category == "macrophage") {
vars_macrophage
} else if (category == "injury") {
vars_injury
} else if (category == "parenchyma") {
vars_parenchyma
}
}
),
data = pmap(
list(features, data),
function(features, data) {
data %>%
dplyr::select(
Center, Patient, Felzartamab, Group, Followup, Felzartamab_Group, Felzartamab_Followup, sample_pairs,
all_of(features)
)
}
)
)
# WRANGLE THE DATA FOR UNIVARIATE TESTS ####
data_01 <- data_00 %>%
mutate(
data_univariate = pmap(
list(data, features),
function(data, features) {
data %>%
pivot_longer(
cols = all_of(features),
names_to = "variable",
values_to = "value"
) %>%
nest(.by = variable) %>%
mutate(
variable = variable %>%
factor(
levels = vars
),
annotation = case_when(
variable %in% vars_cfDNA ~ "cfDNA",
variable %in% vars_tcmr ~ "TCMR-related",
variable %in% vars_abmr ~ "ABMR-related",
variable %in% vars_macrophage ~ "macrophage-related",
variable %in% vars_injury ~ "injury-related",
variable %in% vars_parenchyma ~ "parenchyma-related",
TRUE ~ " "
) %>%
factor(
levels = c(
"cfDNA",
"ABMR-related",
"TCMR-related",
"macrophage-related",
"injury-related",
"parenchyma-related",
"archetypes",
"rejection PC",
"injury PC"
)
),
score = case_when(
variable == "cfDNA_cpml" ~ "Donor-derived cell-free DNA (dd-cfDNA, cp/mL)",
variable == "TCMRt" ~ "TCMR classifier (TCMRProb)",
variable == "TCB" ~ "T cell burden (TCB)",
variable == "tgt1" ~ "Tubulitis classifier (t>1Prob)",
variable == "igt1" ~ "Interstitial infiltrate classifier (i>1Prob)",
variable == "QCAT" ~ "Cytotoxic T cell-associated (QCAT)",
variable == "ABMRpm" ~ "ABMR classifier (ABMRProb)",
variable == "DSAST" ~ "DSA-selective transcripts (DSAST)",
variable == "NKB" ~ "NK cell burden (NKB)",
variable == "ggt0" ~ "Glomerulitis classifier (g>0Prob)",
variable == "ptcgt0" ~ "Peritubular capillaritis classifier (ptc>0Prob)",
variable == "AMAT1" ~ "Alternatively activated macrophage (AMAT1)",
variable == "QCMAT" ~ "Constitutive macrophage (QCMAT)",
variable == "IRITD3" ~ "Injury-repair induced, day 3 (IRITD3)",
variable == "IRITD5" ~ "Injury-repair induced, day 5 (IRITD5)",
variable == "IRRAT30" ~ "Injury-repair associated (IRRAT30)",
variable == "cigt1" ~ "Fibrosis classifier (ci>1Prob)",
variable == "ctgt1" ~ "Atrophy classifier (ct>1Prob)",
variable == "KT1" ~ "Kidney parenchymal (KT1)",
variable == "KT2" ~ "Kidney parenchymal - no solute carriers (KT2)"
), .before = 1
) %>%
arrange(annotation, variable)
}
)
) %>%
dplyr::select(category, data_univariate) %>%
unnest(data_univariate) %>%
mutate(n_cat = score %>% unique() %>% length(), .by = category, .after = variable)
This is
what the data look like after we have organized them. Each row of the
dataframe contains the data for each score, the category it belongs to,
its category annotation, its long-format name, and the name of the
variable in the data.
data_01 %>% print(n="all")
## # A tibble: 20 x 6
## category annotation score variable
## <chr> <fct> <chr> <fct>
## 1 cfDNA cfDNA Dono~ cfDNA_c~
## 2 ABMR ABMR-related ABMR~ ABMRpm
## 3 ABMR ABMR-related Glom~ ggt0
## 4 ABMR ABMR-related Peri~ ptcgt0
## 5 ABMR ABMR-related DSA-~ DSAST
## 6 ABMR ABMR-related NK c~ NKB
## 7 TCMR TCMR-related TCMR~ TCMRt
## 8 TCMR TCMR-related Tubu~ tgt1
## 9 TCMR TCMR-related Inte~ igt1
## 10 TCMR TCMR-related Cyto~ QCAT
## 11 TCMR TCMR-related T ce~ TCB
## 12 macrophage macrophage-~ Alte~ AMAT1
## 13 macrophage macrophage-~ Cons~ QCMAT
## 14 injury injury-rela~ Inju~ IRRAT30
## 15 injury injury-rela~ Inju~ IRITD3
## 16 injury injury-rela~ Inju~ IRITD5
## 17 injury injury-rela~ Fibr~ cigt1
## 18 injury injury-rela~ Atro~ ctgt1
## 19 parenchyma parenchyma-~ Kidn~ KT1
## 20 parenchyma parenchyma-~ Kidn~ KT2
## # i 2 more variables: n_cat <int>,
## # data <list>
At this
stage we can summarize the median values for each score by treatment and
follow-up.
(Note that the median scores are used for general interpretation of
treatment effects because aligned-ranks, the data being assessed for
inference regarding the effects of treatment, are not easily
interpretable.)
# UNIVARIATE MEDIANS ####
data_02 <- data_01 %>%
mutate(
medians = map(
data,
function(data) {
data %>%
reframe(
median = value %>% median(),
IQR = value %>% IQR(),
sample_pairs = n(),
.by = c(Followup, Felzartamab, Felzartamab_Followup)
) %>%
arrange(Felzartamab_Followup)
}
),
medians_delta = map(
medians,
function(medians) {
medians %>%
reframe(
median_delta = combn(median, 2, diff) %>% as.numeric(),
IQR_delta = combn(IQR, 2, mean) %>% as.numeric(),
sample_pairs = sample_pairs,
.by = c(Felzartamab)
) %>%
mutate(
Followup_pairwise = rep(c("Baseline - Week24", "Baseline - Week52", "Week24 - Week52"), 2) %>%
factor(levels = c("Baseline - Week24", "Baseline - Week52", "Week24 - Week52")),
.before = sample_pairs
) %>%
mutate(
median_delta_delta = combn(median_delta, 2, diff) %>% as.numeric(),
IQR_delta_delta = combn(IQR_delta, 2, mean) %>% as.numeric(),
.by = c(Followup_pairwise),
.before = sample_pairs
)
}
)
)
Let’s
take a look at what the median summaries look like for the ABMRprob
score.
data_02 %>%
dplyr::filter(variable == "ABMRpm") %>%
pull(medians) %>%
pluck(1) %>%
flextable::flextable()
Followup | Felzartamab | Felzartamab_Followup | median | IQR | sample_pairs |
---|---|---|---|---|---|
Baseline | Placebo | Baseline_Placebo | 0.6695 | 0.35525 | 10 |
Week24 | Placebo | Week24_Placebo | 0.7740 | 0.39025 | 10 |
Week52 | Placebo | Week52_Placebo | 0.5185 | 0.49825 | 10 |
Baseline | Felzartamab | Baseline_Felzartamab | 0.7360 | 0.30775 | 10 |
Week24 | Felzartamab | Week24_Felzartamab | 0.1675 | 0.35700 | 10 |
Week52 | Felzartamab | Week52_Felzartamab | 0.7025 | 0.53875 | 10 |
Now we
can look at the change in median ABMRprob scores in the placebo and
felzartamab arms, as well as the difference in those medians (i.e., ΔΔ
median - this is our summarized treatment effect).
data_02 %>%
dplyr::filter(variable == "ABMRpm") %>%
pull(medians_delta) %>%
pluck(1) %>%
flextable::flextable()
Felzartamab | median_delta | IQR_delta | Followup_pairwise | median_delta_delta | IQR_delta_delta | sample_pairs |
---|---|---|---|---|---|---|
Placebo | 0.1045 | 0.372750 | Baseline - Week24 | -0.6730 | 0.3525625 | 10 |
Placebo | -0.1510 | 0.426750 | Baseline - Week52 | 0.1175 | 0.4250000 | 10 |
Placebo | -0.2555 | 0.444250 | Week24 - Week52 | 0.7905 | 0.4460625 | 10 |
Felzartamab | -0.5685 | 0.332375 | Baseline - Week24 | -0.6730 | 0.3525625 | 10 |
Felzartamab | -0.0335 | 0.423250 | Baseline - Week52 | 0.1175 | 0.4250000 | 10 |
Felzartamab | 0.5350 | 0.447875 | Week24 - Week52 | 0.7905 | 0.4460625 | 10 |
Now we
carry out the analysis. This includes testing the specific contrasts for
the interaction of treatment and time (i.e., our effect of
treatment).
# UNIVARIATE NONPARAMETRIC TESTS ####
artANOVA <- data_02 %>%
mutate(
art = map(data, art, formula = value ~ Followup * Felzartamab + (1 | Patient)),
art_aov = map(art, anova, type = "II"),
art_aov_tidy = map(art_aov, tidy) %>% suppressWarnings(),
art_aov_contrast = map(
art,
art.con,
formula = "Followup:Felzartamab", adjust = "fdr", method = "pairwise", interaction = TRUE, response = "art"
),
art_aov_contrast_tidy = map(art_aov_contrast, tidy),
art_aov_contrast_cld = map(
art_aov_contrast_tidy,
function(art_aov_contrast_tidy) {
art_aov_contrast_tidy %>%
as.data.frame() %>%
cldList(adj.p.value ~ Followup:Felzartamab, data = .)
}
)
)
The
artANOVA effects are observed to reject or confirm a treatment effect on
the interactive effect for each score (i.e., to support the
interpretation of post-hoc tests for effect of
treatment).
# CREATE FLEXTABLE SUMMARY OF ART MODELS ####
title_art <- paste("Table i. Non-parametric ANOVA (ART) of molecular scores in biopsies from treated vs untreated patients")
artANOVA %>%
dplyr::select(annotation, n_cat, score, art_aov) %>%
unnest(everything()) %>%
dplyr::rename(p.value = `Pr(>F)`) %>%
mutate(FDR = p.value %>% p.adjust(method = "fdr"), .by = annotation) %>%
dplyr::select(annotation, score, Term, `F`, p.value, FDR) %>%
flextable::flextable() %>%
flextable::add_header_row(values = rep(title_art, ncol_keys(.))) %>%
flextable::merge_h(part = "header") %>%
flextable::merge_v(j = 1:2) %>%
flextable::fontsize(size = 8, part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::bg(i = ~ FDR < 0.05 & Term == "Followup:Felzartamab", j = 3:6, bg = "#fbff00") %>%
flextable::colformat_double(j = 2:3, digits = 2) %>%
flextable::colformat_double(j = 4:ncol_keys(.), digits = 3) %>%
flextable::border_remove() %>%
flextable::bold(part = "header") %>%
flextable::padding(padding = 0, part = "all") %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::autofit()
Table i. Non-parametric ANOVA (ART) of molecular scores in biopsies from treated vs untreated patients | |||||
---|---|---|---|---|---|
annotation | score | Term | F | p.value | FDR |
cfDNA | Donor-derived cell-free DNA (dd-cfDNA, cp/mL) | Followup | 4.418 | 0.019 | 0.029 |
Felzartamab | 1.598 | 0.222 | 0.222 | ||
Followup:Felzartamab | 10.396 | 0.000 | 0.001 | ||
ABMR-related | ABMR classifier (ABMRProb) | Followup | 6.659 | 0.003 | 0.007 |
Felzartamab | 1.620 | 0.219 | 0.235 | ||
Followup:Felzartamab | 9.605 | 0.000 | 0.001 | ||
Glomerulitis classifier (g>0Prob) | Followup | 11.768 | 0.000 | 0.001 | |
Felzartamab | 1.511 | 0.235 | 0.235 | ||
Followup:Felzartamab | 10.743 | 0.000 | 0.001 | ||
Peritubular capillaritis classifier (ptc>0Prob) | Followup | 14.188 | 0.000 | 0.000 | |
Felzartamab | 3.178 | 0.092 | 0.125 | ||
Followup:Felzartamab | 7.749 | 0.002 | 0.004 | ||
DSA-selective transcripts (DSAST) | Followup | 10.508 | 0.000 | 0.001 | |
Felzartamab | 1.747 | 0.203 | 0.234 | ||
Followup:Felzartamab | 4.179 | 0.023 | 0.035 | ||
NK cell burden (NKB) | Followup | 6.512 | 0.004 | 0.007 | |
Felzartamab | 1.956 | 0.179 | 0.224 | ||
Followup:Felzartamab | 5.114 | 0.011 | 0.018 | ||
TCMR-related | TCMR classifier (TCMRProb) | Followup | 3.555 | 0.039 | 0.165 |
Felzartamab | 1.417 | 0.249 | 0.468 | ||
Followup:Felzartamab | 1.124 | 0.336 | 0.504 | ||
Tubulitis classifier (t>1Prob) | Followup | 1.016 | 0.372 | 0.506 | |
Felzartamab | 1.853 | 0.190 | 0.408 | ||
Followup:Felzartamab | 0.065 | 0.937 | 0.968 | ||
Interstitial infiltrate classifier (i>1Prob) | Followup | 3.414 | 0.044 | 0.165 | |
Felzartamab | 0.997 | 0.331 | 0.504 | ||
Followup:Felzartamab | 0.032 | 0.968 | 0.968 | ||
Cytotoxic T cell-associated (QCAT) | Followup | 5.693 | 0.007 | 0.107 | |
Felzartamab | 4.118 | 0.057 | 0.172 | ||
Followup:Felzartamab | 0.927 | 0.405 | 0.506 | ||
T cell burden (TCB) | Followup | 4.360 | 0.020 | 0.151 | |
Felzartamab | 2.868 | 0.108 | 0.269 | ||
Followup:Felzartamab | 0.301 | 0.742 | 0.856 | ||
macrophage-related | Alternatively activated macrophage (AMAT1) | Followup | 1.549 | 0.226 | 0.304 |
Felzartamab | 1.047 | 0.320 | 0.320 | ||
Followup:Felzartamab | 2.406 | 0.105 | 0.209 | ||
Constitutive macrophage (QCMAT) | Followup | 3.378 | 0.045 | 0.209 | |
Felzartamab | 3.100 | 0.095 | 0.209 | ||
Followup:Felzartamab | 1.426 | 0.253 | 0.304 | ||
injury-related | Injury-repair associated (IRRAT30) | Followup | 0.055 | 0.946 | 0.946 |
Felzartamab | 3.842 | 0.066 | 0.223 | ||
Followup:Felzartamab | 1.915 | 0.162 | 0.347 | ||
Injury-repair induced, day 3 (IRITD3) | Followup | 0.174 | 0.841 | 0.946 | |
Felzartamab | 3.268 | 0.087 | 0.223 | ||
Followup:Felzartamab | 3.125 | 0.056 | 0.223 | ||
Injury-repair induced, day 5 (IRITD5) | Followup | 0.073 | 0.930 | 0.946 | |
Felzartamab | 1.088 | 0.311 | 0.583 | ||
Followup:Felzartamab | 2.586 | 0.089 | 0.223 | ||
Fibrosis classifier (ci>1Prob) | Followup | 0.360 | 0.700 | 0.876 | |
Felzartamab | 3.482 | 0.078 | 0.223 | ||
Followup:Felzartamab | 0.938 | 0.401 | 0.668 | ||
Atrophy classifier (ct>1Prob) | Followup | 0.568 | 0.572 | 0.780 | |
Felzartamab | 3.297 | 0.086 | 0.223 | ||
Followup:Felzartamab | 0.602 | 0.553 | 0.780 | ||
parenchyma-related | Kidney parenchymal (KT1) | Followup | 0.968 | 0.389 | 0.518 |
Felzartamab | 1.628 | 0.218 | 0.518 | ||
Followup:Felzartamab | 0.605 | 0.552 | 0.552 | ||
Kidney parenchymal - no solute carriers (KT2) | Followup | 0.859 | 0.432 | 0.518 | |
Felzartamab | 0.911 | 0.352 | 0.518 | ||
Followup:Felzartamab | 1.038 | 0.365 | 0.518 |
Here we
present the effect of treatment (ΔΔ) for each score, along with the
effect of time (Δ) for placebo and felzartmab arms.
flextable_pairwise
Table 1. Effect of felzartamab on molecular scores | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Annotation | Score | Baseline - Week 24 | Week 24 - Week 52 | Baseline - Week 52 | |||||||||
Δ Placebo | Δ Felzartamab | ΔΔ | ΔΔ FDR | Δ Placebo | Δ Felzartamab | ΔΔ | ΔΔ FDR | Δ Placebo | Δ Felzartamab | ΔΔ | ΔΔ FDR | ||
cfDNA | Donor-derived cell-free DNA (dd-cfDNA, cp/mL) | 0.50 (76.12) | -55.00 (30) | -55.50 (53.06) | 2e-04 | -3.50 (69.12) | 14.50 (25.5) | 18.00 (47.31) | 0.027 | -3.00 (51) | -40.50 (45.75) | -37.50 (48.38) | 0.045 |
ABMR-related | ABMR classifier (ABMRProb) | 0.10 (0.37) | -0.57 (0.33) | -0.67 (0.35) | 1e-03 | -0.26 (0.44) | 0.54 (0.45) | 0.79 (0.45) | 1e-03 | -0.15 (0.43) | -0.03 (0.42) | 0.12 (0.43) | 0.709 |
Glomerulitis classifier (g>0Prob) | -0.02 (0.23) | -0.31 (0.35) | -0.29 (0.29) | 3e-03 | -0.19 (0.34) | 0.31 (0.41) | 0.50 (0.37) | 2e-04 | -0.22 (0.3) | 0.00 (0.38) | 0.21 (0.34) | 0.277 | |
Peritubular capillaritis classifier (ptc>0Prob) | -0.12 (0.25) | -0.55 (0.27) | -0.43 (0.26) | 3e-03 | -0.10 (0.37) | 0.37 (0.31) | 0.47 (0.34) | 3e-03 | -0.22 (0.3) | -0.18 (0.35) | 0.04 (0.33) | 0.977 | |
DSA-selective transcripts (DSAST) | -0.09 (0.22) | -0.37 (0.24) | -0.28 (0.23) | 0.026 | -0.16 (0.32) | 0.16 (0.33) | 0.32 (0.33) | 0.026 | -0.24 (0.33) | -0.20 (0.25) | 0.04 (0.29) | 0.988 | |
NK cell burden (NKB) | 0.02 (0.22) | -0.78 (0.49) | -0.80 (0.36) | 0.014 | -0.19 (0.4) | 0.68 (0.59) | 0.87 (0.49) | 0.014 | -0.17 (0.46) | -0.10 (0.62) | 0.07 (0.54) | 0.972 | |
TCMR-related | TCMR classifier (TCMRProb) | -0.01 (0.02) | 0.00 (0) | 0.01 (0.01) | 0.358 | 0.00 (0.02) | 0.00 (0) | 0.00 (0.01) | 0.856 | -0.01 (0.02) | 0.00 (0) | 0.01 (0.01) | 0.358 |
Tubulitis classifier (t>1Prob) | -0.01 (0.06) | -0.02 (0.04) | -0.01 (0.05) | 1.000 | 0.00 (0.04) | 0.01 (0.02) | 0.01 (0.03) | 1.000 | -0.01 (0.05) | -0.01 (0.03) | -0.01 (0.04) | 1.000 | |
Interstitial infiltrate classifier (i>1Prob) | -0.01 (0.04) | -0.02 (0.03) | -0.01 (0.04) | 0.964 | 0.01 (0.04) | 0.01 (0.04) | 0.00 (0.04) | 0.964 | 0.00 (0.05) | -0.01 (0.06) | -0.01 (0.05) | 0.964 | |
Cytotoxic T cell-associated (QCAT) | -0.20 (0.41) | -0.70 (0.46) | -0.50 (0.43) | 0.425 | 0.10 (0.33) | 0.54 (0.57) | 0.44 (0.45) | 0.425 | -0.10 (0.46) | -0.16 (0.41) | -0.06 (0.44) | 0.871 | |
T cell burden (TCB) | -0.44 (0.71) | -0.79 (0.56) | -0.36 (0.64) | 0.825 | 0.28 (0.57) | 0.34 (0.6) | 0.06 (0.58) | 0.905 | -0.16 (0.88) | -0.45 (0.43) | -0.29 (0.65) | 0.825 | |
macrophage-related | Alternatively activated macrophage (AMAT1) | 0.08 (0.2) | -0.27 (0.45) | -0.35 (0.33) | 0.128 | 0.09 (0.23) | 0.11 (0.47) | 0.02 (0.35) | 0.812 | 0.18 (0.19) | -0.16 (0.32) | -0.33 (0.25) | 0.128 |
Constitutive macrophage (QCMAT) | 0.00 (0.15) | -0.24 (0.27) | -0.24 (0.21) | 0.340 | 0.01 (0.12) | 0.09 (0.27) | 0.08 (0.2) | 0.685 | 0.01 (0.11) | -0.15 (0.24) | -0.16 (0.18) | 0.348 | |
injury-related | Injury-repair associated (IRRAT30) | 0.18 (0.52) | 0.05 (0.4) | -0.13 (0.46) | 0.353 | 0.05 (0.66) | -0.11 (0.72) | -0.16 (0.69) | 0.353 | 0.23 (0.6) | -0.06 (0.48) | -0.29 (0.54) | 0.175 |
Injury-repair induced, day 3 (IRITD3) | 0.03 (0.19) | 0.04 (0.12) | 0.01 (0.15) | 0.945 | 0.03 (0.18) | -0.18 (0.18) | -0.20 (0.18) | 0.060 | 0.06 (0.19) | -0.13 (0.14) | -0.19 (0.16) | 0.060 | |
Injury-repair induced, day 5 (IRITD5) | -0.01 (0.17) | 0.04 (0.14) | 0.05 (0.15) | 0.932 | 0.07 (0.2) | -0.17 (0.21) | -0.24 (0.21) | 0.093 | 0.06 (0.18) | -0.13 (0.17) | -0.18 (0.18) | 0.093 | |
Fibrosis classifier (ci>1Prob) | -0.02 (0.29) | 0.13 (0.32) | 0.15 (0.3) | 0.434 | 0.06 (0.25) | -0.08 (0.53) | -0.14 (0.39) | 0.844 | 0.04 (0.25) | 0.05 (0.37) | 0.01 (0.31) | 0.434 | |
Atrophy classifier (ct>1Prob) | 0.01 (0.33) | 0.12 (0.31) | 0.12 (0.32) | 0.587 | 0.09 (0.27) | -0.06 (0.44) | -0.15 (0.36) | 0.587 | 0.10 (0.32) | 0.07 (0.41) | -0.03 (0.36) | 0.587 | |
parenchyma-related | Kidney parenchymal (KT1) | -0.05 (0.22) | -0.15 (0.23) | -0.09 (0.22) | 0.731 | -0.01 (0.11) | 0.10 (0.22) | 0.11 (0.17) | 0.705 | -0.06 (0.25) | -0.04 (0.18) | 0.02 (0.22) | 0.705 |
Kidney parenchymal - no solute carriers (KT2) | -0.09 (0.39) | -0.16 (0.37) | -0.07 (0.38) | 0.577 | -0.09 (0.2) | 0.14 (0.38) | 0.23 (0.29) | 0.484 | -0.18 (0.42) | -0.02 (0.31) | 0.16 (0.37) | 0.577 | |
Grey shading denotes ANOVA interactive effect FDR < 0.05 |
# EXTRACT LEGEND FOR PLOTS ####
panel_legend <- plots_artANOVA %>%
dplyr::filter(variable == "AMAT1") %>%
pull(plot_violin) %>%
ggpubr::get_legend() %>%
ggpubr::as_ggplot() +
theme(plot.margin = unit(c(0, 0, -1, 0), "cm"))
# MOLECULAR ABMR PANELS ####
panel_violin_abmr <- plots_artANOVA %>%
dplyr::filter(category %in% c("ABMR")) %>%
pull(plot_violin) %>%
wrap_plots(nrow = 1, ncol = 5) &
theme(
axis.text = element_text(size = 10, colour = "black"),
legend.position = "none",
plot.background = element_rect(fill = "grey95", colour = " white")
)
panel_violin_abmr <- panel_violin_abmr %>%
ggarrange(
labels = "B",
font.label = list(size = 25, face = "bold"),
legend = "none"
) %>%
ggpubr::annotate_figure(
top = text_grob("Effect of felzartamab on molecular ABMR activity scores",
face = "bold.italic",
size = 25,
hjust = 1.27
)
)
# MOLECULAR TCMR PANELS ####
panel_violin_tcmr <- plots_artANOVA %>%
dplyr::filter(category %in% c("TCMR")) %>%
pull(plot_violin) %>%
wrap_plots(nrow = 1, ncol = 5) &
theme(
axis.text = element_text(size = 10, colour = "black"),
legend.position = "none",
)
panel_violin_tcmr <- panel_violin_tcmr %>%
ggarrange(
labels = "C",
font.label = list(size = 25, face = "bold"),
legend = "none"
) %>%
ggpubr::annotate_figure(
top = text_grob("Effect of felzartamab on molecular TCMR activity scores",
face = "bold.italic",
size = 25,
hjust = 1.27
)
)
# COMBINED VIOLIN PANELS ####
panels_violin <- ggarrange(
panel_violin_abmr,
panel_violin_tcmr,
nrow = 2,
heights = c(1, 1)
)
ggarrange(
panel_legend,
panels_violin,
nrow = 2,
heights = c(0.125, 1)
)
While
ABMR-related scores changed monotonically over time, dipping at week 24
then rising again by week 52, initial observations of injury scores over
time alluded to a different relationship. Injury scores appeared to
gradual increase overtime in the placebo arm and a gradual decrease
overtime in the felzartamab arm. However, the use of artANOVA, a
non-parametric rank-based, test failed to detect such a relationship.
This led us to ask if estimating the slopes of injury curves over time
would be a more informative interrogation of these data. We applied a
linear mixed-model to answer this question.
References:
• Vonesh, E., et al. Mixed-effects models for slope-based endpoints in clinical trials of chronic kidney disease. Stat Med 38, 4218-4239 (2019).
First we
need to load the necessary R packages and data.
# HOUSEKEEPING ####
# CRAN packages
library(tidyverse)
library(lme4)
library(lmerTest)
library(performance)
library(ggeffects)
library(flextable)
library(ggpubr)
library(patchwork)
library(ggprism)
# Bioconductor libraries
library(Biobase) # BiocManager::install("Biobase")
# Custom operators and functions
"%nin%" <- function(a, b) match(a, b, nomatch = 0) == 0
source("C:/R/CD38-effect-of-treatment/code/functions/get_slope_function.r")
# load reference set
load("C:/R/CD38-effect-of-treatment/data/cd38.RData")
# load refression plots
load("C:/R/CD38-effect-of-treatment/results/plots_regression.RData")
# load violin plots
load("C:/R/CD38-effect-of-treatment/results/plots_artANOVA.RData")
# Seed for reproducibility
set.seed(42)
# DEFINE THE DATA ####
data <- set %>%
pData() %>%
dplyr::select(Center, Patient, Felzartamab, Group, IRRAT30, IRITD3, IRITD5) %>%
dplyr::filter(Patient %nin% c(15, 18)) %>%
mutate(
time = case_when(
Group == "Index" ~ 0,
Group == "FU1" ~ 0.46,
Group == "FU2" ~ 0.99
),
linetype = "solid" %>% factor()
)
Now we
fit the linear mixed-models.
# FIT MODELS ####
fit_IRRAT30 <- lmer(IRRAT30 ~ Felzartamab * time + (time | Patient), data)
fit_IRITD3 <- lmer(IRITD3 ~ Felzartamab * time + (time | Patient), data)
fit_IRITD5 <- lmer(IRITD5 ~ Felzartamab * time + (time | Patient), data)
We check
the validatity of the model assumptions for IRRAT30.
# test model assumptions
performance::check_model(fit_IRRAT30)
performance::check_normality(fit_IRRAT30)
## OK: residuals appear as normally distributed (p = 0.605).
We check
the validatity of the model assumptions for IRITD3.
# test model assumptions
performance::check_model(fit_IRITD3)
performance::check_normality(fit_IRITD3)
## OK: residuals appear as normally distributed (p = 0.070).
We check
the validatity of the model assumptions for IRITD5.
# test model assumptions
performance::check_model(fit_IRITD5)
performance::check_normality(fit_IRITD5)
## OK: residuals appear as normally distributed (p = 0.090).
The next
step is to extract and summarize the model results.
# EXTRACT THE SLOPES FROM THE MODEL FITS ####
# IRRAT30
# Create list object into which to place model results
slopes_IRRAT30 <- list()
# Acute slopes
slopes_IRRAT30[[1]] <- get_slope(
.model_obj = fit_IRRAT30, .name = "Placebo",
.contrasts = c(0, 0, 1, 0) #
)
slopes_IRRAT30[[2]] <- get_slope(
.model_obj = fit_IRRAT30, .name = "Felzartamab",
.contrasts = c(0, 0, 1, 1)
)
slopes_IRRAT30[[3]] <- get_slope(
.model_obj = fit_IRRAT30, .name = "Intergroup Difference",
.contrasts = c(0, 0, 0, 1)
)
model_IRRAT30 <- slopes_IRRAT30 %>%
bind_rows() %>%
mutate(p_adjusted = p.adjust(p_value, method = "fdr")) %>%
dplyr::select(name, result, p_value, p_adjusted)
# IRITD3
# Create list object into which to place model results
slopes_IRITD3 <- list()
# Acute slopes
slopes_IRITD3[[1]] <- get_slope(
.model_obj = fit_IRITD3, .name = "Placebo",
.contrasts = c(0, 0, 1, 0) #
)
slopes_IRITD3[[2]] <- get_slope(
.model_obj = fit_IRITD3, .name = "Felzartamab",
.contrasts = c(0, 0, 1, 1)
)
slopes_IRITD3[[3]] <- get_slope(
.model_obj = fit_IRITD3, .name = "Intergroup Difference",
.contrasts = c(0, 0, 0, 1)
)
model_IRITD3 <- slopes_IRITD3 %>%
bind_rows() %>%
mutate(p_adjusted = p.adjust(p_value, method = "fdr")) %>%
dplyr::select(name, result, p_value, p_adjusted)
# IRITD5
# Create list object into which to place model results
slopes_IRITD5 <- list()
# Acute slopes
slopes_IRITD5[[1]] <- get_slope(
.model_obj = fit_IRITD5, .name = "Placebo",
.contrasts = c(0, 0, 1, 0) #
)
slopes_IRITD5[[2]] <- get_slope(
.model_obj = fit_IRITD5, .name = "Felzartamab",
.contrasts = c(0, 0, 1, 1)
)
slopes_IRITD5[[3]] <- get_slope(
.model_obj = fit_IRITD5, .name = "Intergroup Difference",
.contrasts = c(0, 0, 0, 1)
)
model_IRITD5 <- slopes_IRITD5 %>%
bind_rows() %>%
mutate(p_adjusted = p.adjust(p_value, method = "fdr")) %>%
dplyr::select(name, result, p_value, p_adjusted)
IRRAT30
size_font <- 10
model_IRRAT30 %>%
dplyr::select(-p_value) %>%
flextable::qflextable() %>%
flextable::set_table_properties(layout = "autofit") %>%
flextable::set_header_labels(
name = "Group", result = "IRRAT30 Slope (95%CI)",
"p_adjusted" = "FDR"
) %>%
flextable::bold(i = NULL, part = "header") %>%
flextable::fontsize(size = size_font, part = "all")
Group | IRRAT30 Slope (95%CI) | FDR |
---|---|---|
Placebo | 0.32 (-0.04 to 0.67) | 0.1133 |
Felzartamab | -0.29 (-0.64 to 0.07) | 0.1133 |
Intergroup Difference | -0.60 (-1.10 to -0.10) | 0.0546 |
IRITD3
model_IRITD3 %>%
dplyr::select(-p_value) %>%
flextable::qflextable() %>%
flextable::set_table_properties(layout = "autofit") %>%
flextable::set_header_labels(
name = "Group", result = "IRITD3 Slope (95%CI)",
"p_adjusted" = "FDR"
) %>%
flextable::bold(i = NULL, part = "header") %>%
flextable::fontsize(size = size_font, part = "all")
Group | IRITD3 Slope (95%CI) | FDR |
---|---|---|
Placebo | 0.06 (-0.06 to 0.18) | 0.3453 |
Felzartamab | -0.14 (-0.26 to -0.02) | 0.0408 |
Intergroup Difference | -0.20 (-0.37 to -0.02) | 0.0408 |
IRITD5
model_IRITD5 %>%
dplyr::select(-p_value) %>%
flextable::qflextable() %>%
flextable::set_table_properties(layout = "autofit") %>%
flextable::set_header_labels(
name = "Group", result = "IRITD5 Slope (95%CI)",
"p_adjusted" = "FDR"
) %>%
flextable::bold(i = NULL, part = "header") %>%
flextable::fontsize(size = size_font, part = "all")
Group | IRITD5 Slope (95%CI) | FDR |
---|---|---|
Placebo | 0.09 (-0.04 to 0.22) | 0.15720 |
Felzartamab | -0.13 (-0.26 to -0.01) | 0.05895 |
Intergroup Difference | -0.23 (-0.41 to -0.05) | 0.04200 |
# DEFINE LEGEND FOR VIOLIN PLOTS ####
panel_legend_violin <- plots_artANOVA %>%
dplyr::filter(variable == "AMAT1") %>%
pull(plot_violin) %>%
ggpubr::get_legend() %>%
ggpubr::as_ggplot() +
theme(plot.margin = unit(c(0, 0, -1, 0), "cm"))
# DEFINE LEGEND FOR REGRESSION PLOTS ####
panel_legend_regression <- plots_regression %>%
dplyr::filter(variable == "IRRAT30") %>%
pull(plot) %>%
ggpubr::get_legend() %>%
ggpubr::as_ggplot()
# DEFIN JOINT LEGEND FOR REGRESSION AND VIOLIN PLOTS ####
panel_legend_all_injury <- panel_legend_violin + panel_legend_regression
# DEFINE INJURY VIOLIN PLOTS ####
plots_violin <- plots_artANOVA %>%
dplyr::filter(category %in% c("injury")) %>%
pull(plot_violin)
# MAKE PANEL OF SLOPE AND VIOLIN PLOTS ####
plot_panels_all_injury <- patchwork::wrap_plots(
plots_violin[[1]], plots_violin[[2]], plots_violin[[3]],
plots_regression$plot[[1]], plots_regression$plot[[2]], plots_regression$plot[[3]],
plots_regression$grob_table[[1]], plots_regression$grob_table[[2]], plots_regression$grob_table[[3]],
nrow = 3,
ncol = 3,
guides = "collect",
heights = c(1, 1, 0.5)
) +
patchwork::plot_annotation(tag_levels = list(c(LETTERS[1:6], rep("", 3)))) &
theme(
legend.position = "none",
# plot.title = element_blank(),
axis.text = element_text(size = 10, colour = "black"),
axis.title = element_text(size = 12, colour = "black"),
plot.tag = element_text(size = 20, face = "bold", vjust = 1)
)
ggarrange(
panel_legend_all_injury,
plot_panels_all_injury,
nrow = 2,
heights = c(0.15, 1.5)
)
In addition to
molecular scores, we had the opportunity to carry out genome-wide
assessement of the effect of felzartamab. We use the Linear Models for
Microarray Data (limma) pipeline for applying empirical Bayes mediated
t-tests on individual genes. Specific contrasts to assess the
interaction between treatment and time are specified to estimate the
effect of treatment on each gene.
References:
• Ritchie, M.E., et al. limma powers differential expression analyses for RNA-sequencing and microarray studies. Nucleic Acids Res 43, e47 (2015).
First we
need to load the necessary R packages and data.
# HOUSEKEEPING ####
# CRAN libraries
library(tidyverse)
library(flextable)
library(officer)
# Bioconductor libraries
library(Biobase)
library(limma)
library(genefilter)
# Custom operators and functions
"%nin%" <- function(a, b) match(a, b, nomatch = 0) == 0
# Load data
load("C:/R/CD38-effect-of-treatment/data/cd38.RData")
load("C:/R/CD38-effect-of-treatment/data/affymap219.RData")
# Seed for reproducibility
set.seed(42)
# DEFINE THE SET ####
set00 <- set[, set$Patient %nin% c(15, 18)]
• We
filter the gene expression data by to remove genes with minimal variance
across the population (IQR filtering).
# IQR FILTER THE DATA ####
f1 <- function(x) (IQR(x) > 0.5)
ff <- filterfun(f1)
if (!exists("selected")) {
selected <- genefilter(set00, ff)
}
set01 <- set00[selected, ]
• We then
retain a single probeset for each gene based on whichever has the
highest mean expression in the population.
# KEEP UNIQUE GENES (keep probe with highest mean expression) ####
mean_exprs_by_probe <- set01 %>%
exprs() %>%
as_tibble(rownames = "AffyID") %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb) %>% tibble(), ., by = "AffyID") %>%
mutate(mean_exprs = set01 %>%
exprs() %>% rowMeans(), .after = Symb)
genes <- mean_exprs_by_probe %>%
group_by(Symb) %>%
dplyr::slice_max(mean_exprs) %>%
dplyr::filter(Symb != "") %>%
distinct(Symb, .keep_all = TRUE) %>%
pull(AffyID)
set <- set01[featureNames(set01) %in% genes, ]
The
felzartamab trial was a phase II randomized control trial (RCT). While
RCTs are the ‘gold standard’ for accounting for confounders while
establishing causality (i.e., the effect of treatment), they are not
perfect, particularly when the number of study participants is low.
Thus, we pre-screened gene expression data to remove any genes that
differed between placebo and felzartamab arms at baseline. This was done
to reduce the potential for interpreting the effect of treatment on
genes with dissimilar baseline profiles.
# DEFINE FACTOR FOR CONTRASTS ####
Felzartamab_Followup <- set$Felzartamab_Followup %>% droplevels()
# BLOCK DESIGN week24 - baseline ####
design_block01 <- model.matrix(~ 0 + Felzartamab_Followup)
contrast_block_01 <- makeContrasts(
"x = (Felzartamab_FollowupBaseline_Felzartamab) -(Felzartamab_FollowupBaseline_Placebo)",
levels = design_block01
)
# FIT BLOCK week24 - baseline LIMMA MODEL ####
fit_block_1 <- limma::lmFit(set, design_block01)
cfit_block_1 <- limma::contrasts.fit(fit_block_1, contrast_block_01)
ebayes_block_1 <- limma::eBayes(cfit_block_1)
tab_block_1 <- limma::topTable(ebayes_block_1, adjust = "fdr", sort.by = "p", number = "all")
# CALCULATE MEAN GENE EXPRESSION FOR EACH PROBE BETWEEN GROUPINGS ####
means_baseline <- fit_block_1 %>%
avearrays() %>%
data.frame() %>%
rownames_to_column("AffyID") %>%
tibble() %>%
mutate_if(is.numeric, ~ 2^. %>% round(0)) %>%
rename_at(vars(contains("Felz")), ~ str_remove(., "Felzartamab_Followup")) %>%
dplyr::select(AffyID, contains("Baseline"))
The top
20 genes that differ at baseline (sorted by p-value) between placebo and
felzartamab arms are summarized below.
(Note that all FDR are > 0.05. This is a consequence of differential expression across a large selection of genes (5,267 in this case) with small samples sizes (i.e., n = 10 by treatment group). Nonetheless, genes with p < 0.05 will be excluded from subsequent analyses).
# FORMAT TOPTABLES ####
baseline_deg <- tab_block_1 %>%
as_tibble(rownames = "AffyID") %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID") %>%
arrange(P.Value) %>%
mutate_at(c("P.Value", "adj.P.Val"), as.numeric) %>%
tibble() %>%
left_join(means_baseline, by = "AffyID") %>%
dplyr::select(
AffyID, Symb,
Baseline_Placebo, Baseline_Felzartamab,
t, logFC, P.Value, adj.P.Val,
) %>%
dplyr::rename(p = P.Value, FDR = adj.P.Val)
baseline_deg %>%
dplyr::slice_min(p, n = 20) %>%
mutate_at(vars(t, logFC, FDR), ~ formatC(.x, format = "f", digits = 2)) %>%
mutate_at(vars(p), ~ formatC(.x, format = "f", digits = 5)) %>%
flextable::flextable()
AffyID | Symb | Baseline_Placebo | Baseline_Felzartamab | t | logFC | p | FDR |
---|---|---|---|---|---|---|---|
11727331_at | SPAG4 | 90 | 50 | -4.23 | -0.85 | 0.00008 | 0.33 |
11763725_a_at | HNRNPU | 366 | 665 | 4.10 | 0.86 | 0.00013 | 0.33 |
11718286_a_at | UBL3 | 74 | 142 | 3.66 | 0.94 | 0.00053 | 0.45 |
11732590_x_at | TRIM5 | 42 | 63 | 3.65 | 0.59 | 0.00055 | 0.45 |
11725671_a_at | IPO7 | 42 | 66 | 3.54 | 0.64 | 0.00079 | 0.45 |
11741431_a_at | KCNJ16 | 203 | 311 | 3.49 | 0.61 | 0.00091 | 0.45 |
11726915_at | SLCO4C1 | 87 | 176 | 3.48 | 1.02 | 0.00096 | 0.45 |
11723109_a_at | GPBP1 | 153 | 263 | 3.46 | 0.78 | 0.00099 | 0.45 |
11739744_x_at | MXI1 | 123 | 191 | 3.45 | 0.64 | 0.00102 | 0.45 |
11720890_a_at | AGL | 39 | 63 | 3.40 | 0.70 | 0.00121 | 0.45 |
11746431_a_at | FBXO11 | 28 | 45 | 3.38 | 0.68 | 0.00127 | 0.45 |
11751841_a_at | TNFRSF17 | 56 | 20 | -3.36 | -1.51 | 0.00134 | 0.45 |
11744367_a_at | ZMYND11 | 30 | 49 | 3.33 | 0.68 | 0.00150 | 0.45 |
11748857_a_at | AK3 | 371 | 578 | 3.32 | 0.64 | 0.00155 | 0.45 |
11727307_x_at | ZNF320 | 46 | 66 | 3.31 | 0.51 | 0.00160 | 0.45 |
11747648_a_at | TCAIM | 96 | 152 | 3.29 | 0.66 | 0.00168 | 0.45 |
11728966_s_at | ZNF28 | 29 | 67 | 3.27 | 1.21 | 0.00181 | 0.45 |
11723387_a_at | PTAR1 | 149 | 261 | 3.25 | 0.81 | 0.00187 | 0.45 |
11739771_a_at | OXR1 | 31 | 51 | 3.24 | 0.73 | 0.00194 | 0.45 |
11730038_at | GNG13 | 44 | 28 | -3.24 | -0.63 | 0.00196 | 0.45 |
We now
carry out differential expression analysis to estimate the treatment
effect on individual genes.
# DEFINE GENES SIMILAR AT BASELINE ####
genes_baseline <- baseline_deg %>%
dplyr::filter(p > 0.05) %>%
pull(AffyID)
# WRANGLE THE MEAN EXPRESSION DATA ####
mean_exprs_by_probe <- set01 %>%
exprs() %>%
as_tibble(rownames = "AffyID") %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb) %>% tibble(), ., by = "AffyID") %>%
mutate(
mean_exprs = set01 %>%
exprs() %>%
rowMeans(),
.after = Symb
)
genes <- mean_exprs_by_probe %>%
group_by(Symb) %>%
dplyr::slice_max(mean_exprs) %>%
dplyr::filter(Symb != "", AffyID %in% genes_baseline) %>%
distinct(Symb, .keep_all = TRUE) %>%
pull(AffyID)
set02 <- set01[featureNames(set01) %in% genes, ]
We need
to define the model structure for the contrasts we intend to make. Here
we will define 3 major contrast blocks; 1) effect of time in placebo
group, 2) effect of time in felzartamab group, and 3) the interactive
effect of time and treatment. Blocks 1 and 2 are assessed purely for
descriptive reasons as we are primarily concerned with block 3. We do
this for each time window (baseline to week 24, week 24 to week 52, and
baseline to week 52). We also include a molecular estimate for % cortex
as a covariate to adjust for difference in biopsy
composition.
# DEFINE FACTOR FOR CONTRASTS ####
Felzartamab_Followup <- set02$Felzartamab_Followup %>% droplevels()
cortex <- set02$Cortexprob
# DESIGN ####
design <- model.matrix(~ 0 + Felzartamab_Followup + cortex)
# CONTRAST DESIGN week24 - baseline ####
contrast_interaction_01 <- makeContrasts(
"x = (Felzartamab_FollowupWeek24_Felzartamab-Felzartamab_FollowupBaseline_Felzartamab)/2 -(Felzartamab_FollowupWeek24_Placebo-Felzartamab_FollowupBaseline_Placebo)/2",
levels = design
)
contrast_felzartamab_01 <- makeContrasts(
"x = (Felzartamab_FollowupWeek24_Felzartamab-Felzartamab_FollowupBaseline_Felzartamab)/2",
levels = design
)
contrast_placebo_01 <- makeContrasts(
"x = (Felzartamab_FollowupWeek24_Placebo-Felzartamab_FollowupBaseline_Placebo)/2",
levels = design
)
# CONTRAST DESIGN week52 - week24 ####
contrast_interaction_02 <- makeContrasts(
"x = (Felzartamab_FollowupWeek52_Felzartamab-Felzartamab_FollowupWeek24_Felzartamab)/2 - (Felzartamab_FollowupWeek52_Placebo-Felzartamab_FollowupWeek24_Placebo)/2",
levels = design
)
contrast_felzartamab_02 <- makeContrasts(
"x = (Felzartamab_FollowupWeek52_Felzartamab-Felzartamab_FollowupWeek24_Felzartamab)/2",
levels = design
)
contrast_placebo_02 <- makeContrasts(
"x = (Felzartamab_FollowupWeek52_Placebo-Felzartamab_FollowupWeek24_Placebo)/2",
levels = design
)
# CONTRAST DESIGN week52 - baseline####
contrast_interaction_03 <- makeContrasts(
"x = (Felzartamab_FollowupWeek52_Felzartamab-Felzartamab_FollowupBaseline_Felzartamab)/2 - (Felzartamab_FollowupWeek52_Placebo-Felzartamab_FollowupBaseline_Placebo)/2",
levels = design
)
contrast_felzartamab_03 <- makeContrasts(
"x = (Felzartamab_FollowupWeek52_Felzartamab-Felzartamab_FollowupBaseline_Felzartamab)/2",
levels = design
)
contrast_placebo_03 <- makeContrasts(
"x = (Felzartamab_FollowupWeek52_Placebo-Felzartamab_FollowupBaseline_Placebo)/2",
levels = design
)
We can
check the specified contrast matrix for interaction terms. This is for
the baseline to week 24 window.
contrast_interaction_01 %>% as_tibble(rownames = "level")
## # A tibble: 7 x 2
## level x = (Felzartamab_Fo~1
## <chr> <dbl>
## 1 Felzartamab_Fol~ 0.5
## 2 Felzartamab_Fol~ -0.5
## 3 Felzartamab_Fol~ 0
## 4 Felzartamab_Fol~ -0.5
## 5 Felzartamab_Fol~ 0.5
## 6 Felzartamab_Fol~ 0
## 7 cortex 0
## # i abbreviated name:
## # 1: `x = (Felzartamab_FollowupWeek24_Felzartamab-Felzartamab_FollowupBaseline_Felzartamab)/2 -(Felzartamab_FollowupWeek24_Placebo-Felzartamab_FollowupBaseline_Placebo)/2`
and this
is for the baseline to week 52 window.
contrast_interaction_03 %>% as_tibble(rownames = "level")
## # A tibble: 7 x 2
## level x = (Felzartamab_Fo~1
## <chr> <dbl>
## 1 Felzartamab_Fol~ 0.5
## 2 Felzartamab_Fol~ 0
## 3 Felzartamab_Fol~ -0.5
## 4 Felzartamab_Fol~ -0.5
## 5 Felzartamab_Fol~ 0
## 6 Felzartamab_Fol~ 0.5
## 7 cortex 0
## # i abbreviated name:
## # 1: `x = (Felzartamab_FollowupWeek52_Felzartamab-Felzartamab_FollowupBaseline_Felzartamab)/2 - (Felzartamab_FollowupWeek52_Placebo-Felzartamab_FollowupBaseline_Placebo)/2`
We now
fit the models we’ve specified
# FIT BLOCK week24 - baseline LIMMA MODEL ####
fit_interaction_1 <- limma::lmFit(set02, design)
cfit_interaction_1 <- limma::contrasts.fit(fit_interaction_1, contrast_interaction_01)
ebayes_interaction_1 <- limma::eBayes(cfit_interaction_1)
tab_interaction_1 <- limma::topTable(ebayes_interaction_1, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_interaction_1$stdev.unscaled * sqrt(ebayes_interaction_1$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
fit_felzartamab_1 <- limma::lmFit(set02, design)
cfit_felzartamab_1 <- limma::contrasts.fit(fit_felzartamab_1, contrast_felzartamab_01)
ebayes_felzartamab_1 <- limma::eBayes(cfit_felzartamab_1)
tab_felzartamab_1 <- limma::topTable(ebayes_felzartamab_1, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_felzartamab_1$stdev.unscaled * sqrt(ebayes_felzartamab_1$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
fit_placebo_1 <- limma::lmFit(set02, design)
cfit_placebo_1 <- limma::contrasts.fit(fit_placebo_1, contrast_placebo_01)
ebayes_placebo_1 <- limma::eBayes(cfit_placebo_1)
tab_placebo_1 <- limma::topTable(ebayes_placebo_1, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_placebo_1$stdev.unscaled * sqrt(ebayes_placebo_1$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
# FIT BLOCK week52 - week24 LIMMA MODEL ####
fit_interaction_2 <- limma::lmFit(set02, design)
cfit_interaction_2 <- limma::contrasts.fit(fit_interaction_2, contrast_interaction_02)
ebayes_interaction_2 <- limma::eBayes(cfit_interaction_2)
tab_interaction_2 <- limma::topTable(ebayes_interaction_2, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_interaction_2$stdev.unscaled * sqrt(ebayes_interaction_2$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
fit_felzartamab_2 <- limma::lmFit(set02, design)
cfit_felzartamab_2 <- limma::contrasts.fit(fit_felzartamab_2, contrast_felzartamab_02)
ebayes_felzartamab_2 <- limma::eBayes(cfit_felzartamab_2)
tab_felzartamab_2 <- limma::topTable(ebayes_felzartamab_2, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_felzartamab_2$stdev.unscaled * sqrt(ebayes_felzartamab_2$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
fit_placebo_2 <- limma::lmFit(set02, design)
cfit_placebo_2 <- limma::contrasts.fit(fit_placebo_2, contrast_placebo_02)
ebayes_placebo_2 <- limma::eBayes(cfit_placebo_2)
tab_placebo_2 <- limma::topTable(ebayes_placebo_2, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_placebo_2$stdev.unscaled * sqrt(ebayes_placebo_2$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
# FIT BLOCK week52 - baseline LIMMA MODEL ####
fit_interaction_3 <- limma::lmFit(set02, design)
cfit_interaction_3 <- limma::contrasts.fit(fit_interaction_3, contrast_interaction_03)
ebayes_interaction_3 <- limma::eBayes(cfit_interaction_3)
tab_interaction_3 <- limma::topTable(ebayes_interaction_3, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_interaction_3$stdev.unscaled * sqrt(ebayes_interaction_3$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
fit_felzartamab_3 <- limma::lmFit(set02, design)
cfit_felzartamab_3 <- limma::contrasts.fit(fit_felzartamab_3, contrast_felzartamab_03)
ebayes_felzartamab_3 <- limma::eBayes(cfit_felzartamab_3)
tab_felzartamab_3 <- limma::topTable(ebayes_felzartamab_3, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_felzartamab_3$stdev.unscaled * sqrt(ebayes_felzartamab_3$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
fit_placebo_3 <- limma::lmFit(set02, design)
cfit_placebo_3 <- limma::contrasts.fit(fit_placebo_3, contrast_placebo_03)
ebayes_placebo_3 <- limma::eBayes(cfit_placebo_3)
tab_placebo_3 <- limma::topTable(ebayes_placebo_3, adjust = "fdr", sort.by = "p", number = "all") %>%
as_tibble(rownames = "AffyID") %>%
mutate(se = (ebayes_placebo_3$stdev.unscaled * sqrt(ebayes_placebo_3$s2.post)) %>% as.vector(), .after = logFC) %>%
right_join(affymap219 %>% dplyr::select(AffyID, Symb, Gene, PBT), ., by = "AffyID")
The
results are now organized for tabulation
# MERGE BLOCKS ####
tab_block_1 <- tab_interaction_1 %>%
left_join(
tab_felzartamab_1 %>%
dplyr::select(AffyID, logFC) %>%
rename(flogFC = logFC),
by = "AffyID"
) %>%
left_join(
tab_placebo_1 %>%
dplyr::select(AffyID, logFC) %>%
rename(plogFC = logFC),
by = "AffyID"
) %>%
relocate(plogFC, flogFC, .before = logFC) %>%
arrange(P.Value)
tab_block_2 <- tab_interaction_2 %>%
left_join(
tab_felzartamab_2 %>%
dplyr::select(AffyID, logFC) %>%
rename(flogFC = logFC),
by = "AffyID"
) %>%
left_join(
tab_placebo_2 %>%
dplyr::select(AffyID, logFC) %>%
rename(plogFC = logFC),
by = "AffyID"
) %>%
relocate(plogFC, flogFC, .before = logFC) %>%
arrange(P.Value)
tab_block_3 <- tab_interaction_3 %>%
left_join(
tab_felzartamab_3 %>%
dplyr::select(AffyID, logFC) %>%
rename(flogFC = logFC),
by = "AffyID"
) %>%
left_join(
tab_placebo_3 %>%
dplyr::select(AffyID, logFC) %>%
rename(plogFC = logFC),
by = "AffyID"
) %>%
relocate(plogFC, flogFC, .before = logFC) %>%
arrange(P.Value)
# CALCULATE MEAN GENE EXPRESSION FOR EACH PROBE BETWEEN GROUPINGS ####
means <- fit_interaction_1 %>%
avearrays() %>%
as_tibble(rownames = "AffyID") %>%
mutate_if(is.numeric, ~ 2^. %>% round(0)) %>%
rename_at(vars(contains("Felz")), ~ str_remove(., "Felzartamab_Followup")) %>%
dplyr::select(-contains("Week12"), -any_of(c("cortex")))
# FORMAT TOPTABLES ####
table_interaction_1 <- tab_block_1 %>%
arrange(P.Value) %>%
mutate_at(c("P.Value", "adj.P.Val"), as.numeric) %>%
tibble() %>%
left_join(means, by = "AffyID") %>%
dplyr::select(
AffyID, Symb, Gene, PBT,
t, plogFC, flogFC, logFC, se, P.Value, adj.P.Val,
all_of(colnames(means))
) %>%
dplyr::rename(
p = P.Value,
FDR = adj.P.Val
)
table_interaction_2 <- tab_block_2 %>%
arrange(P.Value) %>%
mutate_at(c("P.Value", "adj.P.Val"), as.numeric) %>%
tibble() %>%
left_join(means, by = "AffyID") %>%
dplyr::select(
AffyID, Symb, Gene, PBT,
t, plogFC, flogFC, logFC, se, P.Value, adj.P.Val,
all_of(colnames(means))
) %>%
dplyr::rename(
p = P.Value,
FDR = adj.P.Val
)
table_interaction_3 <- tab_block_3 %>%
arrange(P.Value) %>%
mutate_at(c("P.Value", "adj.P.Val"), as.numeric) %>%
tibble() %>%
left_join(means, by = "AffyID") %>%
dplyr::select(
AffyID, Symb, Gene, PBT,
t, plogFC, flogFC, logFC, se, P.Value, adj.P.Val,
all_of(colnames(means))
) %>%
dplyr::rename(
p = P.Value,
FDR = adj.P.Val
)
deg <- tibble(
design = c(
"Baseline_vs_Week24",
"Week24_vs_Week52",
"Baseline_vs_Week52"
),
table = list(
table_interaction_1,
table_interaction_2,
table_interaction_3
)
)
# FORMAT TABLES TO MAKE FLEXTABLES ####
table_deg <- deg %>%
expand_grid(direction = c("all", "increased", "decreased")) %>%
relocate(direction, .after = design) %>%
mutate(
gene_tables = pmap(
list(direction, table),
function(direction, table) {
if (direction == "increased") {
table <- table %>%
dplyr::filter(logFC > 0) %>%
arrange(p)
} else if (direction == "decreased") {
table <- table %>%
dplyr::filter(logFC < 0) %>%
arrange(p)
}
table %>%
dplyr::select(-t, -se, -FDR, -contains("AffyID"), -contains("MMDx")) %>%
dplyr::slice(1:20) %>%
mutate(
Gene = Gene %>% str_remove("///.*"),
plogFC = plogFC %>% round(2),
flogFC = flogFC %>% round(2),
logFC = logFC %>% round(2),
p = case_when(
p < 0.0001 ~ p %>% formatC(digits = 0, format = "e"),
TRUE ~ p %>% formatC(digits = 4, format = "f")
)
)
}
)
)
# GLOBAL PARAMETERS FOR FLEXTABLES ####
header1 <- c(
"Gene\nsymbol", "Gene", "PBT",
rep("Differential expression", 4),
rep("Mean expression by group", 6)
)
header2 <- c(
"Gene\nsymbol", "Gene", "PBT",
"\u394\nplacebo\nlogFC", "\u394\nfelzartamab\nlogFC",
"\u394\u394\nlogFC", "\u394\u394\nP",
rep("Placebo", 3), rep("Felzartamab", 3)
)
header3 <- c(
"Gene\nsymbol", "Gene", "PBT",
"\u394\nplacebo\nlogFC", "\u394\nfelzartamab\nlogFC",
"\u394\u394\nlogFC", "\u394\u394\nP",
"Baseline\n(N=10)", "Week24\n(N=10)", "Week52\n(N=10)", "Baseline\n(N=10)", "Week24\n(N=10)", "Week52\n(N=10)"
)
# MAKE FORMATTED FLEXTABLES ####
flextables <- table_deg %>%
mutate(
flextable = pmap(
list(design, gene_tables),
function(design, gene_tables) {
if (design == "Baseline_vs_Week24") {
title <- paste("Table i. Top 20 differentially expressed genes between baseline and week24 in biopsies from placebo and Felzartamab treated patients (by P-value)", sep = "")
} else if (design == "Week24_vs_Week52") {
title <- paste("Table i. Top 20 differentially expressed genes between week24 and week52 in biopsies from placebo and Felzartamab treated patients (by P-value)", sep = "")
} else if (design == "Baseline_vs_Week52") {
title <- paste("Table i. Top 20 differentially expressed genes between baseline and week52 in biopsies from placebo and Felzartamab treated patients (by P-value)", sep = "")
}
gene_tables %>%
arrange(logFC) %>%
flextable::flextable() %>%
flextable::delete_part("header") %>%
flextable::add_header_row(top = TRUE, values = header3) %>%
flextable::add_header_row(top = TRUE, values = header2) %>%
flextable::add_header_row(top = TRUE, values = header1) %>%
flextable::add_header_row(top = TRUE, values = rep(title, ncol_keys(.))) %>%
flextable::merge_v(part = "header") %>%
flextable::merge_h(part = "header") %>%
flextable::border_remove() %>%
flextable::border(part = "header", border = officer::fp_border()) %>%
flextable::border(part = "body", border = officer::fp_border()) %>%
flextable::align(align = "center") %>%
flextable::align(align = "center", part = "header") %>%
flextable::font(fontname = "Arial", part = "all") %>%
flextable::fontsize(size = 7, part = "all") %>%
flextable::fontsize(size = 7, part = "footer") %>%
flextable::fontsize(i = 1, size = 12, part = "header") %>%
flextable::bold(part = "header") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::padding(padding = 0, part = "all") %>%
flextable::autofit()
}
)
)
Genes
suppressed by felzartamab in week 24 compared to baseline
(Note
that all ΔΔFDR are > 0.05. This is a consequence of differential
expression across a large selection of genes (5,267 in this case) with
small samples sizes (i.e., n = 10 by treatment group). Thus, treatment
effects on individual genes should be interpreted with caution. Instead,
geneset enrichment analysis (GSEA) will be used to assess bulk
trends).
flextables %>%
dplyr::filter(design == "Baseline_vs_Week24", direction == "decreased") %>%
pull(flextable) %>%
pluck(1)
Table i. Top 20 differentially expressed genes between baseline and week24 in biopsies from placebo and Felzartamab treated patients (by P-value) | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Gene | Gene | PBT | Differential expression | Mean expression by group | ||||||||
Δ | Δ | ΔΔ | ΔΔ | Placebo | Felzartamab | |||||||
Baseline | Week24 | Week52 | Baseline | Week24 | Week52 | |||||||
CXCL11 | chemokine (C-X-C motif) ligand 11 | ABMR-RAT,GRIT3,RAT,Rej-RAT | -0.16 | -1.01 | -0.85 | 0.0176 | 494 | 395 | 372 | 617 | 153 | 317 |
CXCL9 | chemokine (C-X-C motif) ligand 9 | ABMR-RAT,GRIT1,GRIT3,RAT,Rej-RAT | -0.14 | -0.94 | -0.79 | 0.0290 | 1,344 | 1,102 | 1,384 | 1,684 | 459 | 1,036 |
CXCL10 | chemokine (C-X-C motif) ligand 10 | ABMR-RAT,GRIT1,GRIT3,RAT,Rej-RAT | -0.21 | -0.98 | -0.77 | 0.0228 | 844 | 632 | 720 | 1,063 | 274 | 615 |
IDO1 | indoleamine 2,3-dioxygenase 1 | ABMR-RAT,GRIT3,RAT,Rej-RAT | -0.09 | -0.77 | -0.68 | 0.0103 | 338 | 298 | 296 | 409 | 140 | 251 |
LYZ | lysozyme | IRITD5,QCMAT | -0.01 | -0.67 | -0.66 | 0.0386 | 1,676 | 1,658 | 1,884 | 2,244 | 884 | 1,184 |
FCGR3A | Fc fragment of IgG, low affinity IIIa, receptor (CD16a) | ABMR-RAT,GRIT2,IRRAT950,RAT,Rej-RAT | 0.06 | -0.58 | -0.64 | 0.0167 | 550 | 602 | 613 | 571 | 256 | 352 |
GZMB | granzyme B | ABMR-RAT,QCAT,RAT,Rej-RAT | -0.01 | -0.62 | -0.61 | 0.0019 | 149 | 147 | 130 | 143 | 61 | 114 |
GBP1 | guanylate binding protein 1, interferon-inducible | ABMR-RAT,GRIT3,RAT,Rej-RAT | -0.03 | -0.61 | -0.59 | 0.0096 | 678 | 655 | 643 | 909 | 389 | 556 |
GNLY | granulysin | ABMR-RAT,DSAST,QCAT,RAT,Rej-RAT | -0.18 | -0.75 | -0.57 | 0.0285 | 192 | 149 | 134 | 165 | 58 | 100 |
SH2D1B | SH2 domain containing 1B | ABMR-RAT,DSAST,NKB,RAT | 0.07 | -0.44 | -0.51 | 0.0010 | 38 | 42 | 34 | 40 | 22 | 33 |
FCGR1A | Fc fragment of IgG, high affinity Ia, receptor (CD64) | GRIT2,IRRAT950,RAT,TCMR-RAT | 0.09 | -0.41 | -0.50 | 0.0304 | 131 | 148 | 161 | 152 | 86 | 86 |
KLRD1 | killer cell lectin-like receptor subfamily D, member 1 | ABMR-RAT,RAT,Rej-RAT | -0.06 | -0.49 | -0.43 | 0.0295 | 110 | 101 | 96 | 126 | 63 | 102 |
EPB41L3 | erythrocyte membrane protein band 4.1-like 3 | 0.22 | -0.20 | -0.42 | 0.0388 | 209 | 286 | 258 | 339 | 258 | 283 | |
LOC339059 | uncharacterized LOC339059 | 0.26 | -0.15 | -0.41 | 0.0203 | 51 | 73 | 55 | 66 | 53 | 59 | |
APOL1 | apolipoprotein L1 | ABMR-RAT,GRIT3,RAT,Rej-RAT | 0.07 | -0.32 | -0.39 | 0.0205 | 626 | 687 | 744 | 754 | 483 | 636 |
LYPD5 | LY6/PLAUR domain containing 5 | ABMR-RAT,RAT,Rej-RAT | 0.04 | -0.33 | -0.37 | 0.0255 | 27 | 28 | 27 | 30 | 19 | 26 |
TRAV12-2 | T cell receptor alpha variable 12-2 | 0.25 | -0.10 | -0.35 | 0.0173 | 39 | 55 | 39 | 49 | 43 | 43 | |
IL18RAP | interleukin 18 receptor accessory protein | 0.02 | -0.32 | -0.34 | 0.0233 | 38 | 39 | 41 | 42 | 27 | 33 | |
PRF1 | perforin 1 (pore forming protein) | ABMR-RAT,QCAT,RAT,Rej-RAT | -0.02 | -0.35 | -0.33 | 0.0382 | 152 | 148 | 137 | 145 | 89 | 123 |
PMM2 | phosphomannomutase 2 | 0.20 | -0.07 | -0.27 | 0.0380 | 38 | 50 | 41 | 46 | 42 | 49 |
Genes
suppressed by felzartamab in week 52 compared to baseline
(Note
that all ΔΔFDR are > 0.05. This is a consequence of differential
expression across a large selection of genes (5,267 in this case) with
small samples sizes (i.e., n = 10 by treatment group). Thus, treatment
effects on individual genes should be interpreted with caution. Instead,
geneset enrichment analysis (GSEA) will be used to assess bulk
trends).
flextables %>%
dplyr::filter(design == "Baseline_vs_Week52", direction == "decreased") %>%
pull(flextable) %>%
pluck(1)
Table i. Top 20 differentially expressed genes between baseline and week52 in biopsies from placebo and Felzartamab treated patients (by P-value) | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Gene | Gene | PBT | Differential expression | Mean expression by group | ||||||||
Δ | Δ | ΔΔ | ΔΔ | Placebo | Felzartamab | |||||||
Baseline | Week24 | Week52 | Baseline | Week24 | Week52 | |||||||
HNRNPK | heterogeneous nuclear ribonucleoprotein K | 0.28 | -0.38 | -0.66 | 0.0055 | 818 | 1,044 | 1,203 | 1,096 | 1,059 | 649 | |
COL1A1 | collagen, type I, alpha 1 | COLA1356,FICOL,IRITD5,LivGST_UP | 0.38 | -0.26 | -0.64 | 0.0075 | 310 | 333 | 526 | 329 | 320 | 230 |
NFIX | nuclear factor I/X (CCAAT-binding transcription factor) | CT1 | 0.42 | -0.15 | -0.57 | 0.0012 | 162 | 179 | 289 | 221 | 167 | 180 |
RHOA | ras homolog family member A | IRRAT950 | 0.28 | -0.29 | -0.57 | 0.0049 | 691 | 803 | 1,017 | 923 | 809 | 620 |
COL1A2 | collagen, type I, alpha 2 | FICOL,IRITD5,LivGST_UP | 0.26 | -0.31 | -0.57 | 0.0118 | 1,238 | 1,152 | 1,769 | 1,534 | 1,194 | 997 |
ITGB3 | integrin beta 3 | IRRAT30,IRRAT950 | 0.34 | -0.21 | -0.55 | 0.0100 | 105 | 137 | 169 | 110 | 103 | 82 |
LOC100129518 | uncharacterized LOC100129518 | GRIT3,IRRAT950 | 0.25 | -0.22 | -0.47 | 0.0050 | 429 | 546 | 605 | 527 | 461 | 390 |
ACTB | actin, beta | IRRAT950 | 0.15 | -0.31 | -0.47 | 0.0077 | 6,451 | 7,352 | 7,993 | 7,274 | 5,662 | 4,720 |
SPARC | secreted protein, acidic, cysteine-rich (osteonectin) | IRITD3 | 0.16 | -0.30 | -0.46 | 0.0100 | 2,411 | 2,532 | 3,011 | 2,343 | 2,094 | 1,550 |
DNAJA4 | DnaJ (Hsp40) homolog, subfamily A, member 4 | IRRAT950 | 0.27 | -0.14 | -0.42 | 0.0095 | 49 | 68 | 72 | 68 | 61 | 56 |
SLFN5 | schlafen family member 5 | 0.19 | -0.20 | -0.40 | 0.0050 | 39 | 44 | 51 | 49 | 38 | 37 | |
CYR61 | cysteine-rich, angiogenic inducer, 61 | IRITD3,IRRAT950 | 0.14 | -0.26 | -0.40 | 0.0074 | 271 | 268 | 330 | 338 | 234 | 236 |
SDCBP | syndecan binding protein | 0.24 | -0.14 | -0.38 | 0.0120 | 1,135 | 1,404 | 1,582 | 1,478 | 1,358 | 1,224 | |
SBSPON | somatomedin B and thrombospondin type 1 domain containing | 0.31 | -0.08 | -0.38 | 0.0123 | 217 | 250 | 332 | 188 | 249 | 169 | |
PAFAH1B2 | platelet-activating factor acetylhydrolase 1b, catalytic subunit 2 (30kDa) | 0.23 | -0.13 | -0.36 | 0.0099 | 309 | 372 | 423 | 386 | 396 | 323 | |
TMEM2 | transmembrane protein 2 | 0.10 | -0.26 | -0.36 | 0.0118 | 270 | 307 | 311 | 318 | 259 | 222 | |
CALM2 | calmodulin 2 (phosphorylase kinase, delta) | CT1,IRITD3 | 0.12 | -0.25 | -0.36 | 0.0138 | 1,304 | 1,427 | 1,538 | 1,623 | 1,342 | 1,155 |
WNK1 | WNK lysine deficient protein kinase 1 | 0.26 | -0.08 | -0.34 | 0.0104 | 703 | 884 | 1,012 | 894 | 996 | 799 | |
CEBPB | CCAAT/enhancer binding protein (C/EBP), beta | IRITD3 | 0.11 | -0.22 | -0.33 | 0.0123 | 419 | 452 | 488 | 417 | 333 | 307 |
STAT6 | signal transducer and activator of transcription 6, interleukin-4 induced | 0.09 | -0.20 | -0.30 | 0.0144 | 671 | 710 | 765 | 746 | 616 | 563 |
We applied
geneset enrichment analysis (GSEA) to identify pathways affected by
felzartamab treatment. Multiple libraries were screened to encompass a
broad spectrum of biological pathways, including Gene Ontology (GO),
Disease Ontology Semantic and Enrichment analysis (DOSE), Kyoto
Encyclopedia of Genes and Genomes (KEGG), Reactome, Molecular Signatures
Database (MsigDB), and WikiPathways (wiki) libraries. An additional
de-novo injury geneset was also assesed. This injury geneset was based
on single-nuclei transcriptomics assessed from healthy and injury kidney
allografts. Geneset enrichment was chosen specifically because it is a
rank-based test and does not require cutoffs based on p-values or fold
change. Nonetheless, here we decided apriori that we would pre-screen
genes based on their p-values from differential expression analyses
described in Section 3. The intent for this pre-screening was to limit
interpretation from GSEA to genes which had the greatest measured
difference between placebo and felzartamab arms over time. Note that
there are many ways for carrying out pathway and functional enrichment
analyses. Also note that geneset libraries are frequently updated and
you may find slightly different results based on the package version
being used. For example, here we encountered such a difference in the
DOSE library, where versions later than 3.30.1 identified different
pathways than version 3.30.1. Thus, we have included a tarball of the
package version (v3.30.1) used for the original analyses in this paper
for convenient installation and replication of the presented
findings.
References:
• Yu, G., Wang, L.G., Han, Y. & He, Q.Y. clusterProfiler: an R package for comparing biological themes among gene clusters. OMICS 16, 284-287 (2012).
• Wu, T., et al. clusterProfiler 4.0: A universal enrichment tool for interpreting omics data. Innovation (Camb) 2, 100141 (2021).
• Hinze, C., et al. Single-cell transcriptomics reveals common epithelial response patterns in human acute kidney injury. Genome Med 14, 103 (2022).
First we
need to load the necessary R packages and data.
# HOUSEKEEPING ####
# CRAN libraries
library(tidyverse)
library(flextable)
library(officer)
library(msigdbr)
# Bioconductor libraries
# DOSE v 3.30.1 required for reproducibility
# install.packages("C:/R/CD38-effect-of-treatment/data/DOSE_3.30.1.tar.gz", repos = NULL, type = "source")
library(DOSE)
library(ReactomePA)
library(Biobase)
library(clusterProfiler)
library(org.Hs.eg.db)
library(pRoloc)
# Custom operators and functions
"%nin%" <- function(a, b) match(a, b, nomatch = 0) == 0
# load DE data
load("C:/R/CD38-effect-of-treatment/results/deg.RData")
# Seed for reproducibility
set.seed(42)
Geneset
enrichment analyses using the ClusterProfiler package have simlar, but
not identical implementation. This section will carry out GSEA on all
described libraries, after which the results will be collated and
summarized. For all GSEA, genes are ranked by
t-value.
GO
library
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_go <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
clusterProfiler::gseGO(
gene = genes_gsea, ont = "BP", OrgDb = org.Hs.eg.db,
minGSSize = 10, maxGSSize = 200,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# SIMPLIFY GO TERMS ####
set.seed(42)
gsea_go_simplified <- gsea_go %>%
mutate(gsea_simplified = map(gsea, clusterProfiler::simplify))
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_go_simplified_formatted <- gsea_go_simplified %>%
mutate(
gsea_tables = map(
gsea_simplified,
function(gsea_simplified) {
gsea_simplified %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(
sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"),
Description = factor(Description, levels = Description, ordered = TRUE)
)
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_go_tables <- gsea_go_simplified_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
slice_min(pvalue, n = 20) %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_go <- gsea_go_tables %>%
mutate(db = "GO", .before = 1)
names(gsea_go$gsea_flextables) <- gsea_go$design
DOSE
library
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = clusterProfiler::bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_dose <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
DOSE::gseDO(
gene = genes_gsea,
minGSSize = 10, maxGSSize = 200,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_dose_formatted <- gsea_dose %>%
mutate(
gsea_tables = map(
gsea,
function(gsea) {
gsea %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"))
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_dose_tables <- gsea_dose_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
slice_min(pvalue, n = 20) %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_dose <- gsea_dose_tables %>%
mutate(db = "DOSE", .before = 1)
names(gsea_dose$gsea_flextables) <- gsea_dose$design
KEGG
library
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_kegg <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
clusterProfiler::gseKEGG(
gene = genes_gsea, organism = "hsa",
minGSSize = 10, maxGSSize = 200,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_kegg_formatted <- gsea_kegg %>%
mutate(
gsea_tables = map(
gsea,
function(gsea) {
gsea %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(
sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"),
Description = factor(Description, levels = Description, ordered = TRUE)
)
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_kegg_tables <- gsea_kegg_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
slice_min(pvalue, n = 20) %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_kegg <- gsea_kegg_tables %>%
mutate(db = "kegg", .before = 1)
names(gsea_kegg$gsea_flextables) <- gsea_kegg$design
MSigDB
library
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# DEFINE MSigDB PATHWAYS ####
gene_sets <- msigdbr(category = "H")
map <- gene_sets[, c("gs_name", "entrez_gene")]
map$entrez_gene <- as.character(map$entrez_gene)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_msigdb <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
clusterProfiler::GSEA(
gene = genes_gsea, TERM2GENE = map,
minGSSize = 10, maxGSSize = 200,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_msigdb_formatted <- gsea_msigdb %>%
mutate(
gsea_tables = map(
gsea,
function(gsea) {
gsea %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(
sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"),
Description = factor(Description, levels = Description, ordered = TRUE)
)
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_msigdb_tables <- gsea_msigdb_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
# slice_min(pvalue, n = 20) %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_msigdb <- gsea_msigdb_tables %>%
mutate(db = "msigdb", .before = 1)
names(gsea_msigdb$gsea_flextables) <- gsea_msigdb$design
REACTOME
library
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_reactome <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
ReactomePA::gsePathway(
gene = genes_gsea, organism = "human",
minGSSize = 10, maxGSSize = 200,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_reactome_formatted <- gsea_reactome %>%
mutate(
gsea_tables = map(
gsea,
function(gsea) {
gsea %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(
sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"),
Description = factor(Description, levels = Description, ordered = TRUE)
)
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_reactome_tables <- gsea_reactome_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
slice_min(pvalue, n = 20) %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_reactome <- gsea_reactome_tables %>%
mutate(db = "Reactome", .before = 1)
names(gsea_reactome$gsea_flextables) <- gsea_reactome$design
WikiPathways
library
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_wiki <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
clusterProfiler::gseWP(
gene = genes_gsea, organism = "Homo sapiens",
minGSSize = 10, maxGSSize = 200,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_wiki_formatted <- gsea_wiki %>%
mutate(
gsea_tables = map(
gsea,
function(gsea) {
gsea %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(
sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"),
Description = factor(Description, levels = Description, ordered = TRUE)
)
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_wiki_tables <- gsea_wiki_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
# slice_min(pvalue, n = 20) %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_wiki <- gsea_wiki_tables %>%
mutate(db = "wiki", .before = 1)
names(gsea_wiki$gsea_flextables) <- gsea_wiki$design
AKI
geneset
# load gene lists
load("C:/R/CD38-effect-of-treatment/data/hinze2022_injury_markers.RData")
# DEFINE INJURY PATHWAYS ####
map_aki <- injury_markers %>% dplyr::select(gs_name, entrez_gene)
map_aki_joined <- injury_markers %>%
dplyr::select(gs_name, entrez_gene) %>%
mutate(gs_name = "AKI")
# WRANGLE GENE DATA FOR GSEA ####
data <- deg %>%
mutate(
genes_gsea = map(
table,
function(table) {
table %>%
dplyr::filter(p < 0.05) %>%
arrange(t %>% dplyr::desc()) %>%
dplyr::mutate(
ENTREZID = bitr(Symb,
fromType = "SYMBOL",
toType = c("ENTREZID"),
OrgDb = org.Hs.eg.db,
drop = FALSE
) %>% pull(ENTREZID)
) %>%
drop_na(ENTREZID) %>%
dplyr::select(t, ENTREZID) %>%
pull(t, ENTREZID)
}
)
)
# GENESET ENRICHMENT ANALYSES (GSEA) ####
set.seed(42)
gsea_aki <- data %>%
mutate(
gsea = map(
genes_gsea,
function(genes_gsea) {
clusterProfiler::GSEA(
gene = genes_gsea, TERM2GENE = map_aki_joined,
minGSSize = 5, maxGSSize = Inf,
pvalueCutoff = 0.001, pAdjustMethod = "fdr", seed = TRUE
) %>% clusterProfiler::setReadable(OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_aki_formatted <- gsea_aki %>%
mutate(
gsea_tables = map(
gsea,
function(gsea) {
gsea %>%
as_tibble() %>%
arrange(pvalue) %>%
mutate(
sign = case_when(NES < 0 ~ "Down Regulated", NES > 0 ~ "Up Regulated"),
Description = "Cellular response to acute kidney injury"
)
}
)
)
# FORMAT TABLES FOR GENESET ENRICHMENT ANALYSES (GSEA) ####
gsea_aki_tables <- gsea_aki_formatted %>%
mutate(
gsea_flextables = map(
gsea_tables,
function(gsea_tables) {
gsea_tables %>%
mutate(
NES = NES %>% round(2),
pvalue = case_when(
pvalue < 0.0001 ~ pvalue %>% formatC(digits = 0, format = "e"),
TRUE ~ pvalue %>% formatC(digits = 4, format = "f")
),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(Description, setSize, NES, pvalue, FDR, dplyr::any_of("core_enrichment")) %>%
flextable::flextable() %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core_enrichment", align = "left", part = "body") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::autofit()
}
)
)
# PREPARE THE RESULTS FOR EXPORT ####
gsea_aki <- gsea_aki_tables %>%
mutate(db = "Hinze", .before = 1)
names(gsea_aki$gsea_flextables) <- gsea_aki$design
GSEA
results from each library can now be combined and summarized. Note that
pathway annotations need to be interpreted in light of their
developement in combination with how GSEA determines their enrichment,
and the relevance of enriched pathways must be evaluated in the context
of transplantation. For our interpretation, we avail to the pathway
annotation as well as core enrichment genes contributing to enrichment
via GSEA. We have simplified the interpretation of enriched pathways
based on the overlap in biological function of their core enrichement
genes.
# JOIN THE GSEA RESULTS ####
gsea <- purrr::reduce(
list(
gsea_go,
gsea_msigdb,
gsea_wiki,
gsea_dose,
gsea_kegg,
gsea_reactome,
gsea_aki
), bind_rows
) %>%
mutate(keep = map_dbl(gsea_tables, nrow)) %>%
dplyr::filter(keep > 0) %>%
dplyr::select(db, design, genes_gsea, gsea_tables) %>%
mutate(gsea_tables = map(gsea_tables, mutate, Description = Description %>% as.character()))
# DEFINE INTERPREATION BASED ON PATHWAYS ####
pathway_interpretation <- gsea %>%
dplyr::select(design, gsea_tables) %>%
unnest(everything()) %>%
mutate(
interpretation = case_when(
ID %in% c(
"GO:0006952",
"GO:0009605",
"GO:0009607",
"GO:0043207",
"GO:0044419",
"GO:0051707",
"GO:0006955",
"GO:0002376",
"DOID:2914",
"DOID:77",
"DOID:1579",
"R-HSA-168256"
) & design == "Baseline_vs_Week24" ~ "immune response",
ID %in% c(
"R-HSA-168256",
"R-HSA-168249",
"R-HSA-1643685",
"R-HSA-5663205"
) & design == "Baseline_vs_Week52" ~ "immune-related response to injury",
ID %in% c("AKI", "R-HSA-109582") ~ "response to injury",
ID %in% c(
"R-HSA-9006934",
"R-HSA-597592",
"R-HSA-5653656",
"R-HSA-392499"
) ~ "response to injury",
ID %in% c(
"R-HSA-2262752",
"R-HSA-8953897"
) ~ "response to injury",
)
) %>%
dplyr::select(design, ID, Description, interpretation)
# WRANGLE THE GSEA TABLES ####
gsea_tables <- gsea %>%
mutate(
gsea_tables = pmap(
list(design, gsea_tables),
function(design, gsea_tables) {
gsea_tables %>%
mutate(
design = design,
Description = Description %>% as.character()
) %>%
left_join(pathway_interpretation, by = c("ID", "Description", "design")) %>%
dplyr::select(ID, Description, interpretation, setSize, NES, p.adjust, core_enrichment) %>%
distinct(ID, setSize, interpretation, .keep_all = TRUE)
}
)
) %>%
unnest(gsea_tables) %>%
arrange(p.adjust) %>%
dplyr::filter(p.adjust < 0.001) %>%
nest(.by = design, gsea_table = c(-design, -genes_gsea)) %>%
left_join(gsea %>% dplyr::select(design, genes_gsea) %>% distinct(design, .keep_all = TRUE),
by = "design"
)
# MAKE FLEXTABLES ####
gsea_flextables <- gsea_tables %>%
mutate(
flextable = pmap(
list(design, gsea_table),
function(design, gsea_table) {
cellWidths <- c(2, 3, 7, 4, 1.5, 1.5, 1.5, 13) # for individual tables up or down
if (design == "Baseline_vs_Week24") {
title <- paste("Table Si. Functional enrichment analysis of genes affects by felzartamab treatment between baseline and week24 (by FDR)", sep = "")
} else if (design == "Week24_vs_Week52") {
title <- paste("Table Si. Functional enrichment analysis of genes affects by felzartamab treatment between week24 and week52 (by FDR)", sep = "")
} else if (design == "Baseline_vs_Week52") {
title <- paste("Table Si. Functional enrichment analysis of genes affects by felzartamab treatment between baseline and week52 (by FDR)", sep = "")
}
gsea_table %>%
mutate(
library = db,
pathway = Description,
"core enrichment genes" = core_enrichment %>% str_replace_all("/", ", "),
"n genes" = setSize,
NES = NES %>% round(2),
FDR = case_when(
p.adjust < 0.0001 ~ p.adjust %>% formatC(digits = 0, format = "e"),
TRUE ~ p.adjust %>% formatC(digits = 4, format = "f")
),
) %>%
dplyr::select(library, ID, pathway, interpretation, "n genes", NES, FDR, "core enrichment genes") %>%
flextable::flextable() %>%
flextable::add_header_row(top = TRUE, values = rep(title, ncol_keys(.))) %>%
flextable::merge_v(part = "header") %>%
flextable::merge_h(part = "header") %>%
flextable::border_remove() %>%
flextable::border(border = officer::fp_border(), part = "all") %>%
flextable::padding(padding = 0) %>%
flextable::padding(j = "core enrichment genes", padding.left = 5) %>%
flextable::align(align = "center", part = "all") %>%
flextable::align(j = "core enrichment genes", align = "left", part = "body") %>%
flextable::font(fontname = "Arial", part = "all") %>%
flextable::fontsize(size = 10, part = "body") %>%
flextable::fontsize(size = 12, part = "header") %>%
flextable::bg(bg = "white", part = "all") %>%
flextable::width(width = cellWidths, unit = "cm")
}
)
)
Summary
of all enriched pathways at week 24 (compared to
baseline)
# PRINT FLETABLES TO POWERPOINT ####
gsea_flextables %>%
dplyr::filter(design == "Baseline_vs_Week24") %>%
pull(flextable) %>%
pluck(1)
Table Si. Functional enrichment analysis of genes affects by felzartamab treatment between baseline and week24 (by FDR) | |||||||
---|---|---|---|---|---|---|---|
library | ID | pathway | interpretation | n genes | NES | FDR | core enrichment genes |
GO | GO:0006952 | defense response | immune response | 20 | -3.07 | 3e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0009605 | response to external stimulus | immune response | 20 | -3.07 | 3e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0009607 | response to biotic stimulus | immune response | 20 | -3.07 | 3e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0043207 | response to external biotic stimulus | immune response | 20 | -3.07 | 3e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0044419 | biological process involved in interspecies interaction between organisms | immune response | 20 | -3.07 | 3e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0051707 | response to other organism | immune response | 20 | -3.07 | 3e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0006955 | immune response | immune response | 23 | -2.93 | 5e-05 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, TRAV12-2, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
GO | GO:0002376 | immune system process | immune response | 24 | -2.75 | 0.0002 | PTPRC, TRIM4, LYZ, PRF1, FCGR1A, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, APOL1, CXCL11, TRAV12-2, FCGR3A, IDO1, GBP1, GZMB, SH2D1B |
Reactome | R-HSA-168256 | Immune System | immune response | 14 | -2.51 | 0.0005 | PIK3AP1, PTPRC, TRIM4, KLRF1, LYZ, FCGR1A, KLRD1, GNLY, IL18RAP, CXCL10, FCGR3A, GBP1, SH2D1B |
DOSE | DOID:2914 | immune system disease | immune response | 10 | -2.56 | 0.0007 | PRF1, FCGR1A, CXCL9, IL18RAP, CXCL10, CXCL11, FCGR3A, IDO1, GZMB |
DOSE | DOID:77 | gastrointestinal system disease | immune response | 15 | -2.59 | 0.0007 | PTPRC, EPB41L3, LYZ, PRF1, KLRD1, CXCL9, GNLY, IL18RAP, CXCL10, CXCL11, FCGR3A, IDO1, GZMB |
DOSE | DOID:1579 | respiratory system disease | immune response | 10 | -2.56 | 0.0007 | PRF1, FCGR1A, CXCL9, IL18RAP, CXCL10, CXCL11, FCGR3A, IDO1, GZMB |
Summary
of all enriched pathways at week 52 (compared to
baseline)
gsea_flextables %>%
dplyr::filter(design == "Baseline_vs_Week52") %>%
pull(flextable) %>%
pluck(1)
Table Si. Functional enrichment analysis of genes affects by felzartamab treatment between baseline and week52 (by FDR) | |||||||
---|---|---|---|---|---|---|---|
library | ID | pathway | interpretation | n genes | NES | FDR | core enrichment genes |
Reactome | R-HSA-168249 | Innate Immune System | immune-related response to injury | 20 | -3.18 | 6e-06 | LRRFIP1, C1QB, JUN, ATP6V1A, NCF2, CFI, EP300, FCGR1A, CAPZA1, CAPZA2, ADAM10, HSPA1A, STAT6, CALM2, SDCBP, PAFAH1B2, ACTB, RHOA |
Reactome | R-HSA-168256 | Immune System | immune-related response to injury | 35 | -3.08 | 2e-05 | LRRFIP1, C1QB, CANX, JUN, ATP6V1A, RNF213, NCF2, PIK3AP1, VIM, CFI, EP300, FCGR1A, CAPZA1, CAPZA2, ADAM10, HSPA1A, IFNGR1, STAT6, CALM2, SDCBP, COL1A2, PAFAH1B2, ACTB, COL1A1, RHOA |
Reactome | R-HSA-1643685 | Disease | immune-related response to injury | 32 | -2.92 | 2e-05 | LRRFIP1, PPFIBP1, CANX, JUN, RNF213, CD163, PIK3AP1, TPM3, SIN3A, EP300, FCGR1A, RAN, ADAM10, HSPA1A, IFNGR1, CALM2, CEBPB, SBSPON, COL1A2, ITGB3, ACTB, COL1A1, HNRNPK |
Hinze | AKI | Cellular response to acute kidney injury | response to injury | 39 | -2.75 | 4e-05 | SPTBN1, NRP1, KLF6, HLA-DRB1, DOCK11, DUSP1, CHSY1, LDHA, CDC42SE2, TPM4, PTBP3, ARHGAP29, SYNE2, SERP1, CORO1C, LRRFIP1, PPFIBP1, CANX, JUN, SCOC, RNF213, SPP1, PIK3AP1, VIM, SVIL, TPM3, RAN, ANXA5, ADAM10, HSPA1A, CEBPB, SPARC, ACTB, HNRNPK, SLFN5, RHOA |
Reactome | R-HSA-9006934 | Signaling by Receptor Tyrosine Kinases | response to injury | 19 | -2.59 | 0.0002 | ATP6V1A, SPP1, NCF2, SIN3A, EP300, ADAM10, STAT6, CALM2, COL1A2, SPARC, ITGB3, ACTB, COL1A1, RHOA |
Reactome | R-HSA-597592 | Post-translational protein modification | response to injury | 17 | -2.68 | 0.0002 | CANX, SPP1, PRMT3, PPP6R3, SIN3A, EP300, CAPZA1, CAPZA2, ADAM10, CALM2, SBSPON, ACTB, HNRNPK, RHOA |
Reactome | R-HSA-109582 | Hemostasis | response to injury | 17 | -2.54 | 0.0005 | SIN3A, CAPZA1, CAPZA2, ANXA5, CALM2, COL1A2, SPARC, ITGB3, ACTB, COL1A1, RHOA |
Reactome | R-HSA-392499 | Metabolism of proteins | response to injury | 19 | -2.42 | 0.0005 | PRMT3, PPP6R3, SIN3A, EP300, CAPZA1, CAPZA2, ADAM10, CALM2, SBSPON, ACTB, HNRNPK, RHOA |
Reactome | R-HSA-5653656 | Vesicle-mediated transport | response to injury | 15 | -2.47 | 0.0008 | SCOC, AP4B1, CD163, PPP6R3, CAPZA1, CAPZA2, CALM2, COL1A2, SPARC, PAFAH1B2, ACTB, COL1A1 |