Matched tumor/normal pairs--a use case for the GenomicDataCommons Bioconductor package

Introduction

The NCI Genomic Data Commons (GDC) is a reboot of the approach that NCI uses to manage and expose genomic and associated clinical and experimental metadata. I have been working on a Bioconductor package that interfaces with the GDC API to provide search and data retrieval from within R.

testing

In the first of what will likely be a set of use cases for the GenomicDataCommons, I am going to address a question that came up on twitter from @sleight82

Asking for a non-tweeter: can you find matched control samples?

The answer is, “Yes.” I am going to assume that what the “non-tweeter” friend meant was that he or she was interested in finding matched tumor/normal data related to, for example, gene expression, and that the interest is in a specific disease category or TCGA. So, I am going to answer the more specific question:

Can you find matched primary tumor/solid tissue normal samples and associated RNA-Seq gene expression files from TCGA breast cancer cases?

library(GenomicDataCommons)
## Loading required package: magrittr
## 
## Attaching package: 'GenomicDataCommons'
## The following object is masked from 'package:stats':
## 
##     filter
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:GenomicDataCommons':
## 
##     count, filter, select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

The GDC API puts some constraints on what can be done directly. But, since we are working in R, we have a toolbox that allows us to build a solution out of component parts. The strategy that I am going to employ is a three-step approach

  1. Find RNA-Seq gene expression files derived from “Solid Tissue Normal”
  2. Find RNA-Seq gene expression files derived from “Primary Tumor”
  3. Limit cases from #1 and #2 that have gene expression results from both normal and tumor.

In this code block, find all “HTSeq - Counts” files that are derived from “Solid Tissue Normal” in the project “TCGA-BRCA”. I used a combination of files() %>% select(available_fields('files') %>% results() to get an overview of the data available in the files() endpoint, followed by grep_fields() and available_values() to determine how to build filters.

nl_ge_files = files() %>%
    GenomicDataCommons::filter(~   cases.samples.sample_type=='Solid Tissue Normal' &
               cases.project.project_id == 'TCGA-BRCA' &
               analysis.workflow_type == "HTSeq - Counts") %>%
    expand(c('cases','cases.samples')) %>%
    results_all() %>%
    as_tibble()

And do the same, but now looking for gene expression from tumors.

tm_ge_files = files() %>%
    GenomicDataCommons::filter(~   cases.samples.sample_type=='Primary Tumor' &
               cases.project.project_id == 'TCGA-BRCA' &
               analysis.workflow_type == "HTSeq - Counts") %>%
    expand(c('cases','cases.samples')) %>%
    results_all() %>%
    as_tibble()

Now, we have two data frames describing the normal- and tumor-derived TCGA-BRCA gene expression files. Note that I asked the GDC to provide cases.case_id as part of the record using select(). By looking for the intersection of cases between these two sets of files, we can find cases that contain files derived from both tumor and normal tissue.

nl_cases = bind_rows(nl_ge_files$cases, .id='file_id')
tm_cases = bind_rows(tm_ge_files$cases, .id='file_id')
matchedcases = intersect(nl_cases$case_id,
                         tm_cases$case_id)
# how many matched cases?
length(matchedcases)
## [1] 112

We can now create a data.frame that contains file information for only the matched samples. Note that the names of the matched cases are the file ids.

matched_nl_files = nl_cases[nl_cases$case_id %in% matchedcases, 'file_id']
matched_tm_files = tm_cases[tm_cases$case_id %in% matchedcases, 'file_id']

matched_tn_ge_file_info = rbind(subset(nl_ge_files,file_id %in% matched_nl_files),
                                subset(tm_ge_files,file_id %in% matched_tm_files))
head(matched_tn_ge_file_info)
## # A tibble: 6 x 17
##   data_type updated_datetime file_name submitter_id file_id file_size cases
##   <chr>     <chr>            <chr>     <chr>        <chr>       <int> <lis>
## 1 Gene Exp… 2019-08-27T15:2… f4a128be… f4a128be-69… aaad18…    259279 <df[…
## 2 Gene Exp… 2019-08-27T15:2… 05eb3daf… 05eb3daf-d3… 7d970a…    259031 <df[…
## 3 Gene Exp… 2019-08-27T15:2… 2eeb46a1… 2eeb46a1-04… 016855…    258320 <df[…
## 4 Gene Exp… 2019-08-27T15:2… 96ddce60… 96ddce60-2b… d45a9e…    260544 <df[…
## 5 Gene Exp… 2019-08-27T15:2… 9151bbcc… 9151bbcc-ec… 2a1ad5…    257275 <df[…
## 6 Gene Exp… 2019-08-27T15:2… 446db9e9… 446db9e9-d5… 2b910e…    254470 <df[…
## # … with 10 more variables: id <chr>, created_datetime <chr>,
## #   md5sum <chr>, data_format <chr>, acl <list>, access <chr>,
## #   state <chr>, data_category <chr>, type <chr>,
## #   experimental_strategy <chr>

Since the gene expression data are not that big, we can use the GDC API to download the data files directly. The GenomicDataCommons uses a cache for files, so the first time the code below is run, data will be downloaded. After that, if the cache is in place, the cached files will be used.

fnames = lapply(as.character(matched_tn_ge_file_info$file_id),
                  function(file_id) {
                      gdcdata(file_id, progress = FALSE)
                  })

Now, fnames should be a list of file names that can be read into and processed with R.

sessionInfo()
## R version 3.6.1 (2019-07-05)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS Mojave 10.14.2
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] dplyr_0.8.1              GenomicDataCommons_1.9.0
## [3] magrittr_1.5            
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.1                  pillar_1.4.2               
##  [3] compiler_3.6.1              GenomeInfoDb_1.21.1        
##  [5] XVector_0.25.0              bitops_1.0-6               
##  [7] tools_3.6.1                 zlibbioc_1.31.0            
##  [9] zeallot_0.1.0               digest_0.6.19              
## [11] jsonlite_1.6                evaluate_0.14              
## [13] tibble_2.1.3                lattice_0.20-38            
## [15] pkgconfig_2.0.2             rlang_0.4.0                
## [17] Matrix_1.2-17               cli_1.1.0                  
## [19] DelayedArray_0.11.2         curl_3.3                   
## [21] yaml_2.2.0                  parallel_3.6.1             
## [23] blogdown_0.13.1             xfun_0.8                   
## [25] GenomeInfoDbData_1.2.1      xml2_1.2.0                 
## [27] httr_1.4.0                  stringr_1.4.0              
## [29] knitr_1.23                  vctrs_0.1.0                
## [31] hms_0.4.2                   rappdirs_0.3.1             
## [33] S4Vectors_0.23.17           IRanges_2.19.10            
## [35] tidyselect_0.2.5            stats4_3.6.1               
## [37] grid_3.6.1                  glue_1.3.1                 
## [39] Biobase_2.45.0              R6_2.4.0                   
## [41] fansi_0.4.0                 BiocParallel_1.19.0        
## [43] rmarkdown_1.13              bookdown_0.11              
## [45] readr_1.3.1                 purrr_0.3.2                
## [47] backports_1.1.4             htmltools_0.3.6            
## [49] matrixStats_0.54.0          BiocGenerics_0.31.4        
## [51] GenomicRanges_1.37.14       assertthat_0.2.1           
## [53] SummarizedExperiment_1.15.5 utf8_1.1.4                 
## [55] stringi_1.4.3               RCurl_1.95-4.12            
## [57] crayon_1.3.4
Avatar
Sean Davis
Senior Associate Scientist

My interests include biomedical data science, open data, genomics, and cancer research.

comments powered by Disqus