Skip to content

Commit 010d43e

Browse files
committed
Refactor custom probe annotation process
- Update Agilent catalog design handling to pull only ProbeID to Ensembl Transcript ID mapping. Map to Ensembl Gene IDs using BioMart and then proceed to standard GeneLab Annotation process - Update outdated links - Clean up formatting and typos in all documentation
1 parent 2b9c2fa commit 010d43e

6 files changed

Lines changed: 202 additions & 158 deletions

File tree

‎Microarray/Agilent_1-channel/Pipeline_GL-DPPD-7112_Versions/GL-DPPD-7112-A.md‎

Lines changed: 83 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,9 +1117,7 @@ if (organism %in% c("athaliana")) {
11171117
}
11181118
}
11191119

1120-
if (use_custom_annot) {
1121-
unloadNamespace("biomaRt")
1122-
} else {
1120+
if (!use_custom_annot) {
11231121
print(ensembl)
11241122

11251123
probe_ids <- unique(norm_data$genes$ProbeName)
@@ -1148,66 +1146,85 @@ if (organism %in% c("athaliana")) {
11481146

11491147
Sys.sleep(10) # Slight break between requests to prevent back-to-back requests
11501148
}
1151-
}
1152-
}
1153-
1154-
# At this point, we have df_mapping from either the BioMart live service or the ensembl genomes ftp archive depending on the organism
1155-
# If no df_mapping obtained (e.g., not supported in BioMart), use custom annotations; otherwise, merge in-house annotations to df_mapping
1156-
1157-
if (use_custom_annot) {
1158-
expected_attribute_name <- 'ProbeName'
1159-
1160-
annot_type <- 'NO_CUSTOM_ANNOT'
1161-
if (!is.null(local_annotation_dir) && !is.null(annotation_config_path)) {
1162-
config_df <- read.csv(annotation_config_path, row.names=1)
1163-
if (unique(df_rs$`biomart_attribute`) %in% row.names(config_df)) {
1164-
annot_config <- config_df[unique(df_rs$`biomart_attribute`), ]
1165-
annot_type <- annot_config$annot_type[[1]]
1149+
} else {
1150+
print(glue::glue("Using custom probe annotation for organism: {unique(df_rs$organism)}"))
1151+
expected_attribute_name <- 'ProbeName'
1152+
1153+
annot_type <- 'NO_CUSTOM_ANNOT'
1154+
if (!is.null(local_annotation_dir) && !is.null(annotation_config_path)) {
1155+
config_df <- read.csv(annotation_config_path, row.names=1)
1156+
if (unique(df_rs$`biomart_attribute`) %in% row.names(config_df)) {
1157+
annot_config <- config_df[unique(df_rs$`biomart_attribute`), ]
1158+
annot_type <- annot_config$annot_type[[1]]
1159+
} else {
1160+
warning(paste0("No entry for '", unique(df_rs$`biomart_attribute`), "' in provided config file: ", annotation_config_path))
1161+
}
11661162
} else {
1167-
warning(paste0("No entry for '", unique(df_rs$`biomart_attribute`), "' in provided config file: ", annotation_config_path))
1163+
warning("Need to provide both local_annotation_dir and annotation_config_path to use custom annotation.")
11681164
}
1169-
} else {
1170-
warning("Need to provide both local_annotation_dir and annotation_config_path to use custom annotation.")
1171-
}
1172-
1173-
if (annot_type == 'agilent') {
1174-
unique_probe_ids <- read.delim(
1175-
file.path(local_annotation_dir, annot_config$annot_filename[[1]]),
1176-
header = TRUE, na.strings = c('NA', '')
1177-
)[c('ProbeID', 'EnsemblID', 'GeneSymbol', 'GeneName', 'RefSeqAccession', 'EntrezGeneID', 'GO')]
11781165

1179-
stopifnot(nrow(unique_probe_ids) == length(unique(unique_probe_ids$ProbeID)))
1166+
if (annot_type == 'agilent') {
1167+
print(glue::glue("Using Agilent Ensembl transcript annotation to retrieve Ensembl Gene IDs from BioMart"))
1168+
print(ensembl)
1169+
1170+
# read in AllAnnotations file and strip version off of Ensembl Transcript ID
1171+
agilent_table <- read.delim(
1172+
file.path(local_annotation_dir, annot_config$annot_filename[[1]]),
1173+
header = TRUE, na.strings = c('NA', '')
1174+
)[c('ProbeID', 'EnsemblID')] %>%
1175+
dplyr::mutate(EnsemblID = stringr::str_split_i(EnsemblID, "\\.", 1))
1176+
1177+
stopifnot(nrow(agilent_table) == length(unique(agilent_table$ProbeID)))
1178+
1179+
# Map Ensembl Transcript IDs to Ensembl Gene IDs using BioMart
1180+
# Run BioMart Queries in chunks to prevent request timeouts
1181+
# Note: If timeout is occurring (possibly due to larger load on BioMart), reduce chunk size
1182+
CHUNK_SIZE= 1500
1183+
transcript_id_chunks <- split(agilent_table$EnsemblID, ceiling(seq_along(agilent_table$EnsemblID) / CHUNK_SIZE))
1184+
id_map <- data.frame()
1185+
for (i in seq_along(transcript_id_chunks)) {
1186+
transcript_id_chunk <- transcript_id_chunks[[i]]
1187+
print(glue::glue("Running BioMart query chunk {i} of {length(transcript_id_chunks)}. Total transcript IDS in query ({length(transcript_id_chunk)})"))
1188+
chunk_results <- biomaRt::getBM(
1189+
attributes = c(
1190+
"ensembl_transcript_id",
1191+
"ensembl_gene_id"
1192+
),
1193+
filters = "ensembl_transcript_id",
1194+
values = transcript_id_chunk,
1195+
mart = ensembl)
1196+
1197+
if (nrow(chunk_results) > 0) {
1198+
id_map <- id_map %>% dplyr::bind_rows(chunk_results)
1199+
}
11801200

1181-
# Clean columns
1182-
unique_probe_ids$GO <- purrr::map_chr(stringr::str_extract_all(unique_probe_ids$GO, 'GO:\\d{7}'), ~paste0(unique(.), collapse = '|')) %>% stringr::str_replace('^$', NA_character_)
1201+
Sys.sleep(10) # Slight break between requests to prevent back-to-back requests
1202+
}
11831203

1184-
names(unique_probe_ids) <- c('ProbeName', 'ENSEMBL', 'SYMBOL', 'GENENAME', 'REFSEQ', 'ENTREZID', 'GOSLIM_IDS')
1204+
df_mapping <-
1205+
dplyr::left_join(agilent_table, id_map, dplyr::join_by(EnsemblID == ensembl_transcript_id)) %>%
1206+
dplyr::select(ProbeID, ensembl_gene_id)
11851207

1186-
unique_probe_ids$STRING_id <- NA_character_
1208+
names(df_mapping) <- c('ProbeName', 'ensembl_gene_id')
11871209

1188-
gene_col <- 'ENSEMBL'
1189-
if (sum(!is.na(unique_probe_ids$ENTREZID)) > sum(!is.na(unique_probe_ids$ENSEMBL))) {
1190-
gene_col <- 'ENTREZID'
1191-
}
1192-
if (sum(!is.na(unique_probe_ids$SYMBOL)) > max(sum(!is.na(unique_probe_ids$ENTREZID)), sum(!is.na(unique_probe_ids$ENSEMBL)))) {
1193-
gene_col <- 'SYMBOL'
1210+
} else if (annot_type == 'custom') {
1211+
unique_probe_ids <- read.csv(
1212+
file.path(local_annotation_dir, annot_config$annot_filename[[1]]),
1213+
header = TRUE, na.strings = c('NA', '')
1214+
)
1215+
} else {
1216+
annot_cols <- c('ProbeName', 'ENTREZID', 'SYMBOL', 'GENENAME', 'ENSEMBL', 'REFSEQ', 'GOSLIM_IDS', 'STRING_id', 'count_gene_mappings', 'gene_mapping_source')
1217+
unique_probe_ids <- setNames(data.frame(matrix(NA_character_, nrow = 1, ncol = length(annot_cols))), annot_cols)
11941218
}
1195-
1196-
unique_probe_ids <- unique_probe_ids %>%
1197-
dplyr::mutate(
1198-
count_gene_mappings = 1 + stringr::str_count(get(gene_col), stringr::fixed("|")),
1199-
gene_mapping_source = gene_col
1200-
)
1201-
} else if (annot_type == 'custom') {
1202-
unique_probe_ids <- read.csv(
1203-
file.path(local_annotation_dir, annot_config$annot_filename[[1]]),
1204-
header = TRUE, na.strings = c('NA', '')
1205-
)
1206-
} else {
1207-
annot_cols <- c('ProbeName', 'ENTREZID', 'SYMBOL', 'GENENAME', 'ENSEMBL', 'REFSEQ', 'GOSLIM_IDS', 'STRING_id', 'count_gene_mappings', 'gene_mapping_source')
1208-
unique_probe_ids <- setNames(data.frame(matrix(NA_character_, nrow = 1, ncol = length(annot_cols))), annot_cols)
12091219
}
1210-
} else {
1220+
}
1221+
1222+
# At this point, we have df_mapping from either the BioMart live service directly or via Agilent probe
1223+
# annotation or the ensembl genomes ftp archive depending on the organism and presence of BioMart probe
1224+
# annotation. If no df_mapping obtained (e.g., not supported in BioMart or lacking Ensembl transcript
1225+
# IDs), use custom annotations; otherwise, merge in-house annotations to df_mapping
1226+
1227+
if (!use_custom_annot || annot_type == 'agilent') {
12111228
annot <- read.table(
12121229
as.character(annotation_file_path),
12131230
sep = "\t",
@@ -1217,19 +1234,20 @@ if (use_custom_annot) {
12171234
)
12181235

12191236
unique_probe_ids <- df_mapping %>%
1220-
dplyr::mutate(dplyr::across(!!sym(expected_attribute_name), as.character)) %>% # Ensure probe ids treated as character type
1221-
dplyr::group_by(!!sym(expected_attribute_name)) %>%
1222-
dplyr::summarise(
1223-
ENSEMBL = list_to_unique_piped_string(ensembl_gene_id)
1224-
) %>%
1225-
# Count number of ensembl IDS mapped
1226-
dplyr::mutate(
1227-
count_gene_mappings = 1 + stringr::str_count(ENSEMBL, stringr::fixed("|")),
1228-
gene_mapping_source = annot_key
1229-
) %>%
1230-
dplyr::left_join(annot, by = c("ENSEMBL" = annot_key))
1237+
dplyr::mutate(dplyr::across(!!sym(expected_attribute_name), as.character)) %>% # Ensure probe ids treated as character type
1238+
dplyr::group_by(!!sym(expected_attribute_name)) %>%
1239+
dplyr::summarise(
1240+
ENSEMBL = list_to_unique_piped_string(ensembl_gene_id)
1241+
) %>%
1242+
# Count number of ensembl IDS mapped
1243+
dplyr::mutate(
1244+
count_gene_mappings = 1 + stringr::str_count(ENSEMBL, stringr::fixed("|")),
1245+
gene_mapping_source = annot_key
1246+
) %>%
1247+
dplyr::left_join(annot, by = c("ENSEMBL" = annot_key))
12311248
}
12321249

1250+
# reformat merged probe annotations
12331251
norm_data$genes <- norm_data$genes %>%
12341252
dplyr::left_join(unique_probe_ids, by = c("ProbeName" = expected_attribute_name ) ) %>%
12351253
dplyr::mutate( count_gene_mappings := ifelse(is.na(count_gene_mappings), 0, count_gene_mappings) ) %>%

‎Microarray/Agilent_1-channel/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@
2626
Jonathan Oribello
2727

2828
**Maintained by:**
29-
Crystal Han (crystal.han@nasa.gov)
29+
Jihan Yehia (jihan.yehia@nasa.gov)
3030
Alexis Torres (alexis.torres@nasa.gov)
31+
Crystal Han (crystal.han@nasa.gov)

‎Microarray/Agilent_1-channel/Workflow_Documentation/NF_MAAgilent1ch/README.md‎

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
### Implementation Tools <!-- omit in toc -->
66

7-
The current GeneLab Agilent 1 Channel Microarray consensus processing pipeline (NF_MAAgilent1ch), [GL-DPPD-7112-A](../../Pipeline_GL-DPPD-7112_Versions/GL-DPPD-7112-A.md), is implemented as a [Nextflow](https://nextflow.io/) DSL2 workflow and utilizes [Singularity](https://docs.sylabs.io/guides/3.10/user-guide/introduction.html) to run all tools in containers. This workflow (NF_MAAgilent1ch) is run using the command line interface (CLI) of any unix-based system. While knowledge of creating workflows in Nextflow is not required to run the workflow as is, [the Nextflow documentation](https://nextflow.io/docs/latest/index.html) is a useful resource for users who want to modify and/or extend this workflow.
7+
The current GeneLab Agilent 1 Channel Microarray consensus processing pipeline (NF_MAAgilent1ch), [GL-DPPD-7112-A](../../Pipeline_GL-DPPD-7112_Versions/GL-DPPD-7112-A.md), is implemented as a [Nextflow](https://nextflow.io/) DSL2 workflow and utilizes [Singularity](https://docs.sylabs.io/guides/3.10/user-guide/introduction.html) or [Docker](https://docs.docker.com/get-started/) containers to run all tools. This workflow (NF_MAAgilent1ch) is run using the command line interface (CLI) of any unix-based system. While knowledge of creating workflows in Nextflow is not required to run the workflow as is, [the Nextflow documentation](https://docs.seqera.io/nextflow/) is a useful resource for users who want to modify and/or extend this workflow.
88

99
### Workflow & Subworkflows <!-- omit in toc -->
1010

@@ -33,13 +33,13 @@ Below is a description of each subworkflow and the additional output files gener
3333
- This subworkflow performs validation and verification (V&V) on the raw and processed data files. It performs a series of checks on the output files generated and flags the results, using the flag codes indicated in the table below, which are outputted into a log file.
3434
**V&V Flags**:
3535

36-
|Flag Codes|Flag Name|Interpretation|
37-
|:---------|:--------|:-------------|
38-
| 2 | MANUAL | Special flag that indicates a manual check that is advised. Often used to advise what assess in QA plots. |
39-
| 20 | GREEN | Indicates the check passed all validation conditions |
40-
| 30 | YELLOW | Indicates the check was flagged for minor issues (e.g. slight outliers) |
41-
| 50 | RED | Indicates the check was flagged for moderate issues (e.g. major outliers) |
42-
| 80 | HALT | Indicates the check was flagged for severe issues that trigger a processing halt (e.g. missing data) |
36+
| Flag Codes | Flag Name | Interpretation |
37+
| :--------- | :-------- | :-------------------------------------------------------------------------------------------------------- |
38+
| 2 | MANUAL | Special flag that indicates a manual check that is advised. Often used to advise what assess in QA plots. |
39+
| 20 | GREEN | Indicates the check passed all validation conditions |
40+
| 30 | YELLOW | Indicates the check was flagged for minor issues (e.g. slight outliers) |
41+
| 50 | RED | Indicates the check was flagged for moderate issues (e.g. major outliers) |
42+
| 80 | HALT | Indicates the check was flagged for severe issues that trigger a processing halt (e.g. missing data) |
4343

4444
<br>
4545

@@ -64,9 +64,11 @@ Below is a description of each subworkflow and the additional output files gener
6464

6565
#### 1a. Install Nextflow
6666

67-
Nextflow can be installed either through [Anaconda](https://anaconda.org/bioconda/nextflow) or as documented on the [Nextflow documentation page](https://www.nextflow.io/docs/latest/getstarted.html).
67+
Nextflow can be installed either through the [Anaconda bioconda channel](https://anaconda.org/bioconda/nextflow) or as documented in the [Nextflow installation documentation](https://docs.seqera.io/nextflow/install).
6868

69-
> Note: If you want to install Anaconda, we recommend installing a Miniconda, Python3 version appropriate for your system, as instructed by [Happy Belly Bioinformatics](https://astrobiomike.github.io/unix/conda-intro#getting-and-installing-conda).
69+
70+
> [!TIP]
71+
> If you wish to install Conda, we recommend installing a Miniforge version appropriate for your system, as documented on the [conda-forge website](https://conda-forge.org/download/), where you can find basic binaries for most systems. More detailed miniforge documentation is available in the [miniforge github repository](https://github.com/conda-forge/miniforge).
7072
>
7173
> Once conda is installed on your system, you can install the latest version of Nextflow by running the following commands:
7274
>
@@ -83,7 +85,9 @@ Singularity is a container platform that allows usage of containerized software.
8385
8486
We recommend installing Singularity on a system wide level as per the associated [documentation](https://docs.sylabs.io/guides/3.10/admin-guide/admin_quickstart.html).
8587
86-
> Note: Singularity is also available through [Anaconda](https://anaconda.org/conda-forge/singularity).
88+
> [!TIP]
89+
> - Singularity is also available through the [Anaconda conda-forge channel](https://anaconda.org/conda-forge/singularity).
90+
> - Alternatively, Docker can be used in place of Singularity. To get started with Docker, see the [Docker CE installation documentation](https://docs.docker.com/engine/install/).
8791
8892
<br>
8993
@@ -106,7 +110,8 @@ unzip NF_MAAgilent1ch_1.0.5.zip
106110
### 3. Run the Workflow
107111
108112
While in the location containing the `NF_MAAgilent1ch_1.0.5` directory that was downloaded in [step 2](#2-download-the-workflow-files), you are now able to run the workflow. Below are three examples of how to run the NF_MAAgilent1ch workflow:
109-
> Note: Nextflow commands use both single hyphen arguments (e.g. -help) that denote general nextflow arguments and double hyphen arguments (e.g. --ensemblVersion) that denote workflow specific parameters. Take care to use the proper number of hyphens for each argument.
113+
> [!NOTE]
114+
> Nextflow commands use both single hyphen arguments (e.g. -help) that denote general nextflow arguments and double hyphen arguments (e.g. --ensemblVersion) that denote workflow specific parameters. Take care to use the proper number of hyphens for each argument.
110115
111116
<br>
112117
@@ -196,7 +201,7 @@ All parameters listed above and additional optional arguments for the NF_MAAgile
196201
nextflow run NF_MAAgilent1ch_1.0.5/main.nf --help
197202
```
198203
199-
See `nextflow run -h` and [Nextflow's CLI run command documentation](https://nextflow.io/docs/latest/cli.html#run) for more options and details common to all nextflow workflows.
204+
See `nextflow run -h` and [Nextflow's CLI run command documentation](https://docs.seqera.io/nextflow/cli) for more options and details common to all nextflow workflows.
200205
201206
<br>
202207
@@ -211,7 +216,7 @@ All R code steps and output are rendered within a Quarto document yielding the f
211216
212217
213218
The outputs from the Analysis Staging and V&V Pipeline Subworkflows are described below:
214-
> Note: The outputs from the Agilent 1 Channel Microarray Processing Subworkflow are documented in the [GL-DPPD-7112-A.md](../../../Pipeline_GL-DPPD-7112_Versions/GL-DPPD-7112-A.md) processing protocol.
219+
> Note: The outputs from the Agilent 1 Channel Microarray Processing Subworkflow are documented in the [GL-DPPD-7112-A.md](../../Pipeline_GL-DPPD-7112_Versions/GL-DPPD-7112-A.md) processing protocol.
215220
216221
**Analysis Staging Subworkflow**
217222
> Note: only applicable for [Approach 1](#3a-approach-1-run-the-workflow-on-a-genelab-agilent-1-channel-microarray-dataset) and [Approach 3](#3c-approach-3-run-the-workflow-using-an-isa-archive)
@@ -229,7 +234,7 @@ The outputs from the Analysis Staging and V&V Pipeline Subworkflows are describe
229234
<br>
230235
231236
Standard Nextflow resource usage logs are also produced as follows:
232-
> Further details about these logs can also found within [this Nextflow documentation page](https://www.nextflow.io/docs/latest/tracing.html#execution-report).
237+
> Further details about these logs can also found in the [Nextflow Report Documentation](https://docs.seqera.io/nextflow/reports).
233238
234239
**Nextflow Resource Usage Logs**
235240
- Output:

‎Microarray/Agilent_1-channel/Workflow_Documentation/NF_MAAgilent1ch/examples/annotations/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
| Column Name | Type | Description | Example |
1717
|:------------|:-----|:------------|:--------|
1818
| array_design | string | A bioMart attribute identifier denoting the microarray probe/probeset attribute used for annotation mapping. | AGILENT SurePrint G3 GE 8x60k v3 |
19-
| annot_type | string | Used to determine how the custom annotations are parsed before merging to the data. Currently, only the below are supported: <ul><li>`agilent`: Annotations file is expected to be in the AA (All Annotations) format by [Agilent](https://earray.chem.agilent.com/earray/)</li><li>`custom`: Annotations file is merged as is, expected to have the following columns: `ProbesetID`, `ENTREZID`, `SYMBOL`, `GENENAME`, `ENSEMBL`, `REFSEQ`, `GOSLIM_IDS`, `STRING_id`, `count_gene_mappings`, `gene_mapping_source`</li></ul> | agilent |
20-
| annot_filename | string | Name of the custom annotations file. This is the AllAnnotations file downloaded from Agilent's eArray web portal. | 072363_D_AA_20240521.txt |
19+
| annot_type | string | Used to determine how the custom annotations are parsed before merging to the data. Currently, only the below are supported: <ul><li>`agilent`: Annotations file is expected to be in the AA (All Annotations) format created by [Agilent](https://earray.chem.agilent.com/earray/) for catalog arrays. The Ensembl Transcript Stable IDs in the file are used to map to Ensembl Gene Stable IDs after which the standard GeneLab annotation process is used.</li><li>`custom`: Annotations file is merged as-is and expected to have the following columns: `ProbeName`, `ENTREZID`, `SYMBOL`, `GENENAME`, `ENSEMBL`, `REFSEQ`, `GOSLIM_IDS`, `STRING_id`, `count_gene_mappings`, `gene_mapping_source`, where `ENSEMBL` is the Ensembl Gene Stable ID, not the Transcript ID</li></ul> | agilent |
20+
| annot_filename | string | Name of the custom annotations file. | 072363_D_AA_20240521.txt |
2121

2222
## Optional columns
2323
If the file was downloaded from a website, provide the download link used and date

0 commit comments

Comments
 (0)