Skip to content

Commit fc83bb5

Browse files
authored
Add cycombine to batch (#90)
* rename previous cycombine implementations * changed some columns to factor * add cycombin all controls to goal * add no control to goal * add one control to goal * Update CHANGELOG.md
1 parent 5ac7e51 commit fc83bb5

15 files changed

Lines changed: 645 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
* Added CytoNorm with aggregate of samples as controls (`methods/cytonorm_no_controls`).
5454
* Added parameters to tune CytoNorm.
5555

56+
* Added cyCombine correction to a reference batch (PR #90).
5657
* Added `metrics/bras`
5758

5859
## MAJOR CHANGES

src/methods/cycombine_all_controls/config.vsh.yaml renamed to src/methods/cycombine_all_controls_to_goal/config.vsh.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ __merge__: ../../api/comp_method.yaml
77

88
# A unique identifier for your component (required).
99
# Can contain only lowercase letters or underscores.
10-
name: cycombine_all_controls
10+
name: cycombine_all_controls_to_goal
1111
# A relatively short label, used when rendering visualisations (required)
12-
label: cyCombine with all controls
12+
label: cyCombine (all-controls, to-goal)
1313
# A one sentence summary of how this method works (required). Used when
1414
# rendering summary tables.
15-
summary: "cyCombine with all control samples"
15+
summary: "cyCombine run with all control samples, correcting to a goal batch"
1616
description: |
1717
cyCombine perform batch integration using self-organizing maps and ComBat.
1818
It first uses self-organizing maps (SOM) to group similar cells into clusters,
1919
then applies a ComBat-based method to correct batch effects within
2020
each cluster.
21-
Here, we run cyCombine using all control samples (replicates in cyCombine terminology)
22-
and square SOM grid.
21+
Here, we run cyCombine using all control samples (replicates, in cyCombine terms),
22+
with a square SOM grid and correct the batches to batch 1.
2323
The size of the SOM grid is varied linearly between value of 6-16, with default set to 8
2424
following the default value in create_som function in cyCombine.
2525
Rlen, which is the number of times data is presented to the SOM network and can impact
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
requireNamespace("cyCombine", quietly = TRUE)
2+
requireNamespace("anndata", quietly = TRUE)
3+
4+
## VIASH START
5+
par <- list(
6+
input = "resources_test/task_cyto_batch_integration/mouse_spleen_flow_cytometry_subset/unintegrated_censored.h5ad",
7+
output = "resources_test/task_cyto_batch_integration/mouse_spleen_flow_cytometry_subset/output.h5ad",
8+
som_grid_size = 8,
9+
rlen = 10
10+
)
11+
meta <- list(name = "cycombine_all_controls_to_goal")
12+
## VIASH END
13+
14+
cat("Reading input files\n")
15+
input_adata <- anndata::read_h5ad(par[["input"]])
16+
17+
cat("Preparing input Anndata and df\n")
18+
19+
markers_to_correct <- input_adata$var_names[input_adata$var$to_correct]
20+
21+
adata_to_correct <- input_adata[, markers_to_correct]
22+
23+
# convert adata to data.frame
24+
25+
df_to_correct <- as.data.frame(
26+
adata_to_correct$layers[["preprocessed"]]
27+
)
28+
df_to_correct$batch <- as.factor(adata_to_correct$obs$batch)
29+
df_to_correct$sample <- as.factor(adata_to_correct$obs$sample)
30+
31+
# add an "anchor" column which specify which samples are the technical replicates
32+
# this is a bit weird as the anchor information should be a column name or character vector
33+
# giving all anchors the same label and every other sample a unique label.
34+
# so for our controls (replicate samples), i have to give it the same name,
35+
# while non-replicate sample should be given a unique identifier.
36+
# hence for non-replicate samples, we will just use the values in the "sample" column.
37+
# for the replicate samples, we will use the values in the "is_control" column.
38+
# that way, samples from the same donor will have the same label, and
39+
# cycombine can identify which samples are technical replicates of each other.
40+
# the as.character function is needed as otherwise we will get NAs for those controls..
41+
42+
df_to_correct$anchor <- ifelse(
43+
adata_to_correct$obs$is_control == 0,
44+
as.character(adata_to_correct$obs$sample),
45+
paste0("control_", adata_to_correct$obs$is_control)
46+
)
47+
df_to_correct$anchor <- as.factor(df_to_correct$anchor)
48+
49+
lineage_markers <- as.vector(input_adata$var_names[
50+
input_adata$var$marker_type == "lineage"
51+
])
52+
53+
54+
cat("Run cyCombine\n")
55+
56+
# use the default parameters in normalize
57+
# do the normalisation on all markers to be corrected
58+
# use z-score normalisation because we are merging batches
59+
# from a single study.
60+
df_to_correct_norm <- cyCombine::normalize(
61+
df = df_to_correct,
62+
markers = markers_to_correct,
63+
norm_method = "scale",
64+
ties.method = "average"
65+
)
66+
67+
# again, using default parameter values
68+
cluster_labels <- cyCombine::create_som(
69+
df = df_to_correct_norm,
70+
markers = lineage_markers,
71+
rlen = par[["rlen"]],
72+
seed = 42,
73+
xdim = par[["som_grid_size"]],
74+
ydim = par[["som_grid_size"]]
75+
)
76+
77+
# Batch correct using default parameter values
78+
df_corrected <- cyCombine::correct_data(
79+
df = df_to_correct,
80+
label = cluster_labels,
81+
markers = markers_to_correct,
82+
method = "ComBat",
83+
covar = NULL,
84+
anchor = "anchor",
85+
ref.batch = "1",
86+
parametric = TRUE
87+
)
88+
89+
cat("Preparing output Anndata\n")
90+
df_not_corrected <- as.data.frame(
91+
input_adata[, !input_adata$var$to_correct]$layers[["preprocessed"]]
92+
)
93+
94+
df_output <- cbind(
95+
df_corrected[, markers_to_correct],
96+
df_not_corrected
97+
)
98+
99+
# reorder column to match input_adata
100+
df_output <- df_output[, input_adata$var_names]
101+
102+
output <- anndata::AnnData(
103+
obs = input_adata$obs[, integer(0)],
104+
var = input_adata$var[colnames(df_output), integer(0)],
105+
layers = list(integrated = as.matrix(df_output)),
106+
uns = list(
107+
dataset_id = input_adata$uns$dataset_id,
108+
method_id = meta$name,
109+
parameters = list(
110+
"normalize" = list(
111+
"markers" = markers_to_correct,
112+
"norm_method" = "scale",
113+
"ties.method" = "average"
114+
),
115+
"create_som" = list(
116+
"markers" = lineage_markers,
117+
"seed" = 42,
118+
"rlen" = par[["rlen"]],
119+
"xdim" = par[["som_grid_size"]],
120+
"ydim" = par[["som_grid_size"]]
121+
),
122+
"correct_data" = list(
123+
"markers" = markers_to_correct,
124+
"method" = "ComBat",
125+
"covar" = NULL,
126+
"anchor" = "anchor",
127+
"ref.batch" = "1",
128+
"parametric" = TRUE
129+
)
130+
)
131+
)
132+
)
133+
134+
cat("Write output AnnData to file\n")
135+
136+
output$write_h5ad(par[["output"]], compression = "gzip")

src/methods/cycombine_one_control/config.vsh.yaml renamed to src/methods/cycombine_all_controls_to_mid/config.vsh.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ __merge__: ../../api/comp_method.yaml
77

88
# A unique identifier for your component (required).
99
# Can contain only lowercase letters or underscores.
10-
name: cycombine_one_control
10+
name: cycombine_all_controls_to_mid
1111
# A relatively short label, used when rendering visualisations (required)
12-
label: cyCombine with one control
12+
label: cyCombine (all-controls, to-middle)
1313
# A one sentence summary of how this method works (required). Used when
1414
# rendering summary tables.
15-
summary: "cyCombine with one control samples"
15+
summary: "cyCombine run with all control samples, correcting to a midpoint"
1616
description: |
1717
cyCombine perform batch integration using self-organizing maps and ComBat.
1818
It first uses self-organizing maps (SOM) to group similar cells into clusters,
1919
then applies a ComBat-based method to correct batch effects within
2020
each cluster.
21-
Here, we run cyCombine with control samples from just one donor
22-
(replicates in cyCombine terminology) using square SOM grid.
21+
Here, we run cyCombine using all control samples (replicates, in cyCombine terms),
22+
with a square SOM grid and correct the batches to a midpoint derived from all batches.
2323
The size of the SOM grid is varied linearly between value of 6-16, with default set to 8
2424
following the default value in create_som function in cyCombine.
2525
Rlen, which is the number of times data is presented to the SOM network and can impact
@@ -53,6 +53,7 @@ argument_groups:
5353
upper: 20
5454
default: 10
5555
description: The number of time data is presented to SOM when clustering
56+
5657

5758
resources:
5859
# The script of your component (required)

src/methods/cycombine_all_controls/script.R renamed to src/methods/cycombine_all_controls_to_mid/script.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ adata_to_correct <- input_adata[, markers_to_correct]
2323
df_to_correct <- as.data.frame(
2424
adata_to_correct$layers[["preprocessed"]]
2525
)
26-
df_to_correct$batch <- adata_to_correct$obs$batch
27-
df_to_correct$sample <- adata_to_correct$obs$sample
26+
df_to_correct$batch <- as.factor(adata_to_correct$obs$batch)
27+
df_to_correct$sample <- as.factor(adata_to_correct$obs$sample)
2828

2929
# add an "anchor" column which specify which samples are the technical replicates
3030
# this is a bit weird as the anchor information should be a column name or character vector
@@ -42,6 +42,7 @@ df_to_correct$anchor <- ifelse(
4242
as.character(adata_to_correct$obs$sample),
4343
paste0("control_", adata_to_correct$obs$is_control)
4444
)
45+
df_to_correct$anchor <- as.factor(df_to_correct$anchor)
4546

4647
lineage_markers <- as.vector(input_adata$var_names[
4748
input_adata$var$marker_type == "lineage"

src/methods/cycombine_no_controls/config.vsh.yaml renamed to src/methods/cycombine_no_controls_to_goal/config.vsh.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
__merge__: ../../api/comp_method.yaml
22

3-
name: cycombine_no_controls
4-
label: cyCombine (no-controls)
5-
summary: "cyCombine without control samples"
3+
name: cycombine_no_controls_to_goal
4+
label: cyCombine (no-controls, to-goal)
5+
summary: "cyCombine run without control samples, correcting to a goal batch"
66
description: |
77
cyCombine perform batch integration using self-organizing maps and ComBat.
88
It first uses self-organizing maps (SOM) to group similar cells into clusters,
99
then applies a ComBat-based method to correct batch effects within
1010
each cluster.
11-
Here, we run cyCombine without any control samples (replicates in cyCombine terminology)
12-
using square SOM grid.
11+
Here, we run cyCombine without any control samples (replicates, in cyCombine terms),
12+
with a square SOM grid and correct the batches to batch 1.
1313
The size of the SOM grid is varied linearly between value of 6-16, with default set to 8
1414
following the default value in create_som function in cyCombine.
1515
Rlen, which is the number of times data is presented to the SOM network and can impact
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
requireNamespace("anndata", quietly = TRUE)
2+
requireNamespace("cyCombine", quietly = TRUE)
3+
4+
## VIASH START
5+
par <- list(
6+
input = "resources_test/task_cyto_batch_integration/mouse_spleen_flow_cytometry_subset/unintegrated_censored.h5ad",
7+
output = "resources_test/task_cyto_batch_integration/mouse_spleen_flow_cytometry_subset/output.h5ad",
8+
som_grid_size = 8,
9+
rlen = 10
10+
)
11+
meta <- list(name = "cycombine_no_controls")
12+
## VIASH END
13+
14+
cat("Reading input files\n")
15+
input_adata <- anndata::read_h5ad(par[["input"]])
16+
17+
cat("Preparing input Anndata and df\n")
18+
19+
adata_to_correct <- input_adata[, input_adata$var$to_correct]
20+
21+
markers_to_correct <- input_adata$var_names[input_adata$var$to_correct]
22+
23+
lineage_markers <- as.vector(input_adata$var_names[
24+
input_adata$var$marker_type == "lineage"
25+
])
26+
27+
df_to_correct <- as.data.frame(
28+
adata_to_correct$layers[["preprocessed"]]
29+
)
30+
df_to_correct$batch <- as.factor(adata_to_correct$obs$batch)
31+
df_to_correct$sample <- as.factor(adata_to_correct$obs$sample)
32+
33+
cat("Run cyCombine\n")
34+
35+
# use the default parameters in normalize
36+
# do the normalisation on all markers to be corrected
37+
df_to_correct_norm <- cyCombine::normalize(
38+
df = df_to_correct,
39+
markers = markers_to_correct,
40+
norm_method = "scale",
41+
ties.method = "average"
42+
)
43+
44+
# again, using default parameter values
45+
cluster_labels <- cyCombine::create_som(
46+
df = df_to_correct_norm,
47+
markers = lineage_markers,
48+
rlen = par[["rlen"]],
49+
seed = 42,
50+
xdim = par[["som_grid_size"]],
51+
ydim = par[["som_grid_size"]]
52+
)
53+
54+
# Batch correct using default parameter values
55+
df_corrected <- cyCombine::correct_data(
56+
df = df_to_correct,
57+
label = cluster_labels,
58+
markers = markers_to_correct,
59+
method = "ComBat",
60+
covar = NULL,
61+
anchor = NULL,
62+
ref.batch = "1",
63+
parametric = TRUE
64+
)
65+
66+
cat("Preparing output Anndata\n")
67+
df_not_corrected <- as.data.frame(
68+
input_adata[, !input_adata$var$to_correct]$layers[["preprocessed"]]
69+
)
70+
71+
df_output <- cbind(
72+
df_corrected[, markers_to_correct],
73+
df_not_corrected
74+
)
75+
76+
# reorder column to match input_adata
77+
df_output <- df_output[, input_adata$var_names]
78+
79+
output <- anndata::AnnData(
80+
obs = input_adata$obs[, integer(0)],
81+
var = input_adata$var[colnames(df_output), integer(0)],
82+
layers = list(integrated = as.matrix(df_output)),
83+
uns = list(
84+
dataset_id = input_adata$uns$dataset_id,
85+
method_id = meta$name,
86+
parameters = list(
87+
"normalize" = list(
88+
"markers" = markers_to_correct,
89+
"norm_method" = "scale",
90+
"ties.method" = "average"
91+
),
92+
"create_som" = list(
93+
"markers" = lineage_markers,
94+
"seed" = 42,
95+
"rlen" = par[["rlen"]],
96+
"xdim" = par[["som_grid_size"]],
97+
"ydim" = par[["som_grid_size"]]
98+
),
99+
"correct_data" = list(
100+
"markers" = markers_to_correct,
101+
"method" = "ComBat",
102+
"covar" = NULL,
103+
"anchor" = NULL,
104+
"ref.batch" = "1",
105+
"parametric" = TRUE
106+
)
107+
)
108+
)
109+
)
110+
111+
cat("Write output AnnData to file\n")
112+
113+
output$write_h5ad(par[["output"]], compression = "gzip")

0 commit comments

Comments
 (0)