2 Kickstarting an R package
- Kickstart an R package
- Write functions
- Document functions
At any time, if you are lost or do not understand how functions in the proposed solution work, type ?<function> in the R console and a help menu will appear.
You can also check the help tab in the corresponding quadrant.
2.1 Proposed package functionalities
In this section, we are going to create a package which aims at:
- Importing
.bigwigcoverage track files as well as.bedfiles of genomic coordinates - Filtering imported
GRangesto make sure they all are contained within the coverage tracks - Calculating the average (+/- quantiles) of coverage of the
.bigwigfile over theGRangesof interest - Plot the aggregated
.bigwigcoverage over theGRangesof interest
Eventually, the functions shipped in this package should be able to turn a pair of .bigwig/.bed files into an aggregated coverage profile — exactly the kind of plot you already produced by hand at the end of Day 1 (mean signal, with its confidence interval, as a function of the distance to the center of the features).
2.2 Starting a new package
To start developing a new package, one may want to make sure all useful developer resources are already set-up.
Make sure to install the developer toolkit packages.
CRAN packages are installed with remotes::install_cran(), and Bioconductor packages with BiocManager::install(). You will need, among others, devtools, usethis, available (from CRAN) and biocthis, BiocCheck (from Bioconductor).
Now the time has come to create your very first package! To keep a coherent cohorte, we’ll create a package named <YOURNAME>TestPackage. But first, you need to make sure that this package name is available.
If this name is available, let’s create your package.
Which function from devtools can be used to create a package skeleton? Use it to create your package.
When creating your package, devtools+RStudio combo automatically opens a new RStudio window, with the package’s project automatically activated. Navigate to your package RStudio project window and re-open this instruction sheet.
Make sure you’re in the right project using the appropriate function from rstudioapi package.
rstudioapi::getActiveProject() returns the path of the currently active RStudio project. If it is not the right one, usethis::proj_activate() can switch to it.
Explore the newly created folder architecture. Do you recognize all the files and directories?
The DESCRIPTION file automatically created does not exactly match Bioconductor requirements. You should replace it by a better-fitted DESCRIPTION file.
Manually edit the important fields from your new DESCRIPTION file: Title & Author. The remaining fields will be filled out later on.
A nice touch is to systematically provide a README file. This provides a good overview of your package when browsed e.g. in GitHub, and for non-R specialists, who don’t necessarily know about DESCRIPTION and vignettes. And since you are developing an R package, let’s stick with the Rmd format for your README.
What are the benefits of having a README.Rmd file?
Which function from biocthis can create a .Rmd README file?
Again a biocthis::use_bioc_*() function — this time one that mentions readme and rmd. A README.Rmd lets you embed runnable R code (and its output) directly in the README.
That being said, package submission requires that a REAMDE.md file only is present in the repository. A way to provide this is to 1) devtools::build_readme() to parse README.Rmd into README.md (with R chunks processed!), and 2) add README.Rmd to .gitignore.
Parse your README.Rmd into README.md and check the differences.
devtools::build_readme() knits README.Rmd into README.md. Load your package first with devtools::load_all('.') so that any package code used in the README is available.
A bunch of extra useful commands from bioc/usethis can be performed at this stage, e.g. to add a NEWS file, a Code of Conduct, a citation file, etc… Beware, each command creates/modify one/several files. Pay attention to the comments printed in the R console!
Run each of the following commands one by one and make sure to fulfill each required action highlighted in the R console.
biocthis::use_bioc_news_md() ## To add a NEWS.md file
biocthis::use_bioc_coc() ## To add a .github/CODE_OF_CONDUCT.md file
biocthis::use_bioc_support() ## To add a .github/SUPPORT.md file
biocthis::use_bioc_issue_template() ## To add a .github/ISSUE_TEMPLATE/issue_template.md file
biocthis::use_bioc_citation() ## To add a inst/CITATION file
usethis::use_lifecycle_badge("Experimental") ## To add a "Experimental" badge to your README.Rmd
devtools::build_readme() ## To rebuild README.md2.3 Write functions to populate your package
To write your first package function, you can either create an R/<name>.R file manually, or run usethis::use_r(name).
In Day 1, you already prototyped three of these functions (importFiles(), filterGRanges(), computeCoverage()) in a plain script. Your job today is to move them into the package (one R/<name>.R file each), write the missing fourth function (plotCoverage()), and document them all. Prototyping in a script, then “packaging”, is exactly how real development goes!
Now is time to write! Refer to the functionalities (described in Section 0) that we are aiming to provide in this package, and start writing 4 functions which, together, can fulfill these functionalities. Briefly, the four functions are:
-
importFiles(): import a coverage track and a set of genomic features (scaled to a fixed width) together in a list; -
filterGRanges(): filter the imported features to only retain those fully overlapping with covered genome segments (from the coverage track); -
computeCoverage(): compute the mean coverage and its confidence interval, for each position within the features’ width; -
plotCoverage(): plot the results withggplot2.
We suggest these exact function names because the exercises for the following days (Day 3, 4 and 5) reuse them. Using the same names will let your package grow smoothly across the week.
The only genuinely new function today is plotCoverage(), which draws the aggregated profile you sketched at the end of Day 1.
Write a plotCoverage(df) function that takes the data.frame returned by computeCoverage() and returns a ggplot.
Map distance to x and mean to y. Add a geom_ribbon() for the confidence interval (ymin = ci_low, ymax = ci_high) and a geom_line() for the mean.
plotCoverage <- function(df) {
ggplot2::ggplot(df, ggplot2::aes(x = distance, y = mean)) +
ggplot2::geom_ribbon(
ggplot2::aes(ymin = ci_low, ymax = ci_high),
fill = "steelblue", alpha = 0.2
) +
ggplot2::geom_line(color = "steelblue") +
ggplot2::labs(x = "Distance to center (bp)", y = "Mean coverage") +
ggplot2::theme_bw()
}The complete reference implementation of the four function files is available in Share/functions/. These files provide one possible implementation fulfilling our package’s requirements.
2.4 Add function documentation
Write roxygen tags (@title, @description, @param, @return, …) for each function.
roxygen comments start with #' and sit directly above the function. The first line is the title, a blank #' line separates blocks, and each @param documents one argument. Do not forget @export (so the function is usable) and @importFrom pkg fun (to declare the functions you borrow from other packages).
#' Import a coverage track and a set of genomic features
#'
#' Longer description of what the function does.
#'
#' @param bw_file Path to a coverage track file.
#' @param features_file Path to a features file.
#' @param width Integer. Width to resize each feature to.
#'
#' @return A named list with `coverage` and `features`.
#'
#' @importFrom rtracklayer import
#' @importFrom GenomicRanges resize
#' @export
importFiles <- function(bw_file, features_file, width) {
# ...
}Once you have documented each function, you can regenerate the documentation with document().