[loomr.git] / R / package.R
1 # Copyright 2017, 2018 Paul Hoffman <phoffman@nygenome.org>
2 #
3 # This file is part of loomR.
4 #
5 # loomR is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # loomR is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with loomR.  If not, see <https://www.gnu.org/licenses/>.
18 #' @importFrom methods setOldClass
21 #' An R interface for loom files
22 #'
23 #' loomR provides an interface for working with loom files in a loom-specific way.
24 #' We provide routines for validating loom files,
25 #' iterating with chunks through data within the loom file,
26 #' and provide a platform for other packages to build support for loom files.
27 #' Unlike other HDF5 pacakges, loomR actively protectes a loom file's structure, enabling the
28 #' user to focus on their analysis and not worry about the integrity of their data.
29 #'
30 #' @section Semantics:
31 #' Throughout all loomR-related documentation and writing, the following styles for distinguising between loom files,
32 #' \code{loom} objects, and loomR will and be used. When talking about loom files, or the actual HDF5 file on disk,
33 #' the word 'loom' will be written in normal text. Capitalization will be done based on a language's rules for
34 #' capitalization in sentences. For English, that means if the word 'loom' appears at the beginning of a sentence
35 #' and is being used to refer to a loom file, it will be capilatized. Otherwise, it will be lowercase.
36 #' For \code{loom} objects, or the object within R, the word 'loom' will always be lowercase and written in monospaced text.
37 #' When referring to the pacakge loomR, it will always be written in normal text with the 'l', 'o's, and 'm' lowercased and
38 #' the 'R' uppercased. This style will be used throughout documentation for loomR as well as any vignettes and tutorials
39 #' produced by the authors.
40 #'
41 #' @section Loom Files:
42 #' Loom files are an HDF5-based format for storing and interacting with large single-cell RNAseq datasets.
43 #' Each loom file has at least six parts to it:
44 #' the raw expression data (\code{matrix}),
45 #' groups for gene- and cell-metadata (\code{row_attrs} and \code{col_attrs}, respectively),
46 #' groups for gene-based and cell-based cluster graphs (\code{row_graphs} and \code{col_graphs}, respectively),
47 #' and \code{layers}, a group containing alternative representations of the data in \code{matrix}.
48 #' Each dataset within the loom file has rules as to what size it may be, creating a structure for the entire loom file and all the data within.
49 #' This structure is enforced to ensure that data remains intact and retriveable when spread across the various datasets in the loom file.
50 #'
51 #' \describe{
52 #'   \item{\code{matrix}}{
53 #'     The dataset that sets the dimensions for most other datasets within a loom file. This dataset has 'n' genes and 'm' cells.
54 #'     Due to the way that loomR presents data, this will appear as 'm' rows and 'n' columns. However, other HDF5 libraries will
55 #'     generally present the data as 'n' rows and 'm' columns
56 #'   }
57 #'   \item{\code{row_attrs} and \code{col_attrs}}{
58 #'     These are one- or two-dimensional datasets where a specific dimension is of length 'n', for row attributes, or 'm', for column attributes.
59 #'     Within loomR, this must be the second dimension of two-dimensional datasets, or the length of one-dimensional datasets Most other
60 #'     HDF5 libraries will show this specific dimension as the first dimension for two-dimensional datasets, or the length of one-dimensional
61 #'     datasets.
62 #'   }
63 #'   \item{\code{row_graphs} and \code{col_graphs}}{
64 #'     Unlike other datasets within a loom file, these are not controlled by \code{matrix}. Instead, within these groups are groups for
65 #'     specific graphs. Each graph group will have three datasets that represent the graph in
66 #'     \href{https://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_(COO)}{coordinate format}: \code{a} for row indices, \code{b} for
67 #'     column indices, and \code{w} for values. Each dataset within a graph must be one-dimensional and all datasets within a graph must be
68 #'     the same length. Not all graphs must be the same length as each other.
69 #'   }
70 #'   \item{\code{layers}}{Each dataset within \code{layers} must have the exact same dimensions as \code{matrix}}
71 #' }
72 #'
73 #' @section Chunk-based iteration:
74 #' As loom files can theoretically hold million-cell datasets, performing analysis on these datasets can be impossible due to the memory
75 #' requirements for holding such a dataset in memory. To combat this problem, \code{loom} objects offer native chunk-based iteration through
76 #' the \code{batch.scan}, \code{batch.next}, \code{map}, and \code{apply} methods. This section will cover the former two methods; the latter
77 #' two are covered in the \href{http://satijalab.org/loomR/loomR_tutorial.html}{loomR tutorial}.
78 #'
79 #' \code{batch.scan} and \code{batch.next} are the heart of all chunk-based iteration in the \code{loom} object. These two methods make
80 #' use of \code{\link{itertools::ichunk}} object to chunk through the data in a loom file. Due to the way that R works, \code{batch.scan}
81 #' initializes the iterator and \code{batch.next} moves through the iterator.
82 #'
83 #' The \code{batch.scan} method will break a dataset in the loom file into chunks, based on a chunk size given to it. \code{batch.scan} will
84 #' work on any dataset, except for two-dimensional attributes and any graph dataset. When iterating over \code{matrix} and the layers, the \code{MARGIN}
85 #' argument tells the \code{loom} object which way to chunk the data. A \code{MARGIN} of 1 will chunk over genes while a \code{MARGIN} of 2 will chunk
86 #' over cells. For one-dimmensional attributes, \code{MARGIN} is ignored. \code{batch.scan} returns an integer whose length is the number of iterations
87 #' it takes to iterate over the dataset selected.
88 #'
89 #' Pulling data in chunks is done by \code{batch.next}. This method simply returns the next chunk of data. If \code{return.data = FALSE} is passed,
90 #' \code{batch.next} will instead return the indices of the next chunk. When using these methods, we recommend storing the results of \code{batch.scan}
91 #' and iterating through this vector to keep track of where the \code{loom} object is in the iteration.
92 #' \preformatted{
93 #'   # Set up the iterator on the `loom` object lfile
94 #'   batch <- lfile$batch.scan(dataset.use = 'matrix', MARGIN = 2)
95 #'   # Iterate through the dataset, pulling data
96 #'   # If `return.data = FALSE` is passed, the indices
97 #'   # of the next chunk will be returned instead
98 #'   for (i in batch) {
99 #'     data.use <- lfile$batch.next()
100 #'   }
101 #' }
102 #'
103 #' @section Extending loomR:
104 #' The \code{loom} class is the heart of loomR. This class is written in the
105 #' \href{https://cran.r-project.org/web/packages/R6/vignettes/Introduction.html}{R6} object style and can be extended in three ways.
106 #' For each of the following, one be discretionary when \code{return} is used instead of \code{\link{invisible}}. As \code{loom} object are merely
107 #' handles to loom files, any function or method that modifies the file should not need to return anything. However, we recommend always returning
108 #' the \code{loom} object invisibly, using \code{\link{invisible}}. While not necessary for functionality, it means that objects in a user's environment
109 #' won't get overwritten if they try to reassign their \code{loom} object to the output of a function. For functions and methods that don't modify the
110 #' loom file, and instead return data, then the \code{return} function should be used.
111 #'
112 #' The first way to extend \code{loom} objects is by subclassing the object and making a new R6 class. This allows new classes to
113 #' declare custom R6 methods and gain access to all of the \code{loom} object's methods, including S3- and S4-style methods.
114 #' New classes can also overwrite any methods for \code{loom} objects, allowing the extender to change the core behaviour of \code{loom} objects.
115 #' While this option allows the greatest control and access to the \code{loom} object, it involves the greatest amount of work
116 #' as one would need to write a new R6 class and all the associated boilerplate code. As such, we recommend subclassing \code{loom} objects
117 #' when a new class is needed, but would advise developers to use the other methods of extending \code{loom} objects for simpler tasks.
118 #'
119 #' The second way is by using S4-style methods can be written for \code{loom} objects. loomR exports the \code{loom} class as an S4 class, allowing
120 #' one to write highly-specialized methods that enforce class-specificity and can change behaviour based on the classes of other objects provided to
121 #' a function. S4 methods look like normal functions to the end user, but can do different things based on the class, or classes, of objects passed to it.
122 #' This allows for highly-customized routines without cluttering a package's namespace, as only the generic function is exported. S4 methods can also be
123 #' written for generics exported by other packages, assuming the said generic has been imported before writing new methods. Furthermore, generics
124 #' and methods can be kept internally, and R will dispatch the appropriate method as if the generic was exported. However, S4 methods have the drawback
125 #' of not autocompleting arguments in the terminal or RStudio. This means that the user may need to keep documentation open while using these methods,
126 #' which detracts from the user-friendliness of these methods. Finally, while there is less boilerplate in declaring S4 generics and methods than
127 #' declaring R6 classes and methods, there is still more to write than our last method. As such, we recommend S4 methods for anyone who needs method
128 #' dispatch for internal functions only.
129 #' \preformatted{
130 #'   #' @export SomeFunction
131 #'   methods::setGeneric(
132 #'     name = 'SomeFunction',
133 #'     def = function(object, ...) {
134 #'       return(standardGeneric(f = 'SomeFunction))
135 #'     }
136 #'   )
137 #'
138 #'   # Note, no extra Roxygen notes needed
139 #'   methods::setMethod(
140 #'     f = 'SomeFunction',
141 #'     signature = c('object' = 'loom'),
142 #'     definition = function(object, loom.param, ...) {
143 #'       # do something
144 #'     }
145 #'   )
146 #' }
147 #'
148 #' As R6 objects are based on S3 objects, the final way to extend \code{loom} objects is by writing S3-style methods. These methods involve the
149 #' least amount of boilerplate to set up. S3 generics are written just like normal functions, albiet with a few differences. Firstly, they have
150 #' two arguments: the argument that determines the class for dispatching and \code{...} to pass other arguments to methods. Finally, the only
151 #' thing an S3 generic needs to do is call \code{UseMethod} to allow R to dispatch based on the class of whatever the object is. Unlike S4 methods,
152 #' S3 methods provide tab-autocompletion for method-specific arguments, providing help messages along the way. This means that S3 methods are more
153 #' user-friendly than S4 methods. Like S4 methods, S3 methods can use S3 generics declared by other packages, with the same assumptions about
154 #' imports applying here as well. However, S3 methods cannot be kept internally, and must be exported for R to properly dispatch the method. This means
155 #' that a package's namespace will have n + 1 functions declared for every S3 generic, where n is the number of classes a method is declared for and the
156 #' one extra is for the generic. Furthermore, as the methods themselves are exported, anyone can simply use the method directly rather than go through
157 #' the generic and have R dispatch a method based on object class. Despite these drawbacks, S3 methods are how we recommend one extends loomR unless
158 #' one needs the specific features of R6 classes or S4-style methods.
159 #' \preformatted{
160 #'   #' @export somefunction
161 #'   somefunction <- function(object, ...) {
162 #'     UseMethod('somefunction', object)
163 #'   }
164 #'
165 #'   #' @export somefunction.loom
166 #'   #' @method somefunction loom
167 #'   somefunction.loom <- function(object, loom.param, ...) {
168 #'     # do something
169 #'   }
170 #' }
171 #'
172 #' @docType package
173 #' @name loomR-package
174 #'
175 NULL
178 # Hooks to set loom as an S4 class upon
179 # loadNamespace or library/require
180 .onLoad <- function(libname, pkgname) {
181   setOldClass(Classes = 'loom')
182 }