Diff of /R/pheatmapwh.R [000000] .. [226bc8]

Switch to unified view

a b/R/pheatmapwh.R
1
## The code in this file is based on that in the file pheatmap.r in the CRAN package pheatmap by  Raivo Kolde <rkolde@gmail.com>,
2
##    with minor (but important to us) formatting modifications.
3
4
## These global parameters were introduced by Wolfgang Huber. In Raivo's code, they were hardcoded numbers
5
gaps_width <- c( col = 26, row = 10)
6
horizonal_annot_mat <- 0 # 8
7
8
lo = function(rown, coln, nrow, ncol, cellheight = NA, cellwidth = NA, treeheight_col, treeheight_row, legend, annotation_row, annotation_col, annotation_colors, annotation_legend, main, fontsize, fontsize_row, fontsize_col, gaps_row, gaps_col, ...){
9
    # Get height of colnames and length of rownames
10
    if(!is.null(coln[1])){
11
        t = c(coln, colnames(annotation_row))
12
        longest_coln = which.max(strwidth(t, units = 'in'))
13
        gp = list(fontsize = fontsize_col, ...)
14
        coln_height = unit(1, "grobheight", textGrob(t[longest_coln], rot = 90, gp = do.call(gpar, gp))) + unit(10, "bigpts")
15
    }
16
    else{
17
        coln_height = unit(5, "bigpts")
18
    }
19
    
20
    if(!is.null(rown[1])){
21
        t = c(rown, colnames(annotation_col))
22
        longest_rown = which.max(strwidth(t, units = 'in'))
23
        gp = list(fontsize = fontsize_row, ...)
24
        rown_width = unit(1, "grobwidth", textGrob(t[longest_rown], gp = do.call(gpar, gp))) ## + unit(10, "bigpts")
25
    }
26
    else{
27
        rown_width = unit(5, "bigpts")
28
    }
29
    
30
    gp = list(fontsize = fontsize, ...)
31
    # Legend position
32
    if(!is.na2(legend)){
33
        longest_break = which.max(nchar(names(legend)))
34
        longest_break = unit(1.1, "grobwidth", textGrob(as.character(names(legend))[longest_break], gp = do.call(gpar, gp)))
35
        title_length = unit(1.1, "grobwidth", textGrob("Scale", gp = gpar(fontface = "bold", ...)))
36
        legend_width = unit(12, "bigpts") + longest_break * 1.2
37
        legend_width = max(title_length, legend_width)
38
    }
39
    else{
40
        legend_width = unit(0, "bigpts")
41
    }
42
    
43
    # Set main title height
44
    if(is.na(main)){
45
        main_height = unit(0, "npc")
46
    }
47
    else{
48
        main_height = unit(1.5, "grobheight", textGrob(main, gp = gpar(fontsize = 1.3 * fontsize, ...)))
49
    }
50
    
51
    # Column annotations
52
    textheight = unit(fontsize, "bigpts")
53
    
54
    if(!is.na2(annotation_col)){
55
        # Column annotation height 
56
        annot_col_height = ncol(annotation_col) * (textheight + unit(2, "bigpts")) + unit(horizonal_annot_mat, "bigpts")
57
        
58
        # Width of the correponding legend
59
        t = c(as.vector(as.matrix(annotation_col)), colnames(annotation_col)) 
60
        annot_col_legend_width = unit(1.2, "grobwidth", textGrob(t[which.max(nchar(t))], gp = gpar(...))) + unit(12, "bigpts")
61
        if(!annotation_legend){
62
            annot_col_legend_width = unit(0, "npc")
63
        }
64
    }
65
    else{
66
        annot_col_height = unit(0, "bigpts")
67
        annot_col_legend_width = unit(0, "bigpts")
68
    }
69
    
70
    # Row annotations
71
    if(!is.na2(annotation_row)){
72
        # Row annotation width 
73
        annot_row_width = ncol(annotation_row) * (textheight + unit(2, "bigpts")) + unit(2, "bigpts")
74
        
75
        # Width of the correponding legend
76
        t = c(as.vector(as.matrix(annotation_row)), colnames(annotation_row)) 
77
        annot_row_legend_width = unit(1.2, "grobwidth", textGrob(t[which.max(nchar(t))], gp = gpar(...))) + unit(12, "bigpts")
78
        if(!annotation_legend){
79
            annot_row_legend_width = unit(0, "npc")
80
        }
81
    }
82
    else{
83
        annot_row_width = unit(0, "bigpts")
84
        annot_row_legend_width = unit(0, "bigpts")
85
    }
86
    
87
    annot_legend_width = max(annot_row_legend_width, annot_col_legend_width)
88
    
89
    # Tree height
90
    treeheight_col = unit(treeheight_col, "bigpts") + unit(5, "bigpts")
91
    treeheight_row = unit(treeheight_row, "bigpts") + unit(5, "bigpts") 
92
    
93
    # Set cell sizes
94
    mat_width = if(is.na(cellwidth)){
95
        unit(1, "npc") - rown_width - legend_width - treeheight_row - annot_row_width - annot_legend_width 
96
    } else{
97
        unit(cellwidth * ncol, "bigpts") + length(gaps_col) * unit(gaps_width["col"], "bigpts")
98
    }
99
    
100
    mat_height = if(is.na(cellheight)){
101
        unit(1, "npc") - main_height - coln_height - treeheight_col - annot_col_height
102
    } else{
103
        unit(cellheight * nrow, "bigpts") + length(gaps_row) * unit(gaps_width["row"], "bigpts")
104
    }    
105
    
106
    # Produce gtable
107
    gt = gtable(widths = unit.c(             treeheight_row, annot_row_width,   mat_width, rown_width, legend_width, annot_legend_width),
108
               heights = unit.c(main_height, treeheight_col, annot_col_height, mat_height, coln_height), vp = viewport(gp = do.call(gpar, gp)))
109
    
110
    cw = convertWidth( mat_width  - (length(gaps_col) * unit(gaps_width["col"], "bigpts")), "bigpts", valueOnly = TRUE) / ncol
111
    ch = convertHeight(mat_height - (length(gaps_row) * unit(gaps_width["row"], "bigpts")), "bigpts", valueOnly = TRUE) / nrow
112
    
113
    # Return minimal cell dimension in bigpts to decide if borders are drawn
114
    mindim = min(cw, ch) 
115
    
116
    res = list(gt = gt, mindim = mindim)
117
    
118
    return(res)
119
}
120
121
find_coordinates = function(n, gaps, m = 1:n, what) {
122
    if (length(gaps) == 0)
123
        return(list(coord = unit(m / n, "npc"), size = unit(1 / n, "npc") ))
124
125
    if(max(gaps) > n)
126
        stop("Gaps do not match with matrix size")
127
128
    size = (1 / n) * (unit(1, "npc") - length(gaps) * unit(gaps_width[what], "bigpts"))
129
    
130
    gaps2 = apply(sapply(gaps, function(gap, x){x > gap}, m), 1, sum) 
131
    coord = m * size + (gaps2 * unit(gaps_width[what], "bigpts"))
132
    
133
    return(list(coord = coord, size = size))
134
}
135
136
draw_dendrogram = function(hc, gaps, horizontal = TRUE){
137
    h = hc$height / max(hc$height) / 1.05
138
    m = hc$merge
139
    o = hc$order
140
    n = length(o)
141
142
    m[m > 0] = n + m[m > 0] 
143
    m[m < 0] = abs(m[m < 0])
144
145
    dist = matrix(0, nrow = 2 * n - 1, ncol = 2, dimnames = list(NULL, c("x", "y"))) 
146
    dist[1:n, 1] = 1 / n / 2 + (1 / n) * (match(1:n, o) - 1)
147
148
    for(i in 1:nrow(m)){
149
        dist[n + i, 1] = (dist[m[i, 1], 1] + dist[m[i, 2], 1]) / 2
150
        dist[n + i, 2] = h[i]
151
    }
152
    
153
    draw_connection = function(x1, x2, y1, y2, y){
154
        res = list(
155
            x = c(x1, x1, x2, x2),
156
            y = c(y1, y, y, y2)
157
        )
158
        
159
        return(res)
160
    }
161
    
162
    x = rep(NA, nrow(m) * 4)
163
    y = rep(NA, nrow(m) * 4)
164
    id = rep(1:nrow(m), rep(4, nrow(m)))
165
    
166
    for(i in 1:nrow(m)){
167
        c = draw_connection(dist[m[i, 1], 1], dist[m[i, 2], 1], dist[m[i, 1], 2], dist[m[i, 2], 2], h[i])
168
        k = (i - 1) * 4 + 1
169
        x[k : (k + 3)] = c$x
170
        y[k : (k + 3)] = c$y
171
    }
172
    
173
    x = find_coordinates(n, gaps, x * n, what = ifelse(horizontal, "col", "row"))$coord
174
    y = unit(y, "npc")
175
    
176
    if(!horizontal){
177
        a = x
178
        x = unit(1, "npc") - y
179
        y = unit(1, "npc") - a
180
    }
181
    res = polylineGrob(x = x, y = y, id = id)
182
    
183
    return(res)
184
}
185
186
draw_matrix = function(matrix, border_color, gaps_rows, gaps_cols, fmat, fontsize_number, number_color){
187
    n = nrow(matrix)
188
    m = ncol(matrix)
189
    
190
    coord_x = find_coordinates(m, gaps_cols, what = "col")
191
    coord_y = find_coordinates(n, gaps_rows, what = "row")
192
    
193
    x = coord_x$coord - 0.5 * coord_x$size
194
    y = unit(1, "npc") - (coord_y$coord - 0.5 * coord_y$size)
195
    
196
    coord = expand.grid(y = y, x = x)
197
    
198
    res = gList()
199
    
200
    res[["rect"]] = rectGrob(x = coord$x, y = coord$y, width = coord_x$size, height = coord_y$size, gp = gpar(fill = matrix, col = border_color))
201
    
202
    if(attr(fmat, "draw")){
203
        res[["text"]] = textGrob(x = coord$x, y = coord$y, label = fmat, gp = gpar(col = number_color, fontsize = fontsize_number))
204
    }
205
    
206
    res = gTree(children = res)
207
    
208
    return(res)
209
}
210
211
draw_colnames = function(coln, gaps, ...){
212
    coord = find_coordinates(length(coln), gaps, what = "col")
213
    x = coord$coord - 0.5 * coord$size
214
    
215
    res = textGrob(coln, x = x, y = unit(1, "npc") - unit(3, "bigpts"), vjust = 0.5, hjust = 0, rot = 270, gp = gpar(...))
216
    
217
    return(res)
218
}
219
220
draw_rownames = function(rown, gaps, ...){
221
    coord = find_coordinates(length(rown), gaps, what = "row")
222
    y = unit(1, "npc") - (coord$coord - 0.5 * coord$size)
223
    
224
    res = textGrob(rown, x = unit(3, "bigpts"), y = y, vjust = 0.5, hjust = 0, gp = gpar(...))
225
    
226
    return(res)
227
}
228
229
draw_legend = function(color, breaks, legend, ...){
230
    height = min(unit(1, "npc"), unit(150, "bigpts"))
231
    
232
    legend_pos = (legend - min(breaks)) / (max(breaks) - min(breaks))
233
    legend_pos = height * legend_pos + (unit(1, "npc") - height)
234
    
235
    breaks = (breaks - min(breaks)) / (max(breaks) - min(breaks))
236
    breaks = height * breaks + (unit(1, "npc") - height)
237
    
238
    h = breaks[-1] - breaks[-length(breaks)]
239
    
240
    rect = rectGrob(x = 0, y = breaks[-length(breaks)], width = unit(10, "bigpts"), height = h, hjust = 0, vjust = 0, gp = gpar(fill = color, col = "#FFFFFF00"))
241
    text = textGrob(names(legend), x = unit(14, "bigpts"), y = legend_pos, hjust = 0, gp = gpar(...))
242
    
243
    res = grobTree(rect, text)
244
    
245
    return(res)
246
}
247
248
convert_annotations = function(annotation, annotation_colors){
249
    new = annotation
250
    for(i in seq_len(ncol(annotation))) {
251
        a = annotation[, i]
252
        b = annotation_colors[[colnames(annotation)[i]]]
253
        if (is.character(a) || is.factor(a)){
254
            a = as.character(a)
255
            
256
            if(length(setdiff(setdiff(a, NA), names(b))) > 0){
257
                stop(sprintf("Factor levels on variable %s do not match with annotation_colors", colnames(annotation)[i]))
258
            }
259
            new[, i] = b[a]
260
        }
261
        else{
262
            a = cut(a, breaks = 100)
263
            new[, i] = colorRampPalette(b)(100)[a]
264
        }
265
    }
266
    return(as.matrix(new))
267
}
268
269
draw_annotations = function(converted_annotations, border_color, gaps, fontsize, horizontal){
270
    n = ncol(converted_annotations)
271
    m = nrow(converted_annotations)
272
    
273
    coord_x = find_coordinates(m, gaps, what = "col")
274
    
275
    x = coord_x$coord - 0.5 * coord_x$size
276
    
277
    # y = cumsum(rep(fontsize, n)) - 4 + cumsum(rep(2, n))
278
    y = cumsum(rep(fontsize, n)) + cumsum(rep(2, n)) - fontsize / 2 + 1 
279
    y = unit(y, "bigpts")
280
    
281
    if (horizontal) {
282
        coord = expand.grid(x = x, y = y)
283
        res = rectGrob(x = coord$x, y = coord$y, width = coord_x$size, height = unit(fontsize, "bigpts"), gp = gpar(fill = converted_annotations, col = border_color))
284
    } else{
285
        a = x
286
        x = unit(1, "npc") - y
287
        y = unit(1, "npc") - a
288
        
289
        coord = expand.grid(y = y, x = x)
290
        res = rectGrob(x = coord$x, y = coord$y, width = unit(fontsize, "bigpts"), height = coord_x$size, gp = gpar(fill = converted_annotations, col = border_color))
291
    }
292
    
293
    return(res)
294
}
295
296
draw_annotation_names = function(annotations, fontsize, horizontal){
297
    n = ncol(annotations)
298
    
299
    x = unit(3, "bigpts")
300
    
301
    y = cumsum(rep(fontsize, n)) + cumsum(rep(2, n)) - fontsize / 2 + 1 
302
    y = unit(y, "bigpts")
303
    
304
    if(horizontal){
305
        res = textGrob(colnames(annotations), x = x, y = y, hjust = 0, gp = gpar(fontsize = fontsize, fontface = 2))
306
    }
307
    else{
308
        a = x
309
        x = unit(1, "npc") - y
310
        y = unit(1, "npc") - a
311
        
312
        res = textGrob(colnames(annotations), x = x, y = y, vjust = 0.5, hjust = 0, rot = 270, gp = gpar(fontsize = fontsize, fontface = 2))
313
    }
314
    
315
    return(res)
316
}
317
318
draw_annotation_legend = function(annotation, annotation_colors, border_color, ...){
319
    y = unit(1, "npc")
320
    text_height = unit(1, "grobheight", textGrob("FGH", gp = gpar(...)))
321
    
322
    res = gList()
323
324
    ## by WH: remove those with leading blank, or "n.d."
325
    columnsToDraw <- names(annotation)
326
    columnsToDraw <- columnsToDraw[ !grepl("^ ", columnsToDraw) ]
327
    
328
    for(i in columnsToDraw) {
329
        res[[i]] = textGrob(i, x = 0, y = y, vjust = 1, hjust = 0, gp = gpar(fontface = "bold", ...))
330
        
331
        y = y - 1.5 * text_height
332
        if(is.character(annotation[[i]]) | is.factor(annotation[[i]])){
333
            aci <- annotation_colors[[i]]
334
            aci <- aci[ names(aci) != "n.d." ]
335
            n <- length(aci)
336
            yy <- y - (seq_len(n) - 1) * 2 * text_height
337
            
338
            res[[paste(i, "r")]] = rectGrob(x = unit(0, "npc"), y = yy, hjust = 0, vjust = 1, height = 2 * text_height, width = 2 * text_height, gp = gpar(col = border_color, fill = aci))
339
            res[[paste(i, "t")]] = textGrob(names(aci), x = text_height * 2.4, y = yy - text_height, hjust = 0, vjust = 0.5, gp = gpar(...))
340
            
341
            y = y - n * 2 * text_height
342
            
343
        }
344
        else{
345
            yy = y - 8 * text_height + seq(0, 1, 0.25)[-1] * 8 * text_height
346
            h = 8 * text_height * 0.25
347
            
348
            res[[paste(i, "r")]] = rectGrob(x = unit(0, "npc"), y = yy, hjust = 0, vjust = 1, height = h, width = 2 * text_height, gp = gpar(col = NA, fill = colorRampPalette(annotation_colors[[i]])(4)))
349
            res[[paste(i, "r2")]] = rectGrob(x = unit(0, "npc"), y = y, hjust = 0, vjust = 1, height = 8 * text_height, width = 2 * text_height, gp = gpar(col = border_color, fill = NA))
350
            
351
            txt = rev(range(grid.pretty(range(annotation[[i]], na.rm = TRUE))))
352
            yy = y - c(1, 7) * text_height
353
            res[[paste(i, "t")]]  = textGrob(txt, x = text_height * 2.4, y = yy, hjust = 0, vjust = 0.5, gp = gpar(...))
354
            y = y - 8 * text_height
355
        }
356
        y = y - 1.5 * text_height
357
    }
358
    
359
    res = gTree(children = res)
360
    
361
    return(res)
362
}
363
364
draw_main = function(text, ...){
365
    res = textGrob(text, gp = gpar(fontface = "bold", ...))
366
    
367
    return(res)
368
}
369
370
vplayout = function(x, y){
371
    return(viewport(layout.pos.row = x, layout.pos.col = y))
372
}
373
374
heatmap_motor = function(matrix, border_color, cellwidth, cellheight, tree_col, tree_row, treeheight_col, treeheight_row, filename, width, height, breaks, color, legend, annotation_row, annotation_col, annotation_colors, annotation_legend, main, fontsize, fontsize_row, fontsize_col, fmat, fontsize_number, number_color, gaps_col, gaps_row, labels_row, labels_col, ...){
375
    # Set layout
376
    lo = lo(coln = labels_col, rown = labels_row, nrow = nrow(matrix), ncol = ncol(matrix), cellwidth = cellwidth, cellheight = cellheight, treeheight_col = treeheight_col, treeheight_row = treeheight_row, legend = legend, annotation_col = annotation_col, annotation_row = annotation_row, annotation_colors = annotation_colors, annotation_legend = annotation_legend, main = main, fontsize = fontsize, fontsize_row = fontsize_row, fontsize_col = fontsize_col, gaps_row = gaps_row, gaps_col = gaps_col,  ...)
377
    
378
    res = lo$gt
379
    mindim = lo$mindim
380
    
381
    if(!is.na(filename)){
382
        if(is.na(height)){
383
            height = convertHeight(gtable_height(res), "inches", valueOnly = T)
384
        }
385
        if(is.na(width)){
386
            width = convertWidth(gtable_width(res), "inches", valueOnly = T)
387
        }
388
        
389
        # Get file type
390
        r = regexpr("\\.[a-zA-Z]*$", filename)
391
        if(r == -1) stop("Improper filename")
392
        ending = substr(filename, r + 1, r + attr(r, "match.length"))
393
394
        f = switch(ending,
395
            pdf = function(x, ...) pdf(x, ...),
396
            png = function(x, ...) png(x, units = "in", res = 300, ...),
397
            jpeg = function(x, ...) jpeg(x, units = "in", res = 300, ...),
398
            jpg = function(x, ...) jpeg(x, units = "in", res = 300, ...),
399
            tiff = function(x, ...) tiff(x, units = "in", res = 300, compression = "lzw", ...),
400
            bmp = function(x, ...) bmp(x, units = "in", res = 300, ...),
401
            stop("File type should be: pdf, png, bmp, jpg, tiff")
402
        )
403
        
404
        # print(sprintf("height:%f width:%f", height, width))
405
        
406
        # gt = heatmap_motor(matrix, cellwidth = cellwidth, cellheight = cellheight, border_color = border_color, tree_col = tree_col, tree_row = tree_row, treeheight_col = treeheight_col, treeheight_row = treeheight_row, breaks = breaks, color = color, legend = legend, annotation_col = annotation_col, annotation_row = annotation_row, annotation_colors = annotation_colors, annotation_legend = annotation_legend, filename = NA, main = main, fontsize = fontsize, fontsize_row = fontsize_row, fontsize_col = fontsize_col, fmat = fmat, fontsize_number =  fontsize_number, number_color = number_color, labels_row = labels_row, labels_col = labels_col, gaps_col = gaps_col, gaps_row = gaps_row, ...)
407
408
        f(filename, height = height, width = width)
409
        gt = heatmap_motor(matrix, cellwidth = cellwidth, cellheight = cellheight, border_color = border_color, tree_col = tree_col, tree_row = tree_row, treeheight_col = treeheight_col, treeheight_row = treeheight_row, breaks = breaks, color = color, legend = legend, annotation_col = annotation_col, annotation_row = annotation_row, annotation_colors = annotation_colors, annotation_legend = annotation_legend, filename = NA, main = main, fontsize = fontsize, fontsize_row = fontsize_row, fontsize_col = fontsize_col, fmat = fmat, fontsize_number =  fontsize_number, number_color = number_color, labels_row = labels_row, labels_col = labels_col, gaps_col = gaps_col, gaps_row = gaps_row, ...)
410
        grid.draw(gt)
411
        dev.off()
412
        
413
        return(gt)
414
    }
415
    
416
    # Omit border color if cell size is too small 
417
    if(mindim < 3) border_color = NA
418
    
419
    # Draw title
420
    if(!is.na(main)){
421
        elem = draw_main(main, fontsize = 1.3 * fontsize, ...)
422
        res = gtable_add_grob(res, elem, t = 1, l = 3, name = "main")
423
    }
424
    
425
    # Draw tree for the columns
426
    if(!is.na2(tree_col) & treeheight_col != 0){
427
        elem = draw_dendrogram(tree_col, gaps_col, horizontal = T)
428
        res = gtable_add_grob(res, elem, t = 2, l = 3, name = "col_tree")
429
    }
430
    
431
    # Draw tree for the rows
432
    if(!is.na2(tree_row) & treeheight_row != 0){
433
        elem = draw_dendrogram(tree_row, gaps_row, horizontal = F)
434
        res = gtable_add_grob(res, elem, t = 4, l = 1, name = "row_tree")
435
    }
436
    
437
    # Draw matrix
438
    elem = draw_matrix(matrix, border_color, gaps_row, gaps_col, fmat, fontsize_number, number_color)
439
    res = gtable_add_grob(res, elem, t = 4, l = 3, clip = "off", name = "matrix")
440
    
441
    # Draw colnames
442
    if(length(labels_col) != 0){
443
        pars = list(labels_col, gaps = gaps_col, fontsize = fontsize_col, ...)
444
        elem = do.call(draw_colnames, pars)
445
        res = gtable_add_grob(res, elem, t = 5, l = 3, clip = "off", name = "col_names")
446
    }
447
    
448
    # Draw rownames
449
    if(length(labels_row) != 0){
450
        pars = list(labels_row, gaps = gaps_row, fontsize = fontsize_row, ...)
451
        elem = do.call(draw_rownames, pars)
452
        res = gtable_add_grob(res, elem, t = 4, l = 4, clip = "off", name = "row_names")
453
    }
454
    
455
    # Draw annotation tracks on cols
456
    if(!is.na2(annotation_col)){
457
        # Draw tracks
458
        converted_annotation = convert_annotations(annotation_col, annotation_colors)
459
        elem = draw_annotations(converted_annotation, border_color, gaps_col, fontsize, horizontal = T)
460
        res = gtable_add_grob(res, elem, t = 3, l = 3, clip = "off", name = "col_annotation")
461
        
462
        # Draw names
463
        elem = draw_annotation_names(annotation_col, fontsize, horizontal = T)
464
        res = gtable_add_grob(res, elem, t = 3, l = 4, clip = "off", name = "row_annotation_names")
465
        
466
    }
467
    
468
    # Draw annotation tracks on rows
469
    if(!is.na2(annotation_row)){
470
        # Draw tracks
471
        converted_annotation = convert_annotations(annotation_row, annotation_colors)
472
        elem = draw_annotations(converted_annotation, border_color, gaps_row, fontsize, horizontal = F)
473
        res = gtable_add_grob(res, elem, t = 4, l = 2, clip = "off", name = "row_annotation")
474
        
475
        # Draw names
476
        if(length(labels_col) != 0){
477
            elem = draw_annotation_names(annotation_row, fontsize, horizontal = F)
478
            res = gtable_add_grob(res, elem, t = 5, l = 2, clip = "off", name = "row_annotation_names")
479
        }
480
    }
481
    
482
    # Draw annotation legend
483
    annotation = c(annotation_col[length(annotation_col):1], annotation_row[length(annotation_row):1])
484
    annotation = annotation[unlist(lapply(annotation, function(x) !is.na2(x)))]
485
    
486
    if(length(annotation) > 0 & annotation_legend){
487
        elem = draw_annotation_legend(annotation, annotation_colors, border_color, fontsize = fontsize, ...)
488
        
489
        t = ifelse(is.null(labels_row), 4, 3)
490
        res = gtable_add_grob(res, elem, t = t, l = 6, b = 5, clip = "off", name = "annotation_legend")
491
    }
492
    
493
    # Draw legend
494
    if(!is.na2(legend)){
495
        elem = draw_legend(color, breaks, legend, fontsize = fontsize, ...)
496
        
497
        t = ifelse(is.null(labels_row), 4, 3)
498
        res = gtable_add_grob(res, elem, t = t, l = 5, b = 5, clip = "off", name = "legend")
499
    }
500
    
501
    return(res)
502
}
503
504
generate_breaks = function(x, n, center = F){
505
    if(center){
506
        m = max(abs(c(min(x, na.rm = T), max(x, na.rm = T))))
507
        res = seq(-m, m, length.out = n + 1)
508
    }
509
    else{
510
        res = seq(min(x, na.rm = T), max(x, na.rm = T), length.out = n + 1)
511
    }
512
    
513
    return(res)
514
}
515
516
scale_vec_colours = function(x, col = rainbow(10), breaks = NA){
517
    return(col[as.numeric(cut(x, breaks = breaks, include.lowest = T))])
518
}
519
520
scale_colours = function(mat, col = rainbow(10), breaks = NA){
521
    mat = as.matrix(mat)
522
    return(matrix(scale_vec_colours(as.vector(mat), col = col, breaks = breaks), nrow(mat), ncol(mat), dimnames = list(rownames(mat), colnames(mat))))
523
}
524
525
cluster_mat = function(mat, distance, method){
526
    if(!(method %in% c("ward.D", "ward.D2", "ward", "single", "complete", "average", "mcquitty", "median", "centroid"))){
527
        stop("clustering method has to one form the list: 'ward', 'ward.D', 'ward.D2', 'single', 'complete', 'average', 'mcquitty', 'median' or 'centroid'.")
528
    }
529
    if(!(distance[1] %in% c("correlation", "euclidean", "maximum", "manhattan", "canberra", "binary", "minkowski")) & class(distance) != "dist"){
530
        stop("distance has to be a dissimilarity structure as produced by dist or one measure  form the list: 'correlation', 'euclidean', 'maximum', 'manhattan', 'canberra', 'binary', 'minkowski'")
531
    }
532
    if(distance[1] == "correlation"){
533
        d = as.dist(1 - cor(t(mat)))
534
    }
535
    else{
536
        if(class(distance) == "dist"){
537
            d = distance
538
        }
539
        else{
540
            d = dist(mat, method = distance)
541
        }
542
    }
543
    
544
    return(hclust(d, method = method))
545
}
546
547
scale_rows = function(x){
548
    m = apply(x, 1, mean, na.rm = T)
549
    s = apply(x, 1, sd, na.rm = T)
550
    return((x - m) / s)
551
}
552
553
scale_mat = function(mat, scale){
554
    if(!(scale %in% c("none", "row", "column"))){
555
        stop("scale argument shoud take values: 'none', 'row' or 'column'")
556
    }
557
    mat = switch(scale, none = mat, row = scale_rows(mat), column = t(scale_rows(t(mat))))
558
    return(mat)
559
}
560
561
generate_annotation_colours = function(annotation, annotation_colors, drop){
562
    if(is.na2(annotation_colors)){
563
        annotation_colors = list()
564
    }
565
    count = 0
566
    for(i in 1:length(annotation)){
567
        annotation[[i]] = annotation[[i]][!is.na(annotation[[i]])]
568
        if(is.character(annotation[[i]]) | is.factor(annotation[[i]])){
569
            if (is.factor(annotation[[i]]) & !drop){
570
                count = count + length(levels(annotation[[i]]))
571
            }
572
            else{
573
                count = count + length(unique(annotation[[i]]))
574
            }
575
        }
576
    }
577
    
578
    factor_colors = dscale(factor(1:count), hue_pal(l = 75))
579
    
580
    set.seed(3453)
581
    
582
    cont_counter = 2
583
    for(i in 1:length(annotation)){
584
        if(!(names(annotation)[i] %in% names(annotation_colors))){
585
            if(is.character(annotation[[i]]) | is.factor(annotation[[i]])){
586
                n = length(unique(annotation[[i]]))
587
                if (is.factor(annotation[[i]]) & !drop){
588
                    n = length(levels(annotation[[i]]))
589
                }
590
                ind = sample(1:length(factor_colors), n)
591
                annotation_colors[[names(annotation)[i]]] = factor_colors[ind]
592
                l = levels(as.factor(annotation[[i]]))
593
                l = l[l %in% unique(annotation[[i]])]
594
                if (is.factor(annotation[[i]]) & !drop){
595
                    l = levels(annotation[[i]])
596
                }
597
                
598
                names(annotation_colors[[names(annotation)[i]]]) = l
599
                factor_colors = factor_colors[-ind]
600
            }
601
            else{
602
                annotation_colors[[names(annotation)[i]]] = brewer_pal("seq", cont_counter)(5)[1:4]
603
                cont_counter = cont_counter + 1
604
            }
605
        }
606
    }
607
    return(annotation_colors)
608
}
609
610
kmeans_pheatmap = function(mat, k = min(nrow(mat), 150), sd_limit = NA, ...){
611
    # Filter data
612
    if(!is.na(sd_limit)){
613
        s = apply(mat, 1, sd)
614
        mat = mat[s > sd_limit, ]    
615
    }
616
    
617
    # Cluster data
618
    set.seed(1245678)
619
    km = kmeans(mat, k, iter.max = 100)
620
    mat2 = km$centers
621
    
622
    # Compose rownames
623
    t = table(km$cluster)
624
    rownames(mat2) = sprintf("cl%s_size_%d", names(t), t)
625
    
626
    # Draw heatmap
627
    pheatmapwh(mat2, ...)
628
}
629
630
find_gaps = function(tree, cutree_n){
631
    v = cutree(tree, cutree_n)[tree$order]
632
    gaps = which((v[-1] - v[-length(v)]) != 0)
633
    
634
}
635
636
is.na2 = function(x){
637
    if(is.list(x) | length(x) > 1){
638
        return(FALSE)
639
    }
640
    if(length(x) == 0){
641
        return(TRUE)
642
    }
643
    
644
    return(is.na(x))
645
}
646
647
identity2 = function(x, ...){
648
    return(x)
649
}
650
651
pheatmapwh = function(mat, color = colorRampPalette(rev(brewer.pal(n = 7, name = "RdYlBu")))(100), kmeans_k = NA, breaks = NA, border_color = "grey60", cellwidth = NA, cellheight = NA, scale = "none", cluster_rows = TRUE, cluster_cols = TRUE, clustering_distance_rows = "euclidean", clustering_distance_cols = "euclidean", clustering_method = "complete", clustering_callback = identity2, cutree_rows = NA, cutree_cols = NA,  treeheight_row = ifelse(cluster_rows, 50, 0), treeheight_col = ifelse(cluster_cols, 50, 0), legend = TRUE, legend_breaks = NA, legend_labels = NA, annotation_row = NA, annotation_col = NA, annotation = NA, annotation_colors = NA, annotation_legend = TRUE, drop_levels = TRUE, show_rownames = T, show_colnames = T, main = NA, fontsize = 10, fontsize_row = fontsize, fontsize_col = fontsize, display_numbers = F, number_format = "%.2f", number_color = "grey30", fontsize_number = 0.8 * fontsize, gaps_row = NULL, gaps_col = NULL, labels_row = NULL, labels_col = NULL, filename = NA, width = NA, height = NA, silent = FALSE, ...){
652
    
653
    # Set labels
654
    if(is.null(labels_row)){
655
        labels_row = rownames(mat)
656
    }
657
    if(is.null(labels_col)){
658
        labels_col = colnames(mat)
659
    }
660
    
661
    # Preprocess matrix
662
    mat = as.matrix(mat)
663
    if(scale != "none"){
664
        mat = scale_mat(mat, scale)
665
        if(is.na2(breaks)){
666
            breaks = generate_breaks(mat, length(color), center = T)
667
        }
668
    }
669
    
670
    
671
    # Kmeans
672
    if(!is.na(kmeans_k)){
673
        # Cluster data
674
        km = kmeans(mat, kmeans_k, iter.max = 100)
675
        mat = km$centers
676
        
677
        # Compose rownames
678
        t = table(km$cluster)
679
        labels_row = sprintf("Cluster: %s Size: %d", names(t), t)
680
    }
681
    else{
682
        km = NA
683
    }
684
    
685
    # Format numbers to be displayed in cells
686
    if(is.matrix(display_numbers) | is.data.frame(display_numbers)){
687
        if(nrow(display_numbers) != nrow(mat) | ncol(display_numbers) != ncol(mat)){
688
            stop("If display_numbers provided as matrix, its dimensions have to match with mat")
689
        }
690
        
691
        display_numbers = as.matrix(display_numbers)
692
        fmat = matrix(as.character(display_numbers), nrow = nrow(display_numbers), ncol = ncol(display_numbers))
693
        fmat_draw = TRUE
694
        
695
    }
696
    else{
697
        if(display_numbers){
698
            fmat = matrix(sprintf(number_format, mat), nrow = nrow(mat), ncol = ncol(mat))
699
            fmat_draw = TRUE
700
        }
701
        else{
702
            fmat = matrix(NA, nrow = nrow(mat), ncol = ncol(mat))
703
            fmat_draw = FALSE
704
        }
705
    }
706
    
707
    # Do clustering
708
    if(cluster_rows){
709
        tree_row = cluster_mat(mat, distance = clustering_distance_rows, method = clustering_method)
710
        tree_row = clustering_callback(tree_row, mat)
711
        mat = mat[tree_row$order, , drop = FALSE]
712
        fmat = fmat[tree_row$order, , drop = FALSE]
713
        labels_row = labels_row[tree_row$order]
714
        if(!is.na(cutree_rows)){
715
            gaps_row = find_gaps(tree_row, cutree_rows)
716
        }
717
        else{
718
            gaps_row = NULL
719
        }
720
    }
721
    else{
722
        tree_row = NA
723
        treeheight_row = 0
724
    }
725
    
726
    if(cluster_cols){
727
        tree_col = cluster_mat(t(mat), distance = clustering_distance_cols, method = clustering_method)
728
        tree_col = clustering_callback(tree_col, t(mat))
729
        mat = mat[, tree_col$order, drop = FALSE]
730
        fmat = fmat[, tree_col$order, drop = FALSE]
731
        labels_col = labels_col[tree_col$order]
732
        if(!is.na(cutree_cols)){
733
            gaps_col = find_gaps(tree_col, cutree_cols)
734
        }
735
        else{
736
            gaps_col = NULL
737
        }
738
    }
739
    else{
740
        tree_col = NA
741
        treeheight_col = 0
742
    }
743
    
744
    attr(fmat, "draw") = fmat_draw
745
    
746
    # Colors and scales
747
    if(!is.na2(legend_breaks) & !is.na2(legend_labels)){
748
        if(length(legend_breaks) != length(legend_labels)){
749
            stop("Lengths of legend_breaks and legend_labels must be the same")
750
        }
751
    }
752
    
753
    
754
    if(is.na2(breaks)){
755
        breaks = generate_breaks(as.vector(mat), length(color))
756
    }
757
    if (legend & is.na2(legend_breaks)) {
758
        legend = grid.pretty(range(as.vector(breaks)))
759
        names(legend) = legend
760
    }
761
    else if(legend & !is.na2(legend_breaks)){
762
        legend = legend_breaks[legend_breaks >= min(breaks) & legend_breaks <= max(breaks)]
763
        
764
        if(!is.na2(legend_labels)){
765
            legend_labels = legend_labels[legend_breaks >= min(breaks) & legend_breaks <= max(breaks)]
766
            names(legend) = legend_labels
767
        }
768
        else{
769
            names(legend) = legend
770
        }
771
    }
772
    else {
773
        legend = NA
774
    }
775
    mat = scale_colours(mat, col = color, breaks = breaks)
776
    
777
    # Preparing annotations
778
    if(is.na2(annotation_col) & !is.na2(annotation)){
779
        annotation_col = annotation
780
    }
781
    # Select only the ones present in the matrix
782
    if(!is.na2(annotation_col)){
783
        annotation_col = annotation_col[colnames(mat), , drop = F]
784
    }
785
    
786
    if(!is.na2(annotation_row)){
787
        annotation_row = annotation_row[rownames(mat), , drop = F]
788
    }
789
    
790
    annotation = c(annotation_row, annotation_col)
791
    annotation = annotation[unlist(lapply(annotation, function(x) !is.na2(x)))]
792
    if(length(annotation) != 0){
793
        annotation_colors = generate_annotation_colours(annotation, annotation_colors, drop = drop_levels)
794
    }
795
    else{
796
        annotation_colors = NA
797
    }
798
    
799
    if(!show_rownames){
800
        labels_row = NULL
801
    }
802
    
803
    if(!show_colnames){
804
        labels_col = NULL
805
    }
806
    
807
    # Draw heatmap
808
    gt = heatmap_motor(mat, border_color = border_color, cellwidth = cellwidth, cellheight = cellheight, treeheight_col = treeheight_col, treeheight_row = treeheight_row, tree_col = tree_col, tree_row = tree_row, filename = filename, width = width, height = height, breaks = breaks, color = color, legend = legend, annotation_row = annotation_row, annotation_col = annotation_col, annotation_colors = annotation_colors, annotation_legend = annotation_legend, main = main, fontsize = fontsize, fontsize_row = fontsize_row, fontsize_col = fontsize_col, fmat = fmat, fontsize_number = fontsize_number, number_color = number_color, gaps_row = gaps_row, gaps_col = gaps_col, labels_row = labels_row, labels_col = labels_col, ...)
809
    
810
    if(is.na(filename) & !silent){
811
        grid.newpage()
812
        grid.draw(gt)
813
    }
814
    
815
    invisible(list(tree_row = tree_row, tree_col = tree_col, kmeans = km, gtable = gt))
816
}
817
818