Browse code

Updates to plot SCE - Fixes palette option - Orders columns by PC1 - Support column summary - New color palettes and scaling options

Ashastry2 authored on 26/08/2024 04:24:39
Showing 1 changed files

... ...
@@ -50,8 +50,12 @@
50 50
 #' cell labeling. Should match the entries in the \code{cellAnnotations} or
51 51
 #' \code{colDataName}. For each entry, there should be a list/vector of colors
52 52
 #' named with categories. Default \code{NULL}.
53
-#' @param palette Choose from \code{"ggplot"}, \code{"celda"} or \code{"random"}
53
+#' @param annotationPalette Choose from \code{"ggplot"}, \code{"celda"} or \code{"random"}
54 54
 #' to generate unique category colors.
55
+#' @param palette Choose from \code{"sequential"}, \code{"diverging"} or supply custom palette with colorScheme
56
+#' to generate unique category colors. Default is \code{"sequential"}
57
+#' @param addCellSummary Add summary barplots to column annotation. Supply the name of the column in colData as a character. This option will add summary for categorical variables 
58
+#' as stacked barplots.
55 59
 #' @param rowSplitBy character. Do semi-heatmap based on the grouping of
56 60
 #' this(these) annotation(s). Should exist in either \code{rowDataName} or
57 61
 #' \code{names(featureAnnotations)}. Default \code{NULL}.
... ...
@@ -98,7 +102,7 @@
98 102
 #' @importFrom SummarizedExperiment colData assayNames<-
99 103
 plotSCEHeatmap <- function(inSCE, useAssay = 'logcounts', useReducedDim = NULL,
100 104
                            doLog = FALSE, featureIndex = NULL, cellIndex = NULL,
101
-                           scale = TRUE, trim = c(-2, 2),
105
+                           scale = TRUE, trim = c(-2,2),
102 106
                            featureIndexBy = 'rownames',
103 107
                            cellIndexBy = 'rownames',
104 108
                            rowDataName = NULL, colDataName = NULL,
... ...
@@ -106,7 +110,9 @@ plotSCEHeatmap <- function(inSCE, useAssay = 'logcounts', useReducedDim = NULL,
106 110
                            featureAnnotations = NULL, cellAnnotations = NULL,
107 111
                            featureAnnotationColor = NULL,
108 112
                            cellAnnotationColor = NULL,
109
-                           palette = c("ggplot", "celda", "random"),
113
+                           annotationPalette = c("ggplot", "celda", "random"),
114
+                           palette = c("sequential","diverging"),
115
+                           addCellSummary = NULL,
110 116
                            rowSplitBy = NULL, colSplitBy = NULL,
111 117
                            rowLabel = FALSE, colLabel = FALSE,
112 118
                            rowLabelSize = 6, colLabelSize = 6,
... ...
@@ -116,239 +122,327 @@ plotSCEHeatmap <- function(inSCE, useAssay = 'logcounts', useReducedDim = NULL,
116 122
                            rowGap = grid::unit(0, 'mm'),
117 123
                            colGap = grid::unit(0, 'mm'),
118 124
                            border = FALSE, colorScheme = NULL, ...){
119
-    palette <- match.arg(palette)
120
-    # STAGE 1: Create clean SCE object with only needed information ####
121
-    ## .selectSCEMatrix, .manageCellVar and .manageFeatureVar perform checks
122
-    useMat <- .selectSCEMatrix(inSCE, useAssay = useAssay,
123
-                               useReducedDim = useReducedDim,
124
-                               returnMatrix = TRUE, cellAsCol = TRUE)
125
-    useAssay <- useMat$names$useAssay
126
-    useReducedDim <- useMat$names$useReducedDim
127
-    useData <- ifelse(!is.null(useAssay), useAssay, useReducedDim)
128
-    ### cell annotation
129
-    colDataName <- unique(c(colDataName, aggregateCol))
130
-    colDataAnns <- lapply(colDataName, function(x) .manageCellVar(inSCE, x))
131
-    if (length(colDataName) > 0)
132
-        colDataAnns <- data.frame(colDataAnns, row.names = colnames(inSCE))
125
+  annotationPalette<-match.arg(annotationPalette)
126
+  palette<-match.arg(palette)
127
+  # STAGE 1: Create clean SCE object with only needed information ####
128
+  ## .selectSCEMatrix, .manageCellVar and .manageFeatureVar perform checks
129
+  useMat <- .selectSCEMatrix(inSCE, useAssay = useAssay,
130
+                             useReducedDim = useReducedDim,
131
+                             returnMatrix = TRUE, cellAsCol = TRUE)
132
+  useAssay <- useMat$names$useAssay
133
+  useReducedDim <- useMat$names$useReducedDim
134
+  useData <- ifelse(!is.null(useAssay), useAssay, useReducedDim)
135
+  ### cell annotation
136
+  oldColData <- colData(inSCE)
137
+  colDataName <- unique(c(colDataName, aggregateCol))
138
+  colDataAnns <- lapply(colDataName, function(x) .manageCellVar(inSCE, x))
139
+  if (length(colDataName) > 0)
140
+    colDataAnns <- data.frame(colDataAnns, row.names = colnames(inSCE))
141
+  else
142
+    colDataAnns <- data.frame(row.names = colnames(inSCE))
143
+  colnames(colDataAnns) <- colDataName
144
+  cellAnnotations <- .mergeAnnotationDF(colDataAnns, cellAnnotations)
145
+  if (!is.null(colSplitBy) &&
146
+      any(!colSplitBy %in% colnames(cellAnnotations)))
147
+    stop('Specified `colSplitBy` variables not found.')
148
+  if (isTRUE(colLabel)) {
149
+    colLabelName <- colnames(inSCE)
150
+  } else if (isFALSE(colLabel)) {
151
+    colLabelName <- NULL
152
+  } else {
153
+    colLabelName <- .manageCellVar(inSCE, colLabel)
154
+    colLabel <- TRUE
155
+  }
156
+  ### feature annotation
157
+  rowDataAnns <- data.frame(row.names = rownames(useMat$mat))
158
+  if (!is.null(useAssay)) {
159
+    # When using reducedDim, no rowData can be applied
160
+    rowDataName <- unique(c(rowDataName, aggregateRow))
161
+    rowDataAnns <- lapply(rowDataName, function(x) .manageFeatureVar(inSCE, x))
162
+    if (length(rowDataName) > 0)
163
+      rowDataAnns <- data.frame(rowDataAnns, row.names = rownames(inSCE))
133 164
     else
134
-        colDataAnns <- data.frame(row.names = colnames(inSCE))
135
-    colnames(colDataAnns) <- colDataName
136
-    cellAnnotations <- .mergeAnnotationDF(colDataAnns, cellAnnotations)
137
-    if (!is.null(colSplitBy) &&
138
-        any(!colSplitBy %in% colnames(cellAnnotations)))
139
-        stop('Specified `colSplitBy` variables not found.')
140
-    if (isTRUE(colLabel)) {
141
-        colLabelName <- colnames(inSCE)
142
-    } else if (isFALSE(colLabel)) {
143
-        colLabelName <- NULL
144
-    } else {
145
-        colLabelName <- .manageCellVar(inSCE, colLabel)
146
-        colLabel <- TRUE
147
-    }
148
-    ### feature annotation
149
-    rowDataAnns <- data.frame(row.names = rownames(useMat$mat))
165
+      rowDataAnns <- data.frame(row.names = rownames(inSCE))
166
+    colnames(rowDataAnns) <- rowDataName
167
+  }
168
+  # But customized featureAnnotations should work
169
+  featureAnnotations <- .mergeAnnotationDF(rowDataAnns, featureAnnotations)
170
+  if (!is.null(rowSplitBy) &&
171
+      any(!rowSplitBy %in% colnames(featureAnnotations)))
172
+    stop('Specified `rowSplitBy` variables not found.')
173
+  if (isTRUE(rowLabel)) {
174
+    rowLabelName <- rownames(useMat$mat)
175
+  } else if (isFALSE(rowLabel)) {
176
+    rowLabelName <- NULL
177
+  } else {
150 178
     if (!is.null(useAssay)) {
151
-        # When using reducedDim, no rowData can be applied
152
-        rowDataName <- unique(c(rowDataName, aggregateRow))
153
-        rowDataAnns <- lapply(rowDataName, function(x) .manageFeatureVar(inSCE, x))
154
-        if (length(rowDataName) > 0)
155
-            rowDataAnns <- data.frame(rowDataAnns, row.names = rownames(inSCE))
156
-        else
157
-            rowDataAnns <- data.frame(row.names = rownames(inSCE))
158
-        colnames(rowDataAnns) <- rowDataName
159
-    }
160
-    # But customized featureAnnotations should work
161
-    featureAnnotations <- .mergeAnnotationDF(rowDataAnns, featureAnnotations)
162
-    if (!is.null(rowSplitBy) &&
163
-        any(!rowSplitBy %in% colnames(featureAnnotations)))
164
-        stop('Specified `rowSplitBy` variables not found.')
165
-    if (isTRUE(rowLabel)) {
166
-        rowLabelName <- rownames(useMat$mat)
167
-    } else if (isFALSE(rowLabel)) {
168
-        rowLabelName <- NULL
179
+      rowLabelName <- .manageFeatureVar(inSCE, rowLabel)
180
+      rowLabel <- TRUE
169 181
     } else {
170
-        if (!is.null(useAssay)) {
171
-            rowLabelName <- .manageFeatureVar(inSCE, rowLabel)
172
-            rowLabel <- TRUE
173
-        } else {
174
-            # Using customized rowLabel for reducedDim
175
-            if (length(rowLabel) != nrow(useMat$mat))
176
-                stop("Length of `rowLabel` does not match nrow of specified ",
177
-                     "`useReducedDim`")
178
-            rowLabelName <- rowLabel
179
-            rowLabel <- TRUE
180
-        }
182
+      # Using customized rowLabel for reducedDim
183
+      if (length(rowLabel) != nrow(useMat$mat))
184
+        stop("Length of `rowLabel` does not match nrow of specified ",
185
+             "`useReducedDim`")
186
+      rowLabelName <- rowLabel
187
+      rowLabel <- TRUE
181 188
     }
182
-    ### create SCE object
183
-    SCE <- SingleCellExperiment(assay = list(useMat$mat),
184
-                                colData = cellAnnotations,
185
-                                rowData = featureAnnotations)
186
-    assayNames(SCE) <- useData
187
-    # STAGE 2: Subset SCE object as needed ####
188
-    # Manage cell subsetting
189
-    if(is.null(cellIndex)){
190
-        cellIndex <- seq(ncol(SCE))
191
-    } else if (is.character(cellIndex)) {
192
-        # cellIndexBy not necessarily included in new "SCE"
193
-        cellIndex <- retrieveSCEIndex(inSCE, cellIndex, axis = "col",
194
-                                      by = cellIndexBy)
195
-    } else if (is.logical(cellIndex)) {
196
-        if (length(cellIndex) != ncol(inSCE)) {
197
-            stop("Logical index length does not match ncol(inSCE)")
198
-        }
199
-        cellIndex <- which(cellIndex)
189
+  }
190
+  ### create SCE object
191
+  SCE <- SingleCellExperiment(assay = list(useMat$mat),
192
+                              colData = cellAnnotations,
193
+                              rowData = featureAnnotations)
194
+  assayNames(SCE) <- useData
195
+  
196
+  .minmax<-function(mat){
197
+    min_max<- function(x) {
198
+      new_x =  (x - min(x))/ (max(x) - min(x))
199
+      return(new_x)}
200
+    new_mat<-as.matrix(apply(mat,FUN = min_max,MARGIN = 2))
201
+    return(new_mat)
200 202
     }
201
-    # Manage feature subsetting
202
-    if(is.null(featureIndex)){
203
-        featureIndex <- seq(nrow(SCE))
204
-    } else if (is.character(featureIndex)) {
205
-        if (!is.null(useAssay))
206
-            featureIndex <- retrieveSCEIndex(inSCE, featureIndex, axis = "row",
207
-                                             by = featureIndexBy)
208
-        else
209
-            # When using reducedDim, can only go with "PC" names
210
-            # or customized "by"
211
-            featureIndex <- retrieveSCEIndex(SCE, featureIndex, axis = "row",
212
-                                             by = featureIndexBy)
213
-    } else if (is.logical(featureIndex)) {
214
-        if (length(featureIndex) != nrow(SCE)) {
215
-            stop("Logical index length does not match nrow(inSCE)")
216
-        }
217
-        featureIndex <- which(featureIndex)
203
+  
204
+  # STAGE 2: Subset SCE object as needed ####
205
+  # Manage cell subsetting
206
+  if(is.null(cellIndex)){
207
+    cellIndex <- seq(ncol(SCE))
208
+  } else if (is.character(cellIndex)) {
209
+    # cellIndexBy not necessarily included in new "SCE"
210
+    cellIndex <- retrieveSCEIndex(inSCE, cellIndex, axis = "col",
211
+                                  by = cellIndexBy)
212
+  } else if (is.logical(cellIndex)) {
213
+    if (length(cellIndex) != ncol(inSCE)) {
214
+      stop("Logical index length does not match ncol(inSCE)")
218 215
     }
219
-    colnames(SCE) <- colLabelName
220
-    rownames(SCE) <- rowLabelName
221
-    SCE <- SCE[featureIndex, cellIndex]
222
-    ### Scaling should be done before aggregating
223
-    if (isTRUE(doLog)) assay(SCE) <- log1p(assay(SCE))
224
-    if (isTRUE(scale)) assay(SCE) <- as.matrix(computeZScore(assay(SCE)))
225
-    if (!is.null(trim)) assay(SCE) <- trimCounts(assay(SCE), trim)
226
-    # STAGE 3: Aggregate As needed ####
227
-    if (!is.null(aggregateCol)) {
228
-        # TODO: whether to also aggregate numeric variable that users want
229
-        # Might need to use "coldata.merge" in aggregate function
230
-        colIDS <- colData(SCE)[, aggregateCol]
231
-        origRowData <- rowData(SCE)
232
-        SCE <- aggregateAcrossCells(SCE, ids = colIDS,
233
-                                    use.assay.type = useData,
234
-                                    store.number = NULL, statistics = "mean")
235
-        # TODO: `aggregateAcrossCells` produce duplicated variables in colData
236
-        # and unwanted "ncell" variable even if I set `store.number = NULL`.
237
-        colData(SCE) <- colData(SCE)[,aggregateCol,drop=FALSE]
238
-        newColnames <- do.call(paste, c(colData(SCE), list(sep = "_")))
239
-        colnames(SCE) <- newColnames
240
-        rowData(SCE) <- origRowData
241
-    }
242
-    if (!is.null(aggregateRow)) {
243
-        # `aggregateAcrossFeatures` doesn't work by with multi-var
244
-        # Remake one single variable vector
245
-        rowIDS <- rowData(SCE)[, aggregateRow, drop = FALSE]
246
-        rowIDS <- do.call(paste, c(rowIDS, list(sep = "_")))
247
-        origColData <- colData(SCE)
248
-        SCE <- aggregateAcrossFeatures(SCE, ids = rowIDS, average = TRUE,
249
-                                       use.assay.type = useData)
250
-        colData(SCE) <- origColData
216
+    cellIndex <- which(cellIndex)
217
+  }
218
+  # Manage feature subsetting
219
+  if(is.null(featureIndex)){
220
+    featureIndex <- seq(nrow(SCE))
221
+  } else if (is.character(featureIndex)) {
222
+    if (!is.null(useAssay))
223
+      featureIndex <- retrieveSCEIndex(inSCE, featureIndex, axis = "row",
224
+                                       by = featureIndexBy)
225
+    else
226
+      # When using reducedDim, can only go with "PC" names
227
+      # or customized "by"
228
+      featureIndex <- retrieveSCEIndex(SCE, featureIndex, axis = "row",
229
+                                       by = featureIndexBy)
230
+  } else if (is.logical(featureIndex)) {
231
+    if (length(featureIndex) != nrow(SCE)) {
232
+      stop("Logical index length does not match nrow(inSCE)")
251 233
     }
252
-    # STAGE 4: Other minor preparation for plotting ####
234
+    featureIndex <- which(featureIndex)
235
+  }
236
+  colnames(SCE) <- colLabelName
237
+  rownames(SCE) <- rowLabelName
238
+  SCE <- SCE[featureIndex, cellIndex]
239
+  ### Scaling should be done before aggregating
240
+  if (isTRUE(doLog)) assay(SCE) <- log1p(assay(SCE))
241
+  if(isTRUE(scale)) scale <- "zscore"
242
+  if ((scale == "zscore")) {
243
+    assay(SCE) <- as.matrix(scale(assay(SCE)))
244
+  } else if (scale ==  "min_max") {
245
+    assay(SCE) <- as.matrix(.minmax(assay(SCE)))
246
+  }    
247
+  
248
+  
249
+  # STAGE 3: Aggregate As needed ####
250
+  if (!is.null(aggregateCol)) {
251
+    # TODO: whether to also aggregate numeric variable that users want
252
+    # Might need to use "coldata.merge" in aggregate function
253
+    colIDS <- colData(SCE)[, aggregateCol]
254
+    origRowData <- rowData(SCE)
255
+    SCE <- aggregateAcrossCells(SCE, ids = colIDS,
256
+                                use.assay.type = useData,
257
+                                store.number = NULL, statistics = "mean")
258
+    # TODO: `aggregateAcrossCells` produce duplicated variables in colData
259
+    # and unwanted "ncell" variable even if I set `store.number = NULL`.
260
+    colData(SCE) <- colData(SCE)[,c(aggregateCol),drop=FALSE] ##change
261
+    newColnames <- do.call(paste, c(colData(SCE), list(sep = "_")))
262
+    colnames(SCE) <- newColnames
263
+    rowData(SCE) <- origRowData
264
+  }
265
+  if (!is.null(aggregateRow)) {
266
+    # `aggregateAcrossFeatures` doesn't work by with multi-var
267
+    # Remake one single variable vector
268
+    rowIDS <- rowData(SCE)[, aggregateRow, drop = FALSE]
269
+    rowIDS <- do.call(paste, c(rowIDS, list(sep = "_")))
270
+    origColData <- colData(SCE)
271
+    SCE <- aggregateAcrossFeatures(SCE, ids = rowIDS, average = TRUE,
272
+                                   use.assay.type = useData)
273
+    colData(SCE) <- origColData
274
+  }
275
+  # STAGE 4: Other minor preparation for plotting ####
276
+
277
+  # Create a function that sorts the matrix by PC1
278
+  .orderMatrix<-function(mat){
279
+    # Adding extra character to rownames because presence of some char gets a "." if I don't
280
+    mat2<-data.frame(t(mat))
281
+    rownames(mat2)<-str_c("K_",rownames(mat2))
282
+    pca_mat<-prcomp(mat2,center = TRUE, scale. = FALSE)
283
+    kl<-arrange(data.frame(pca_mat$x)["PC1"],desc(PC1))
284
+    mat<-data.frame(t(mat2)) %>% dplyr::select(rownames(kl))
285
+    colnames(mat)<-str_replace_all(colnames(mat),"K_","")
286
+    return(as.matrix(mat))
287
+  }
288
+  
289
+  # Prepare
253 290
     mat <- assay(SCE)
254
-    if (is.null(colorScheme)) {
255
-        if (!is.null(trim))
256
-            colorScheme <- circlize::colorRamp2(c(trim[1], 0, trim[2]),
257
-                                                c('blue', 'white', 'red'))
258
-        else
259
-            colorScheme <- circlize::colorRamp2(c(min(mat),
260
-                                                  (max(mat) + min(mat))/2,
261
-                                                  max(mat)),
262
-                                                c('blue', 'white', 'red'))
263
-    } else {
264
-        if (!is.function(colorScheme))
265
-            stop('`colorScheme` must be a function generated by ',
266
-                 'circlize::colorRamp2')
267
-        breaks <- attr(colorScheme, 'breaks')
268
-        if (breaks[1] != min(trim) || breaks[length(breaks)] != max(trim))
269
-            stop('Breaks of `colorScheme` do not match with `trim`.')
291
+    mat <- .orderMatrix(mat)
292
+ 
293
+  
294
+  if (!is.null(trim) & scale == "zscore") {
295
+    assay(SCE) <- trimCounts(assay(SCE), trim)  
296
+    print(quantile(assay(SCE)))
297
+  }    
298
+  
299
+  
300
+  if (is.null(colorScheme)) {
301
+    if (isFALSE(scale)){
302
+      if (palette == "sequential"){
303
+        colorScheme <- circlize::colorRamp2(quantile(mat),
304
+                                            c('white', "#fecc5c",'#fdae61',"#f03b20","#bd0026"))
305
+      }
306
+      else if (palette == "diverging"){
307
+      colorScheme <- circlize::colorRamp2(c(min(mat),
308
+                                            (max(mat) + min(mat))/2,
309
+                                            max(mat)),
310
+                                          c('blue', 'white', 'red'))
311
+      }
270 312
     }
271
-    ### Generate HeatmapAnnotation object
272
-    ca <- NULL
273
-    cellAnnotationColor <- .heatmapAnnColor(SCE, slot = "colData",
274
-                                            custom = cellAnnotationColor,
275
-                                            palette = palette)
276
-    if(dim(cellAnnotations)[2] > 0)
277
-        ca <- ComplexHeatmap::HeatmapAnnotation(df = colData(SCE),
278
-                                                col = cellAnnotationColor)
279
-    ra <- NULL
280
-    featureAnnotationColor <- .heatmapAnnColor(SCE, slot = "rowData",
281
-                                               custom = featureAnnotationColor,
282
-                                               palette = palette)
283
-    if(ncol(rowData(SCE)) > 0)
284
-        ra <- ComplexHeatmap::rowAnnotation(df = rowData(SCE),
285
-                                            col = featureAnnotationColor)
286
-    ### Set split variable
287
-    cs <- NULL
288
-    if (!is.null(colSplitBy)) cs <- colData(SCE)[colSplitBy]
289
-    rs <- NULL
290
-    if (!is.null(rowSplitBy)) rs <- rowData(SCE)[rowSplitBy]
291
-    ###
292
-    if (!is.null(colGap)) {
293
-        if (!inherits(colGap, "unit"))
294
-            stop("`colGap` has to be 'unit' object. Try `grid::unit(", colGap,
295
-                 ", 'mm')`.")
313
+    else if (scale == "zscore"){
314
+      colorScheme <- circlize::colorRamp2(quantile(assay(SCE)),
315
+                                          c('#2c7bb6','#abd9e9','#ffffbf','#fdae61','#d7191c'))
296 316
     }
297
-    else colGap <- grid::unit(0, 'mm')
298
-    if (!is.null(rowGap)) {
299
-        if (!inherits(rowGap, "unit"))
300
-            stop("`rowGap` has to be 'unit' object. Try `grid::unit(", rowGap,
301
-                 ", 'mm')`.")
317
+    else if (scale == "min_max"){
318
+      if(palette == "sequential"){
319
+        colorScheme <- circlize::colorRamp2(c(0,0.3,0.6,0.8,1),
320
+                                            c('white', "#fecc5c",'#fdae61',"#f03b20","#bd0026")) 
321
+        
322
+      }
323
+      else if (palette == "diverging") {
324
+      colorScheme <- circlize::colorRamp2(c(0,0.3,0.6,0.8,1),
325
+                                          c('#2c7bb6','#abd9e9','#ffffbf','#fdae61','#d7191c'))     
326
+      }
302 327
     }
303
-    else rowGap <- grid::unit(0, 'mm')
304
-
305
-    if (!is.null(useAssay)) name <- useAssay
306
-    else name <- useReducedDim
307
-    hm <- ComplexHeatmap::Heatmap(mat, name = name, left_annotation = ra,
308
-                                  top_annotation = ca, col = colorScheme,
309
-                                  row_split = rs, column_split = cs,
310
-                                  row_title = rowTitle, column_title = colTitle,
311
-                                  show_row_names = rowLabel,
312
-                                  row_names_gp = grid::gpar(fontsize = rowLabelSize),
313
-                                  show_row_dend = rowDend,
314
-                                  show_column_names = colLabel,
315
-                                  column_names_gp = grid::gpar(fontsize = colLabelSize),
316
-                                  show_column_dend = colDend,
317
-                                  row_gap = rowGap, column_gap = colGap,
318
-                                  border = border,
319
-                                  ...)
320
-    return(hm)
328
+  } else {
329
+    if (!is.function(colorScheme))
330
+      stop('`colorScheme` must be a function generated by ',
331
+           'circlize::colorRamp2')
332
+    breaks <- attr(colorScheme, 'breaks')
333
+    if (breaks[1] != min(trim) || breaks[length(breaks)] != max(trim))
334
+      stop('Breaks of `colorScheme` do not match with `trim`.')
335
+  }
336
+  ### Generate HeatmapAnnotation object
337
+  ca <- NULL
338
+  cellAnnotationColor <- .heatmapAnnColor(SCE, slot = "colData",
339
+                                          custom = cellAnnotationColor,
340
+                                          palette = annotationPalette)
341
+  if(dim(cellAnnotations)[2] > 0)
342
+    if(is.null(addCellSummary)){
343
+      ca <- ComplexHeatmap::HeatmapAnnotation(df = colData(SCE),
344
+                                              col = cellAnnotationColor)
345
+    }
346
+  else if (!addCellSummary %in% colnames(oldColData)){
347
+    stop(addCellSummary,
348
+         "' not found in colData")
349
+  }
350
+  else if (addCellSummary %in% colnames(oldColData)){
351
+    oldColData %>%
352
+      as.data.frame() %>%
353
+      group_by(!!!rlang::syms(aggregateCol),!!!rlang::syms(addCellSummary)) %>%
354
+      count() %>%
355
+      ungroup() %>%
356
+      group_by(!!! rlang::syms(aggregateCol)) %>%
357
+      mutate(sum = sum(n)) %>%
358
+      mutate(value = n/sum) %>%
359
+      select(-n,sum) %>%
360
+      spread(one_of(addCellSummary),value) %>%
361
+      ungroup() %>%
362
+      select(-one_of(aggregateCol),-sum) -> boxdata
363
+    
364
+    
365
+    boxdata[is.na(boxdata)]  <- 0
366
+    boxdata<-as.matrix(boxdata)
367
+    ca <- ComplexHeatmap::HeatmapAnnotation(addCellSummary = anno_barplot(boxdata,
368
+                                                                         gp = gpar(fill = 2:5)),
369
+                                            annotation_label = addCellSummary,
370
+                                            col = cellAnnotationColor)
371
+  }
372
+  ra <- NULL
373
+  featureAnnotationColor <- .heatmapAnnColor(SCE, slot = "rowData",
374
+                                             custom = featureAnnotationColor,
375
+                                             palette = annotationPalette)
376
+  if(ncol(rowData(SCE)) > 0)
377
+    ra <- ComplexHeatmap::rowAnnotation(df = rowData(SCE),
378
+                                        col = featureAnnotationColor)
379
+  ### Set split variable
380
+  cs <- NULL
381
+  if (!is.null(colSplitBy)) cs <- colData(SCE)[colSplitBy]
382
+  rs <- NULL
383
+  if (!is.null(rowSplitBy)) rs <- rowData(SCE)[rowSplitBy]
384
+  ###
385
+  if (!is.null(colGap)) {
386
+    if (!inherits(colGap, "unit"))
387
+      stop("`colGap` has to be 'unit' object. Try `grid::unit(", colGap,
388
+           ", 'mm')`.")
389
+  }
390
+  else colGap <- grid::unit(0, 'mm')
391
+  if (!is.null(rowGap)) {
392
+    if (!inherits(rowGap, "unit"))
393
+      stop("`rowGap` has to be 'unit' object. Try `grid::unit(", rowGap,
394
+           ", 'mm')`.")
395
+  }
396
+  else rowGap <- grid::unit(0, 'mm')
397
+  
398
+  if (!is.null(useAssay)) name <- useAssay
399
+  else name <- useReducedDim
400
+  hm <- ComplexHeatmap::Heatmap(mat, name = name, left_annotation = ra,
401
+                                top_annotation = ca, col = colorScheme,
402
+                                row_split = rs, column_split = cs,
403
+                                row_title = rowTitle, column_title = colTitle,
404
+                                show_row_names = rowLabel,
405
+                                row_names_gp = grid::gpar(fontsize = rowLabelSize),
406
+                                show_row_dend = rowDend,
407
+                                row_dend_reorder = TRUE,
408
+                                cluster_columns = FALSE,
409
+                                show_column_names = colLabel,
410
+                                column_names_gp = grid::gpar(fontsize = colLabelSize),
411
+                                row_gap = rowGap, column_gap = colGap,
412
+                                border = border,
413
+                                ...)
414
+  return(hm)
321 415
 }
322 416
 
323 417
 .mergeAnnotationDF <- function(origin, external) {
324
-    if (!is.null(external)) {
325
-        external <- external[match(rownames(origin), rownames(external)), ,drop = FALSE]
326
-        origin <- cbind(origin, external)
327
-    }
328
-    return(origin)
418
+  if (!is.null(external)) {
419
+    external <- external[match(rownames(origin), rownames(external)), ,drop = FALSE]
420
+    origin <- cbind(origin, external)
421
+  }
422
+  return(origin)
329 423
 }
330 424
 
331 425
 .heatmapAnnColor <- function(inSCE, slot = c("colData", "rowData"),
332
-                          custom = NULL, palette = palette) {
333
-    slot <- match.arg(slot)
334
-    if (!is.null(custom) && !is.list(custom))
335
-        stop("'cellAnnotationColor' or 'featureAnnotationColor' must be a list.")
336
-    if (is.null(custom)) custom <- list()
337
-    if (slot == "colData") data <- SummarizedExperiment::colData(inSCE)
338
-    if (slot == "rowData") data <- SummarizedExperiment::rowData(inSCE)
339
-    todoNames <- colnames(data)
340
-    todoNames <- todoNames[!todoNames %in% names(custom)]
341
-    newColor <- lapply(todoNames, function(n) {
342
-        var <- data[[n]]
343
-        if (is.factor(var)) categories <- levels(var)
344
-        else categories <- unique(var)
345
-        colors <- discreteColorPalette(length(categories), palette = palette)
346
-        names(colors) <- categories
347
-        return(colors)
348
-    })
349
-    names(newColor) <- todoNames
350
-    custom <- c(custom, newColor)
351
-    return(custom)
426
+                             custom = NULL, palette = annotationPalette) {
427
+  slot <- match.arg(slot)
428
+  if (!is.null(custom) && !is.list(custom))
429
+    stop("'cellAnnotationColor' or 'featureAnnotationColor' must be a list.")
430
+  if (is.null(custom)) custom <- list()
431
+  if (slot == "colData") data <- SummarizedExperiment::colData(inSCE)
432
+  if (slot == "rowData") data <- SummarizedExperiment::rowData(inSCE)
433
+  todoNames <- colnames(data)
434
+  todoNames <- todoNames[!todoNames %in% names(custom)]
435
+  newColor <- lapply(todoNames, function(n) {
436
+    var <- data[[n]]
437
+    if (is.factor(var)) categories <- levels(var)
438
+    else categories <- unique(var)
439
+    colors <- discreteColorPalette(length(categories), palette = palette)
440
+    names(colors) <- categories
441
+    return(colors)
442
+  })
443
+  names(newColor) <- todoNames
444
+  custom <- c(custom, newColor)
445
+  return(custom)
352 446
 }
353 447
 # Test
354 448
 #logcounts(sceBatches) <- log1p(counts(sceBatches))
... ...
@@ -370,13 +464,13 @@ plotSCEHeatmap <- function(inSCE, useAssay = 'logcounts', useReducedDim = NULL,
370 464
 #                      aggregateRow = "marker")
371 465
 #plotSCEDimReduceColData(sce, "cluster", "UMAP")
372 466
 CellVarColor <- function(inSCE, var,
373
-                            palette = c("ggplot", "random", "celda"),
374
-                            seed = 12345, ...) {
375
-    var <- .manageCellVar(inSCE, var = var)
376
-    palette <- match.arg(palette)
377
-    if (is.factor(var)) uniqVar <- levels(var)
378
-    else uniqVar <- unique(var)
379
-    colors <- discreteColorPalette(length(uniqVar), palette = palette, seed = seed, ...)
380
-    names(colors) <- uniqVar
381
-    return(colors)
467
+                         annotationPalette = c("ggplot", "random", "celda"),
468
+                         seed = 12345, ...) {
469
+  var <- .manageCellVar(inSCE, var = var)
470
+  palette <- match.arg(annotationPalette)
471
+  if (is.factor(var)) uniqVar <- levels(var)
472
+  else uniqVar <- unique(var)
473
+  colors <- discreteColorPalette(length(uniqVar), palette = palette, seed = seed, ...)
474
+  names(colors) <- uniqVar
475
+  return(colors)
382 476
 }