Skip to contents

Introduction

curatedBreastData contained 34 high-quality GEO gene expression microarray datasets, all with advanced breast cancer. These datasets all contain at least one survival and/or treatment response variable, and minimal treamtent information (such as whether they had chemotherapy or not.) Clinical variables were semantically normalized across all datasets to provide a powerful database for investigating genes that are related to clinical variables such as pathological complete response, ER and HER2 IHC pathology tests, pam50 subtyping tests (when available), and tumor stage.

This database was original designed as a MySQL database, but has been re-represented as S4 ExpressionObjects with attached clinical data (phenoData) for easier data analyses in R.

Prerequisites

The package curatedBreastData requires minimal external packages; it requires impute.knn from the impute package and the ggplot2 package for optional plots during data matrix processing, and the CRAN package XML (which uses RCurl). It also uses Biobase, Bioconductor’s base package, for the ExpressionSet and its accessor functions.

The database

The schema of both the microarray datasets and the corresponding clinical data is detailed in proceedings from the 2013 AMIA Joint Summit on Translational Bioinformatics Planey et al. titled “Database integration of 4923 publicly-available samples of breast cancer molecular and clinical data.”. The data presented in this package contains 2719 samples; the “missing” samples are from a dataset that is only available via a restricted data request (the 2000-sample METABRIC study) and thus is not given here (however any researcher can request the data via EBI: http://www.ebi.ac.uk/)

Whenever possible, raw data was used. Authors of linked publications to each GEO dataset were directly contacted for additional clinical information, and we are grateful for the authors who responded and kindly shared additional clinical data that we added to this package.

The database schema is shown in the figure below.

schema
schema

Careful processing steps were taken before any normalization, such as removing duplicated patient samples within and between datasets. For example, GSE2226, the UCSF I-SPY study, actually had samples that overlapped with samples from GSE25055, an MD-Anderson study. While rare, the author took care to keep original I-SPY samples only in the GSE22226 dataset and remove them from the GSE25055 dataset.

Additionally, samples names were inspected to infer whether clear batches within a dataset existed. Often times, a multi-site GEO dataset would have samples labeled with the patient name and the country, such as “Spain” or “Portugal”. Such data tags imply the samples were collected at different sites. When such data was available, batch datasets were treated as separate datasts. GSE2226 also contained batches, but because two microarray platforms were used. These batch effects can indeed lead to differences in gene expression levels; especially for a meta-analysis, these should be treated as separate datasets.

A real-life example of this for a selected gene across several datasets in the package is shown below. The effect size for each dataset is measured. As one can see, the two different tissue collection sites for study 4 and the two differnt platforms used in study 7 manifest themselves in clear batches in both studies that result in differing microarray expression levels. Thus, they are treated as two, and not one, dataset for each of these studies.

Effect sizes of a selected gene showing how different sites (batches) from the same study can have different end effect size results (with survival as the meta-analysis outcome variable.)
Effect sizes of a selected gene showing how different sites (batches) from the same study can have different end effect size results (with survival as the meta-analysis outcome variable.)

The data objects in the curatedBreastDataExprSetList list provided in this package have already been normalized according to the protocol outlined below. Original scripts used for each original dataset can be found on the Github repository https://github.com/kplaney/curatedBreastCancer. This repository also contains results from a data quality check test in RMarkdown, and more notes on microarray processing.

microarray_processing
microarray_processing

Analysis

Gene expression datasets must be post-processed after normalization; curatedBreastData provides functions for removing samples and genes that have high NA rates, imputing missing values, collapsing duplicated genes or probes, removing duplicated patient samples, and filtering genes by variance. Documentation for each function is providing in its corresponding Rd documentation file. The wrapper function processExpressionSetList() completes all these post-processing steps on a list of S4 ExpressionSet objects like the curatedBreastDataExprSetList.rda list provided in this package.

This package works with expression data in the form of S4 ExpressionSet objects and/or lists of S4 ExpressionSet objects. In this example workflow, we load up all of the datasets.

## Loading required package: XML
## Loading required package: ggplot2
## Loading required package: impute
## Loading required package: Biobase
## Loading required package: BiocGenerics
## Loading required package: generics
## 
## Attaching package: 'generics'
## The following objects are masked from 'package:base':
## 
##     as.difftime, as.factor, as.ordered, intersect, is.element, setdiff,
##     setequal, union
## 
## Attaching package: 'BiocGenerics'
## The following objects are masked from 'package:stats':
## 
##     IQR, mad, sd, var, xtabs
## The following object is masked from 'package:utils':
## 
##     data
## The following objects are masked from 'package:base':
## 
##     anyDuplicated, aperm, append, as.data.frame, basename, cbind,
##     colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
##     get, grep, grepl, is.unsorted, lapply, Map, mapply, match, mget,
##     order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank,
##     rbind, Reduce, rownames, sapply, saveRDS, scale, sequence, table,
##     tapply, transform, unique, unsplit, which.max, which.min
## Welcome to Bioconductor
## 
##     Vignettes contain introductory material; view with
##     'browseVignettes()'. To cite Bioconductor, see
##     'citation("Biobase")', and for packages 'citation("pkgname")'.
## Warning in fun(libname, pkgname): Package 'curatedBreastData' is deprecated and will be removed from
##   Bioconductor version 3.24
#load up datasets that are in S4 expressionSet format.
#clinical data from master clinicalTable already linked to each sample
#in these ExpressionSets in the phenoData slot.
curatedBreastDataExprSetList <- getCuratedBreastDataExprSetList(test=TRUE);

We can see that clinical data for each dataset is included in the phenoData slot.

#check out the clinical data for dataset 1
#first look at the GEO study name
names(curatedBreastDataExprSetList)[1]
## [1] "study_1379_GPL1223_all"
#only take the first 3 patients for sake of printing to screen
#look at first 10 clinical variables
head(pData(curatedBreastDataExprSetList[[1]])[c(1:3), c(1:10)])
##                  datasetName dbUniquePatientID study_ID.x patient_ID GEO_GSMID
## 22449 study_1379_GPL1223_all               925       1379      22449     22449
## 22450 study_1379_GPL1223_all               926       1379      22450     22450
## 22451 study_1379_GPL1223_all               927       1379      22451     22451
##       platform_ID GEO_platform_ID AE_platform_ID coordinating_GSE_series_GSMID
## 22449        1223         GPL1223           <NA>                         22365
## 22450        1223         GPL1223           <NA>                         22366
## 22451        1223         GPL1223           <NA>                         22367
##       original_study_ID
## 22449                25
## 22450                59
## 22451                68

We then post-process this data. Let’s say we only want the top 5000 genes by variance for each dataset. This can take a few minutes as we’re post-processing 34 datasets. This takes a while, so it’s not included in the examples() section.

#process only the first two datasets to avoid a long-running example:
#take top 5000 genes by variance from each dataset.
proc_curatedBreastDataExprSetList <- processExpressionSetList(exprSetList=curatedBreastDataExprSetList[1:2], outputFileDirectory = tempdir(), numTopVarGenes=5000)
## 
## Analyzing dataset 1 or dataset named study_1379_GPL1223_all
## 
## Note: this function assumes your missing values
##   are proper NAs, not "null",etc.
## It's best to impute NA values before running this function
## otherwise it may set averages to NA if there is 1 NA present.
## This function just removes any genes whose key is NA.
## 
## You may get a notification here because key (usually gene) names are
##   duplicated so it can't use them as row names. 
##   That's OK, because we are immediately collapsing them into one feature.
## 
## Starting with  30patients.
## found no multiple samples from the same patient(s)
## 
## Analyzing dataset 2 or dataset named study_2034_GPL96_all
## 
## Note: this function assumes your missing values
##   are proper NAs, not "null",etc.
## It's best to impute NA values before running this function
## otherwise it may set averages to NA if there is 1 NA present.
## This function just removes any genes whose key is NA.
## 
## You may get a notification here because key (usually gene) names are
##   duplicated so it can't use them as row names. 
##   That's OK, because we are immediately collapsing them into one feature.
## 
## Starting with  30patients.
## found no multiple samples from the same patient(s)

Now we have processed expression matrices, each with the top 5000 genes by variance. We can then begin to explore what clinical variables we could use to run a meta-analysis or supervised analysis with all of this data.

All clinical variables are semantically normalized/have the same names across all datasets in the list. This is helpful, but oftentimes, we want to know if we have enough patients with certain clinical variables across all datasets. The global clinicalTable data frame provided in curatedBreastData combines all of the clinical data across all datasets.

Treatment data has been carefully curated and aggregtaed into semantically normalized variables so that a user can both determine which patients received a therapy, and then control for these variables in analyses. For example, using the master clinicalData table, we can quickly determine how many patients ever had chemotherapy. The user can also inspect specific classes of chemotherapy, such as taxanes. Whenever granular therapy information was provided, it was recorded in the clinicalTable (otherwise, it was left as NA.)

#load up master clinical data table
clinicalData <- getClinicalData(test=TRUE)
#look at some of the clinical variable name definitions
clinicalData$clinicalVarDef[c(1:2),]
##                        variableName
## dbUniquePatientID dbUniquePatientID
## study_ID                   study_ID
##                               definition.NA.means.not.recorded
## dbUniquePatientID Unique patient id created for this database.
## study_ID                                     GEO GSE study ID.
#Check out the treatment information.
#just do first three patients
head(clinicalData$clinicalTable)[c(1:3),
                                 c(112:ncol(clinicalData$clinicalTable))]
##     aromatase_inhibitor estrogen_receptor_blocker
## 117                   0                         0
## 118                   0                         0
## 119                   0                         0
##     estrogen_receptor_blocker_and_stops_production
## 117                                              0
## 118                                              0
## 119                                              0
##     estrogen_receptor_blocker_and_eliminator anti_HER2 tamoxifen doxorubicin
## 117                                        0         0         0           0
## 118                                        0         0         0           0
## 119                                        0         0         0           0
##     epirubicin docetaxel capecitabine fluorouracil paclitaxel cyclophosphamide
## 117          0         0            0            0          0                0
## 118          0         0            0            0          0                0
## 119          0         0            0            0          0                0
##     anastrozole fulvestrant gefitinib trastuzumab letrozole chemotherapy
## 117           0           0         0           0         0            0
## 118           0           0         0           0         0            0
## 119           0           0         0           0         0            0
##     hormone_therapy no_treatment methotrexate cetuximab carboplatin other
## 117               0            0            0         0           0     0
## 118               0            0            0         0           0     0
## 119               0            0            0         0           0     0
##     taxaneGeneral neoadjuvant_or_adjuvant study_specific_protocol_number
## 117             0                     adj                              1
## 118             0                     adj                              1
## 119             0                     adj                              1
#how many had chemotherapy?
numChemoPatients <- length(which(
  clinicalData$clinicalTable$chemotherapyClass==1))
#around 1500 had chemotherapy
numChemoPatients
## [1] 0
#which patients specifically had a taxane chemotherapy?
numChemoTaxane <- length(which(clinicalData$clinicalTable$taxane==1))
numChemoTaxane
## [1] 0

We can also look at adjuvant vs. neoadjuvant therapy.

#how many had adjuvant therapy?
numAdjPatients <- length(which(
  clinicalData$clinicalTable$neoadjuvant_or_adjuvant=="adj"))
#over a 1000 had (documented) adjuvant therapy
numAdjPatients 
## [1] 60

The curatedBreastData_pData Dataset

As a convenience, the package also provides curatedBreastData_pData, which is a single, pre-concatenated data.frame containing the pData (phenotypic and clinical metadata) from all of the datasets in curatedBreastDataExprSetList. Rownames correspond to the sample IDs.

You can load this dataset directly using the standard data() function:

# Load the pre-concatenated pData dataset
data(curatedBreastData_pData)

# View its dimensions and structure
dim(curatedBreastData_pData)
## [1] 2719  153
head(curatedBreastData_pData)[1:5, 1:5]
##                                         datasetName dbUniquePatientID
## study_1379_GPL1223_all.22449 study_1379_GPL1223_all               925
## study_1379_GPL1223_all.22450 study_1379_GPL1223_all               926
## study_1379_GPL1223_all.22451 study_1379_GPL1223_all               927
## study_1379_GPL1223_all.22452 study_1379_GPL1223_all               928
## study_1379_GPL1223_all.22453 study_1379_GPL1223_all               929
##                              study_ID.x patient_ID GEO_GSMID
## study_1379_GPL1223_all.22449       1379      22449     22449
## study_1379_GPL1223_all.22450       1379      22450     22450
## study_1379_GPL1223_all.22451       1379      22451     22451
## study_1379_GPL1223_all.22452       1379      22452     22452
## study_1379_GPL1223_all.22453       1379      22453     22453

The clinicalTable data frame can allow us to then select the relevant patients from each dataset who have the outcomes variable we want. For example, we inspect the number of samples with data pertaining to Overall Survival below.

#how many patients have non-NA OS binary data?
length(which(!is.na(clinicalData$clinicalTable$OS)))
## [1] 0
#how many have OS data in the more granular form of months until OS? 
#this variable includes studies that had a cieling for tracking OS
length(which(!is.na(clinicalData$clinicalTable$OS_months_or_MIN_months_of_OS)))
## [1] 0
#how many patients have OS information that is definitively 
#followed up until their death (details on how studies collect OS data can be surprising!)
length(which(!is.na(clinicalData$clinicalTable$OS_up_until_death)))
## [1] 0
#finish up with sessionInfo
sessionInfo()
## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.4 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] curatedBreastData_2.40.2 Biobase_2.73.1           BiocGenerics_0.59.10    
## [4] generics_0.1.4           impute_1.87.0            ggplot2_4.0.3           
## [7] XML_3.99-0.23            BiocStyle_2.41.0        
## 
## loaded via a namespace (and not attached):
##  [1] sass_0.4.10         RSQLite_3.53.3      digest_0.6.39      
##  [4] magrittr_2.0.5      evaluate_1.0.5      grid_4.6.1         
##  [7] RColorBrewer_1.1-3  fastmap_1.2.0       blob_1.3.0         
## [10] jsonlite_2.0.0      DBI_1.3.0           BiocManager_1.30.27
## [13] scales_1.4.0        httr2_1.3.0         textshaping_1.0.5  
## [16] jquerylib_0.1.4     cli_3.6.6           rlang_1.3.0        
## [19] dbplyr_2.6.0        bit64_4.8.2         withr_3.0.3        
## [22] cachem_1.1.0        yaml_2.3.12         otel_0.2.0         
## [25] tools_4.6.1         memoise_2.0.1       dplyr_1.2.1        
## [28] filelock_1.0.3      curl_7.1.0          vctrs_0.7.3        
## [31] R6_2.6.1            lifecycle_1.0.5     BiocFileCache_3.3.0
## [34] fs_2.1.0            htmlwidgets_1.6.4   bit_4.6.0          
## [37] ragg_1.5.2          pkgconfig_2.0.3     desc_1.4.3         
## [40] pkgdown_2.2.1       pillar_1.11.1       bslib_0.11.0       
## [43] gtable_0.3.6        glue_1.8.1          systemfonts_1.3.2  
## [46] xfun_0.60           tibble_3.3.1        tidyselect_1.2.1   
## [49] knitr_1.51          farver_2.1.2        htmltools_0.5.9    
## [52] rmarkdown_2.31      compiler_4.6.1      S7_0.2.2

References

  1. Planey, Butte. Database integration of 4923 publicly-available samples of breast cancer molecular and clinical data. AMIA Joint Summits Translational Science Proceedings. (2003) PMC3814460
  2. Github repo with code, further documentation on datasets and baseline normalization schemes, and database quality checks: https://github.com/kplaney/curatedBreastCancer