Browse code

alter_fun in oncoPrint supports a single function

Zuguang Gu authored on 04/03/2016 22:53:18
Showing 9 changed files

... ...
@@ -106,6 +106,7 @@ importFrom("colorspace", diverge_hcl)
106 106
 importFrom("RColorBrewer", brewer.pal)
107 107
 importFrom("dendextend", get_branches_heights)
108 108
 importFrom("dendextend", nnodes)
109
+importFrom("dendextend", "labels<-")
109 110
 import(graphics)
110 111
 import(stats)
111 112
 import(grDevices)
... ...
@@ -5,6 +5,7 @@ CHANEGS in VERSION 1.9.3
5 5
 * add `range` option in `densityHeatmap()`
6 6
 * when `gap` is set for the main heatmap, other heatmps also adjust their `gap` values to it
7 7
 * fixed a bug that when rownames/colnames are not complete, dendrograms are corrupted
8
+* `alter_fun` now support adding graphics grid by grid
8 9
 
9 10
 ==============================
10 11
 
... ...
@@ -235,7 +235,6 @@ setMethod(f = "make_layout",
235 235
     }
236 236
 
237 237
     nr = nrow(object@ht_list[[i_main]]@matrix)
238
-    object@ht_list_param$main_heatmap = i_main
239 238
 
240 239
     if(n > 1) {
241 240
         if(length(gap) == 1) {
... ...
@@ -307,7 +306,8 @@ setMethod(f = "make_layout",
307 306
         gap = gap[seq_len(n-1)]
308 307
     }
309 308
     object@ht_list_param$gap = gap
310
-
309
+    object@ht_list_param$main_heatmap = i_main
310
+    
311 311
     n = length(object@ht_list)
312 312
 
313 313
     ## orders of other heatmaps should be changed
... ...
@@ -379,8 +379,6 @@ setMethod(f = "make_layout",
379 379
     for(i in seq_len(n)) {
380 380
         if(inherits(object@ht_list[[i]], "Heatmap")) {
381 381
             object@ht_list[[i]]@matrix_param$gap = ht_main@matrix_param$gap
382
-        } else {
383
-            object@ht_list[[i]]@gap = ht_main@matrix_param$gap
384 382
         }
385 383
     }
386 384
 
... ...
@@ -8,8 +8,9 @@
8 8
 #      You can use `unify_mat_list` to make all matrix having same row names and column names.
9 9
 # -get_type If different alterations are encoded in the matrix, this self-defined function
10 10
 #           determines how to extract them. Only work when ``mat`` is a matrix.
11
-# -alter_fun_list a list of functions which define how to add graphics for different alterations.
12
-#                 The names of the list should cover all alteration types.
11
+# -alter_fun a single function or a list of functions which define how to add graphics for different alterations.
12
+#                 If it is a list, the names of the list should cover all alteration types.
13
+# -alter_fun_list deprecated, use ``alter_run`` instead.
13 14
 # -col a vector of color for which names correspond to alteration types.
14 15
 # -row_order order of genes. By default it is sorted by frequency of alterations decreasingly.
15 16
 #                            Set it to ``NULL`` if you don't want to set the order
... ...
@@ -17,6 +18,7 @@
17 18
 #                                 the mutual exclusivity across genes. Set it to ``NULL`` if you don't want to set the order
18 19
 # -show_column_names whether show column names
19 20
 # -pct_gp graphic paramters for percent row annotation
21
+# -pct_digits digits for percent values
20 22
 # -axis_gp graphic paramters for axes
21 23
 # -show_row_barplot whether show barplot annotation on rows
22 24
 # -row_barplot_width width of barplot annotation on rows. It should be a `grid::unit` object
... ...
@@ -44,11 +46,11 @@
44 46
 # Zuguang Gu <z.gu@dkfz.de>
45 47
 #
46 48
 oncoPrint = function(mat, get_type = function(x) x,
47
-	alter_fun_list, col, 
49
+	alter_fun = alter_fun_list, alter_fun_list = NULL, col, 
48 50
 	row_order = oncoprint_row_order(),
49 51
 	column_order = oncoprint_column_order(),
50 52
 	show_column_names = FALSE,
51
-	pct_gp = gpar(), 
53
+	pct_gp = gpar(), pct_digits = 0,
52 54
 	axis_gp = gpar(fontsize = 8), 
53 55
 	show_row_barplot = TRUE, 
54 56
 	row_barplot_width = unit(2, "cm"),
... ...
@@ -64,6 +66,10 @@ oncoPrint = function(mat, get_type = function(x) x,
64 66
 			stop("`show_column_barplot` and `column_barplot_height` is deprecated, please configure `top_annotation` directly.")
65 67
 		}
66 68
 	}
69
+
70
+	if(!is.null(alter_fun_list)) {
71
+		warning("`alter_fun_list` is deprecated, please `alter_fun` instead.")
72
+	}
67 73
 	
68 74
 	# convert mat to mat_list
69 75
 	if(inherits(mat, "matrix")) {
... ...
@@ -103,19 +109,42 @@ oncoPrint = function(mat, get_type = function(x) x,
103 109
 		stop("Incorrect type of 'mat'")
104 110
 	}
105 111
 
106
-	if(missing(alter_fun_list) && missing(col)) {
112
+	if(missing(alter_fun) && missing(col)) {
107 113
 		if(length(mat_list) == 1) {
108
-			alter_fun_list = list(function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = "red", col = NA)))
114
+			af = function(x, y, w, h, v) {
115
+				grid.rect(x, y, w, h, gp = gpar(fill = "#CCCCCC", col = NA))
116
+				if(v[1]) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = "red", col = NA))
117
+			}
109 118
 			col = "red"
110 119
 		} else if(length(mat_list) == 2) {
111
-			alter_fun_list = list(
112
-		        function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = "red", col = NA)),
113
-		        function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = "blue", col = NA))
114
-		    )
120
+			af = function(x, y, w, h, v) {
121
+				grid.rect(x, y, w, h, gp = gpar(fill = "#CCCCCC", col = NA))
122
+				if(v[1]) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = "red", col = NA))
123
+		        if(v[2]) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = "blue", col = NA))
124
+		    }
115 125
 		    col = c("red", "blue")
126
+		} else {
127
+			stop("`alter_fun` should be specified.")
116 128
 		}
117
-		names(alter_fun_list) = names(mat_list)
118 129
 		names(col) = names(mat_list)
130
+	} else if(is.list(alter_fun)) {
131
+
132
+		# validate the list first
133
+		if(is.null(alter_fun$background)) alter_fun$background = function(x, y, w, h) grid.rect(x, y, w, h, gp = gpar(fill = "#CCCCCC", col = NA))
134
+		sdf = setdiff(all_type, names(alter_fun))
135
+		if(length(sdf) > 0) {
136
+			stop(paste0("You should define shape function for: ", paste(sdf, collapse = ", ")))
137
+		}
138
+
139
+		af = function(x, y, w, h, v) {
140
+			if(!is.null(alter_fun$background)) alter_fun$background(x, y, w, h)
141
+			alter_fun = alter_fun[names(alter_fun) != "background"]
142
+			for(nm in names(alter_fun)) {
143
+				if(v[nm]) alter_fun[[nm]](x, y, w, h)
144
+			}
145
+		}
146
+	} else {
147
+		af = alter_fun
119 148
 	}
120 149
 
121 150
 	# type as the third dimension
... ...
@@ -157,31 +186,15 @@ oncoPrint = function(mat, get_type = function(x) x,
157 186
 		column_order = structure(seq_len(sum(l)), names = which(l))[as.character(intersect(column_order, which(l)))]
158 187
 	}
159 188
 
160
-	# validate alter_fun_list
161
-	if(is.null(alter_fun_list$background)) alter_fun_list$background = function(x, y, w, h) grid.rect(x, y, w, h, gp = gpar(fill = "#CCCCCC", col = NA))
162
-	sdf = setdiff(all_type, names(alter_fun_list))
163
-	if(length(sdf) > 0) {
164
-		stop(paste0("You should define shape function for: ", paste(sdf, collapse = ", ")))
165
-	}
166
-
167
-	all_type = intersect(all_type, names(alter_fun_list))
168
-	all_type = setdiff(all_type, "background")
169
-
170
-	arr = arr[, , all_type, drop = FALSE]
171
-
172 189
 	# validate col
173 190
 	sdf = setdiff(all_type, names(col))
174 191
 	if(length(sdf) > 0) {
175 192
 		stop(paste0("You should define colors for:", paste(sdf, collapse = ", ")))
176 193
 	}
177 194
 
178
-	add_oncoprint = function(type, x, y, width, height) {
179
-		alter_fun_list[[type]](x, y, width, height)
180
-	}
181
-
182 195
 	# for each gene, percent of samples that have alterations
183 196
 	pct = rowSums(apply(arr, 1:2, any)) / ncol(mat_list[[1]])
184
-	pct = paste0(round(pct * 100), "%")
197
+	pct = paste0(round(pct * 100, digits = pct_digits), "%")
185 198
 	ha_pct = rowAnnotation(pct = row_anno_text(pct, just = "right", offset = unit(1, "npc"), gp = pct_gp), width = grobWidth(textGrob("100%", gp = pct_gp)))
186 199
 
187 200
 	#####################################################################
... ...
@@ -244,14 +257,17 @@ oncoPrint = function(mat, get_type = function(x) x,
244 257
 	dim(pheudo) = dim(arr)[1:2]
245 258
 	dimnames(pheudo) = dimnames(arr)[1:2]
246 259
 	
260
+	if(length(list(...))) {
261
+		if(names(list(...)) %in% c("rect_gp", "cluster_rows", "cluster_columns", "cell_fun")) {
262
+			stop("'rect_gp', 'cluster_rows', 'cluster_columns', 'cell_fun' are not allowed to use in `oncoPrint()`.")
263
+		}
264
+	}
265
+
247 266
 	ht = Heatmap(pheudo, col = col, rect_gp = gpar(type = "none"), 
248 267
 		cluster_rows = FALSE, cluster_columns = FALSE, row_order = row_order, column_order = column_order,
249 268
 		cell_fun = function(j, i, x, y, width, height, fill) {
250 269
 			z = arr[i, j, ]
251
-			add_oncoprint("background", x, y, width, height)
252
-			for(type in all_type[z]) {
253
-				add_oncoprint(type, x, y, width, height)
254
-			}
270
+			af(x, y, width, height, z)
255 271
 		}, show_column_names = show_column_names,
256 272
 		top_annotation = top_annotation,
257 273
 		heatmap_legend_param = heatmap_legend_param, ...)
... ...
@@ -21,10 +21,10 @@
21 21
 # # No example for this function
22 22
 # NULL
23 23
 # 
24
-selectArea = function(mark = FALSE) {
24
+selectArea = function(mark = TRUE) {
25 25
 
26 26
 	if(!interactive()) {
27
-		stop("`select()` can only be used under interactive mode.")
27
+		stop("`selectArea()` can only be used under interactive mode.")
28 28
 	}
29 29
 
30 30
 	x = dev.cur()
... ...
@@ -54,8 +54,8 @@ selectArea = function(mark = FALSE) {
54 54
 		pos1$y = tmp
55 55
 	}
56 56
 
57
-	grid.rect( (0.5*pos1$x + 0.5*pos2$x), (0.5*pos1$y + 0.5*pos2$y),
58
-		abs_width(pos2$x - pos1$x), abs_height(pos2$y - pos1$y), gp = gpar(col = "orange") )
57
+	# grid.rect( (0.5*pos1$x + 0.5*pos2$x), (0.5*pos1$y + 0.5*pos2$y),
58
+	# 	abs_width(pos2$x - pos1$x), abs_height(pos2$y - pos1$y), gp = gpar(col = "orange") )
59 59
 
60 60
 	# calcualte each heatmap's position under main_heatmap_list viewport
61 61
 	vp_cumsum = unit(0, "mm")
... ...
@@ -69,7 +69,7 @@ selectArea = function(mark = FALSE) {
69 69
 			seekViewport("main_heatmap_list")
70 70
 			pos1_cp = list()
71 71
 			pos2_cp = list()
72
-browser()
72
+
73 73
 			# relative to current heatmap body
74 74
 			pos1_cp$x = pos1$x - convertWidth(vp$x, "mm") - sum(component_width(ht, 1:3))
75 75
 			pos1_cp$y = pos1$y - convertHeight(vp$y, "mm") - sum(component_height(ht, 6:9))
... ...
@@ -8,7 +8,7 @@ Using boxplot as annotation
8 8
 }
9 9
 \usage{
10 10
 anno_boxplot(x, which = c("column", "row"), border = TRUE,
11
-    gp = gpar(fill = "#CCCCCC"), ylim = NULL,
11
+    gp = gpar(fill = "#CCCCCC"), ylim = NULL, outline = TRUE,
12 12
     pch = 16, size = unit(2, "mm"), axis = FALSE, axis_side = NULL,
13 13
     axis_gp = gpar(fontsize = 8), axis_direction = c("normal", "reverse"))
14 14
 }
... ...
@@ -19,6 +19,7 @@ anno_boxplot(x, which = c("column", "row"), border = TRUE,
19 19
   \item{border}{whether show border of the annotation compoment}
20 20
   \item{gp}{graphic parameters}
21 21
   \item{ylim}{data ranges.}
22
+  \item{outline}{whether draw outliers}
22 23
   \item{pch}{point type}
23 24
   \item{size}{point size}
24 25
   \item{axis}{whether add axis}
... ...
@@ -8,11 +8,11 @@ Make oncoPrint
8 8
 }
9 9
 \usage{
10 10
 oncoPrint(mat, get_type = function(x) x,
11
-    alter_fun_list, col,
11
+    alter_fun = alter_fun_list, alter_fun_list = NULL, col,
12 12
     row_order = oncoprint_row_order(),
13 13
     column_order = oncoprint_column_order(),
14 14
     show_column_names = FALSE,
15
-    pct_gp = gpar(),
15
+    pct_gp = gpar(), pct_digits = 0,
16 16
     axis_gp = gpar(fontsize = 8),
17 17
     show_row_barplot = TRUE,
18 18
     row_barplot_width = unit(2, "cm"),
... ...
@@ -27,12 +27,14 @@ oncoPrint(mat, get_type = function(x) x,
27 27
 
28 28
   \item{mat}{a character matrix which encodes mulitple alterations or a list of matrix for which every matrix contains binary value representing the alteration is present or absent. When it is a list, the names represent alteration types. You can use \code{\link{unify_mat_list}} to make all matrix having same row names and column names.}
29 29
   \item{get_type}{If different alterations are encoded in the matrix, this self-defined function determines how to extract them. Only work when \code{mat} is a matrix.}
30
-  \item{alter_fun_list}{a list of functions which define how to add graphics for different alterations. The names of the list should cover all alteration types.}
30
+  \item{alter_fun}{a single function or a list of functions which define how to add graphics for different alterations. If it is a list, the names of the list should cover all alteration types.}
31
+  \item{alter_fun_list}{deprecated, use \code{alter_run} instead.}
31 32
   \item{col}{a vector of color for which names correspond to alteration types.}
32 33
   \item{row_order}{order of genes. By default it is sorted by frequency of alterations decreasingly. Set it to \code{NULL} if you don't want to set the order}
33 34
   \item{column_order}{order of samples. By default the order is calculated by the 'memo sort' method which can visualize the mutual exclusivity across genes. Set it to \code{NULL} if you don't want to set the order}
34 35
   \item{show_column_names}{whether show column names}
35 36
   \item{pct_gp}{graphic paramters for percent row annotation}
37
+  \item{pct_digits}{digits for percent values}
36 38
   \item{axis_gp}{graphic paramters for axes}
37 39
   \item{show_row_barplot}{whether show barplot annotation on rows}
38 40
   \item{row_barplot_width}{width of barplot annotation on rows. It should be a \code{\link[grid]{unit}} object}
... ...
@@ -7,7 +7,7 @@ Select an area in the heatmap
7 7
 Select an area in the heatmap
8 8
 }
9 9
 \usage{
10
-selectArea(mark = FALSE)
10
+selectArea(mark = TRUE)
11 11
 }
12 12
 \arguments{
13 13
 
... ...
@@ -70,11 +70,12 @@ to alteration types. It is used to generate the barplots and the legends.
70 70
 
71 71
 ```{r}
72 72
 library(ComplexHeatmap)
73
+col = c(snv = "red", indel = "blue")
73 74
 oncoPrint(mat, get_type = function(x) strsplit(x, ";")[[1]],
74
-	alter_fun_list = list(
75
-		snv = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = "red", col = NA)),
76
-		indel = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = "blue", col = NA))
77
-	), col = c(snv = "red", indel = "blue"))
75
+	alter_fun = list(
76
+		snv = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = col["snv"], col = NA)),
77
+		indel = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = col["indel"], col = NA))
78
+	), col = col)
78 79
 ```
79 80
 
80 81
 The second type of input data is a list of matrix for which each matrix contains binary value representing
... ...
@@ -104,11 +105,11 @@ how to add graphics when there is no alteration and it is always put as the firs
104 105
 
105 106
 ```{r}
106 107
 oncoPrint(mat_list,
107
-	alter_fun_list = list(
108
+	alter_fun = list(
108 109
 		background = function(x, y, w, h) NULL,
109
-		snv = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = "red", col = NA)),
110
-		indel = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = "blue", col = NA))
111
-	), col = c(snv = "red", indel = "blue"))
110
+		snv = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = col["snv"], col = NA)),
111
+		indel = function(x, y, w, h) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = col["indel"], col = NA))
112
+	), col = col)
112 113
 ```
113 114
 
114 115
 If types of alterations is less than two and the purpose is only to have a quick look at the data, there are default
... ...
@@ -118,6 +119,31 @@ graphics added:
118 119
 oncoPrint(mat_list)
119 120
 ```
120 121
 
122
+In above examples, `alter_fun` is a list of functons which add graphics layer by layer. Graphics
123
+can also be added in a grid-by-grid style by specifying `alter_fun` as a single function. The difference
124
+from the function list is now `alter_fun` should accept a fifth argument which is a logical vector. 
125
+This logical vector shows whether different alterations exist for current gene in current sample.
126
+
127
+```{r}
128
+oncoPrint(mat_list,
129
+	alter_fun = function(x, y, w, h, v) {
130
+		if(v["snv"]) grid.rect(x, y, w*0.9, h*0.9, gp = gpar(fill = col["snv"], col = NA))
131
+		if(v["indel"]) grid.rect(x, y, w*0.9, h*0.4, gp = gpar(fill = col["indel"], col = NA))
132
+	}, col = col)
133
+```
134
+
135
+If `alter_fun` is set as a single function, customization can be more flexible. In following example,
136
+rectangles always fill the whole grid.
137
+
138
+```{r}
139
+oncoPrint(mat_list,
140
+    alter_fun = function(x, y, w, h, v) {
141
+		n = sum(v)
142
+		h = h*0.9
143
+		if(n) grid.rect(x, y - h*0.5 + 1:n/n*h, w*0.9, 1/n*h, gp = gpar(fill = col[which(v)], col = NA), just = "top")
144
+    }, col = col)
145
+```
146
+
121 147
 Now we make an oncoPrint with a real-world data. The data is retrieved from [cBioPortal](http://www.cbioportal.org/). 
122 148
 Steps for getting the data are as follows:
123 149
 
... ...
@@ -152,7 +178,7 @@ There are three different alterations in `mat`: `HOMDEL`, `AMP` and `MUT`. We fi
152 178
 define how to add graphics which correspond to different alterations. 
153 179
 
154 180
 ```{r}
155
-alter_fun_list = list(
181
+alter_fun = list(
156 182
 	background = function(x, y, w, h) {
157 183
 		grid.rect(x, y, w-unit(0.5, "mm"), h-unit(0.5, "mm"), gp = gpar(fill = "#CCCCCC", col = NA))
158 184
 	},
... ...
@@ -178,7 +204,7 @@ Make the oncoPrint and adjust heatmap components such as the title and the legen
178 204
 
179 205
 ```{r, fig.width = 12, fig.height = 8}
180 206
 oncoPrint(mat, get_type = function(x) strsplit(x, ";")[[1]],
181
-	alter_fun_list = alter_fun_list, col = col, 
207
+	alter_fun = alter_fun, col = col, 
182 208
 	column_title = "OncoPrint for TCGA Lung Adenocarcinoma, genes in Ras Raf MEK JNK signalling",
183 209
 	heatmap_legend_param = list(title = "Alternations", at = c("AMP", "HOMDEL", "MUT"), 
184 210
 		labels = c("Amplification", "Deep deletion", "Mutation")))
... ...
@@ -196,7 +222,7 @@ By default, if one sample has no alteration, it will still remain in the heatmap
196 222
 
197 223
 ```{r, fig.width = 12, fig.height = 8}
198 224
 oncoPrint(mat, get_type = function(x) strsplit(x, ";")[[1]],
199
-	alter_fun_list = alter_fun_list, col = col, 
225
+	alter_fun = alter_fun, col = col, 
200 226
 	remove_empty_columns = TRUE,
201 227
 	column_title = "OncoPrint for TCGA Lung Adenocarcinoma, genes in Ras Raf MEK JNK signalling",
202 228
 	heatmap_legend_param = list(title = "Alternations", at = c("AMP", "HOMDEL", "MUT"), 
... ...
@@ -211,7 +237,7 @@ You can see the difference for the sample order between 'memo sort' and the meth
211 237
 sample_order = scan(paste0(system.file("extdata", package = "ComplexHeatmap"), 
212 238
     "/sample_order.txt"), what = "character")
213 239
 oncoPrint(mat, get_type = function(x) strsplit(x, ";")[[1]],
214
-	alter_fun_list = alter_fun_list, col = col, 
240
+	alter_fun = alter_fun, col = col, 
215 241
 	row_order = NULL, column_order = sample_order,
216 242
 	remove_empty_columns = TRUE,
217 243
 	column_title = "OncoPrint for TCGA Lung Adenocarcinoma, genes in Ras Raf MEK JNK signalling",
... ...
@@ -226,7 +252,7 @@ Following example splits the heatmap into two halves and add a new heatmap to th
226 252
 
227 253
 ```{r, fig.width = 12, fig.height = 8}
228 254
 ht_list = oncoPrint(mat, get_type = function(x) strsplit(x, ";")[[1]],
229
-	alter_fun_list = alter_fun_list, col = col, 
255
+	alter_fun = alter_fun, col = col, 
230 256
 	remove_empty_columns = TRUE,
231 257
 	column_title = "OncoPrint for TCGA Lung Adenocarcinoma, genes in Ras Raf MEK JNK signalling",
232 258
 	heatmap_legend_param = list(title = "Alternations", at = c("AMP", "HOMDEL", "MUT"), 
... ...
@@ -240,7 +266,7 @@ In some scenarios, you don't want to show some of the alterations on the barplot
240 266
 
241 267
 `````{r, fig.width = 12, fig.height = 8}
242 268
 oncoPrint(mat, get_type = function(x) strsplit(x, ";")[[1]],
243
-	alter_fun_list = alter_fun_list, col = col, 
269
+	alter_fun = alter_fun, col = col, 
244 270
 	remove_empty_columns = TRUE,
245 271
 	column_title = "OncoPrint for TCGA Lung Adenocarcinoma, genes in Ras Raf MEK JNK signalling",
246 272
 	heatmap_legend_param = list(title = "Alternations", at = c("AMP", "HOMDEL", "MUT"),