Commit id: d5adc92bdbf27b45a6a4b5c7e29d2e4fe8380194
allow reordering
git-svn-id: https://hedgehog.fhcrc.org/bioconductor/trunk/madman/Rpacks/ComplexHeatmap@107130 bc3139a8-67e5-0310-9ffc-ced21a209358
... | ... |
@@ -5,7 +5,6 @@ export(anno_histogram) |
5 | 5 |
export(HeatmapAnnotation) |
6 | 6 |
export(plotDataFrame) |
7 | 7 |
exportMethods(draw_dimnames) |
8 |
-export(+.AdditiveUnit) |
|
9 | 8 |
exportClasses(ColorMapping) |
10 | 9 |
export(ColorMapping) |
11 | 10 |
exportClasses(HeatmapAnnotation) |
... | ... |
@@ -15,6 +14,7 @@ export(anno_points) |
15 | 14 |
exportMethods(draw_title) |
16 | 15 |
exportMethods(draw) |
17 | 16 |
exportMethods(draw_heatmap_body) |
17 |
+export('+.AdditiveUnit') |
|
18 | 18 |
exportMethods(color_mapping_legend) |
19 | 19 |
exportMethods(prepare) |
20 | 20 |
exportMethods(heatmap_legend_size) |
... | ... |
@@ -1,3 +1,11 @@ |
1 |
+CHANGES in VERSION 1.2.7 |
|
2 |
+ |
|
3 |
+* add `rows_reorder_weight` and `columns_reorder_weight` in `Heatmap()` |
|
4 |
+* `draw,HeatmapList-method` returns a list of orders |
|
5 |
+* clustering is stored as dendrogram internally |
|
6 |
+ |
|
7 |
+========================================= |
|
8 |
+ |
|
1 | 9 |
CHANGES in VERSION 1.2.6 |
2 | 10 |
|
3 | 11 |
* graphical parameters for row names can be set as same length |
... | ... |
@@ -157,12 +157,16 @@ Heatmap = setClass("Heatmap", |
157 | 157 |
# -column_order order of column. It makes it easy to adjust column order for both matrix and column annotations. |
158 | 158 |
# -row_names_side should the row names be put on the left or right of the heatmap? |
159 | 159 |
# -show_row_names whether show row names. |
160 |
+# -row_reorder apply reordering on rows. The value can be a logical value or a vector which contains weight |
|
161 |
+# which is used to reorder rows |
|
160 | 162 |
# -row_names_max_width maximum width of row names viewport. Because some times row names can be very long, it is not reasonable |
161 | 163 |
# to show them all. |
162 | 164 |
# -row_names_gp graphic parameters for drawing text. |
163 | 165 |
# -column_names_side should the column names be put on the top or bottom of the heatmap? |
164 | 166 |
# -column_names_max_height maximum height of column names viewport. |
165 | 167 |
# -show_column_names whether show column names. |
168 |
+# -column_reorder apply reordering on columns. The value can be a logical value or a vector which contains weight |
|
169 |
+# which is used to reorder columns |
|
166 | 170 |
# -column_names_gp graphic parameters for drawing text. |
167 | 171 |
# -top_annotation a `HeatmapAnnotation` object which contains a list of annotations. |
168 | 172 |
# -top_annotation_height total height of the column annotations on the top. |
... | ... |
@@ -214,10 +218,12 @@ Heatmap = function(matrix, col, name, na_col = "grey", rect_gp = gpar(col = NA), |
214 | 218 |
cluster_rows = TRUE, clustering_distance_rows = "euclidean", |
215 | 219 |
clustering_method_rows = "complete", row_hclust_side = c("left", "right"), |
216 | 220 |
row_hclust_width = unit(10, "mm"), show_row_hclust = TRUE, |
221 |
+ row_reorder = NULL, |
|
217 | 222 |
row_hclust_gp = gpar(), cluster_columns = TRUE, |
218 | 223 |
clustering_distance_columns = "euclidean", clustering_method_columns = "complete", |
219 | 224 |
column_hclust_side = c("top", "bottom"), column_hclust_height = unit(10, "mm"), |
220 | 225 |
show_column_hclust = TRUE, column_hclust_gp = gpar(), |
226 |
+ column_reorder = NULL, |
|
221 | 227 |
row_order = NULL, column_order = NULL, |
222 | 228 |
row_names_side = c("right", "left"), show_row_names = TRUE, |
223 | 229 |
row_names_max_width = unit(4, "cm"), row_names_gp = gpar(fontsize = 12), |
... | ... |
@@ -281,9 +287,13 @@ Heatmap = function(matrix, col, name, na_col = "grey", rect_gp = gpar(col = NA), |
281 | 287 |
if(inherits(cluster_rows, c("dendrogram", "hclust"))) { |
282 | 288 |
.Object@matrix_param$split = split |
283 | 289 |
} else { |
284 |
- if(!is.data.frame(split)) split = data.frame(split) |
|
285 |
- if(nrow(split) != nrow(matrix)) { |
|
286 |
- stop("Length or number of rows of `split` should be same as rows in `matrix`.") |
|
290 |
+ if(identical(cluster_rows, TRUE) && is.numeric(split) && length(split) == 1) { |
|
291 |
+ |
|
292 |
+ } else { |
|
293 |
+ if(!is.data.frame(split)) split = data.frame(split) |
|
294 |
+ if(nrow(split) != nrow(matrix)) { |
|
295 |
+ stop("Length or number of rows of `split` should be same as rows in `matrix`.") |
|
296 |
+ } |
|
287 | 297 |
} |
288 | 298 |
} |
289 | 299 |
} |
... | ... |
@@ -398,6 +408,7 @@ Heatmap = function(matrix, col, name, na_col = "grey", rect_gp = gpar(col = NA), |
398 | 408 |
.Object@row_hclust_param$width = row_hclust_width + unit(1, "mm") # append the gap |
399 | 409 |
.Object@row_hclust_param$show = show_row_hclust |
400 | 410 |
.Object@row_hclust_param$gp = check_gp(row_hclust_gp) |
411 |
+ .Object@row_hclust_param$reorder = row_reorder |
|
401 | 412 |
.Object@row_order_list = list() # default order |
402 | 413 |
if(is.null(row_order)) { |
403 | 414 |
.Object@row_order = seq_len(nrow(matrix)) |
... | ... |
@@ -431,6 +442,7 @@ Heatmap = function(matrix, col, name, na_col = "grey", rect_gp = gpar(col = NA), |
431 | 442 |
.Object@column_hclust_param$height = column_hclust_height + unit(1, "mm") # append the gap |
432 | 443 |
.Object@column_hclust_param$show = show_column_hclust |
433 | 444 |
.Object@column_hclust_param$gp = check_gp(column_hclust_gp) |
445 |
+ .Object@column_hclust_param$reorder = column_reorder |
|
434 | 446 |
if(is.null(column_order)) { |
435 | 447 |
.Object@column_order = seq_len(ncol(matrix)) |
436 | 448 |
} else { |
... | ... |
@@ -520,6 +532,7 @@ setMethod(f = "make_column_cluster", |
520 | 532 |
distance = object@column_hclust_param$distance |
521 | 533 |
method = object@column_hclust_param$method |
522 | 534 |
order = object@column_order |
535 |
+ reorder = object@column_hclust_param$reorder |
|
523 | 536 |
|
524 | 537 |
if(object@column_hclust_param$cluster) { |
525 | 538 |
if(!is.null(object@column_hclust_param$obj)) { |
... | ... |
@@ -530,6 +543,35 @@ setMethod(f = "make_column_cluster", |
530 | 543 |
object@column_hclust = hclust(get_dist(t(mat), distance), method = method) |
531 | 544 |
} |
532 | 545 |
column_order = get_hclust_order(object@column_hclust) # we don't need the pre-defined orders |
546 |
+ |
|
547 |
+ if(inherits(object@column_hclust, "hclust")) { |
|
548 |
+ object@column_hclust = as.dendrogram(object@column_hclust) |
|
549 |
+ } |
|
550 |
+ |
|
551 |
+ if(identical(reorder, NULL)) { |
|
552 |
+ if(is.numeric(mat)) { |
|
553 |
+ reorder = TRUE |
|
554 |
+ } else { |
|
555 |
+ reorder = FALSE |
|
556 |
+ } |
|
557 |
+ } |
|
558 |
+ |
|
559 |
+ do_reorder = TRUE |
|
560 |
+ if(identical(reorder, NA) || identical(reorder, FALSE)) { |
|
561 |
+ do_reorder = FALSE |
|
562 |
+ } |
|
563 |
+ if(identical(reorder, TRUE)) { |
|
564 |
+ do_reorder = TRUE |
|
565 |
+ reorder = colMeans(mat, na.rm = TRUE) |
|
566 |
+ } |
|
567 |
+ |
|
568 |
+ if(do_reorder) { |
|
569 |
+ if(length(reorder) != ncol(mat)) { |
|
570 |
+ stop("weight of reordering should have same length as number of columns.\n") |
|
571 |
+ } |
|
572 |
+ object@column_hclust = reorder(object@column_hclust, reorder) |
|
573 |
+ column_order = order.dendrogram(object@column_hclust) |
|
574 |
+ } |
|
533 | 575 |
} else { |
534 | 576 |
column_order = order |
535 | 577 |
} |
... | ... |
@@ -574,9 +616,16 @@ setMethod(f = "make_row_cluster", |
574 | 616 |
order = object@row_order # pre-defined row order |
575 | 617 |
km = object@matrix_param$km |
576 | 618 |
split = object@matrix_param$split |
619 |
+ reorder = object@row_hclust_param$reorder |
|
577 | 620 |
|
578 | 621 |
if(object@row_hclust_param$cluster) { |
579 | 622 |
|
623 |
+ if(is.numeric(split) && length(split) == 1) { |
|
624 |
+ if(is.null(object@row_hclust_param$obj)) { |
|
625 |
+ object@row_hclust_param$obj = hclust(get_dist(mat, distance), method = method) |
|
626 |
+ } |
|
627 |
+ } |
|
628 |
+ |
|
580 | 629 |
if(!is.null(object@row_hclust_param$obj)) { |
581 | 630 |
if(km > 1) { |
582 | 631 |
stop("You can not make k-means clustering since you have already specified a clustering object.") |
... | ... |
@@ -677,7 +726,7 @@ setMethod(f = "make_row_cluster", |
677 | 726 |
object@row_title = row_level |
678 | 727 |
} |
679 | 728 |
} |
680 |
- |
|
729 |
+ o_row_order_list = row_order_list |
|
681 | 730 |
# make hclust in each slice |
682 | 731 |
if(object@row_hclust_param$cluster) { |
683 | 732 |
row_hclust_list = rep(list(NULL), length(row_order_list)) |
... | ... |
@@ -696,15 +745,52 @@ setMethod(f = "make_row_cluster", |
696 | 745 |
} |
697 | 746 |
} |
698 | 747 |
object@row_hclust_list = row_hclust_list |
748 |
+ |
|
749 |
+ for(i in seq_along(object@row_hclust_list)) { |
|
750 |
+ if(inherits(object@row_hclust_list[[i]], "hclust")) { |
|
751 |
+ object@row_hclust_list[[i]] = as.dendrogram(object@row_hclust_list[[i]]) |
|
752 |
+ } |
|
753 |
+ } |
|
754 |
+ |
|
755 |
+ if(identical(reorder, NULL)) { |
|
756 |
+ if(is.numeric(mat)) { |
|
757 |
+ reorder = TRUE |
|
758 |
+ } else { |
|
759 |
+ reorder = FALSE |
|
760 |
+ } |
|
761 |
+ } |
|
762 |
+ |
|
763 |
+ do_reorder = TRUE |
|
764 |
+ if(identical(reorder, NA) || identical(reorder, FALSE)) { |
|
765 |
+ do_reorder = FALSE |
|
766 |
+ } |
|
767 |
+ if(identical(reorder, TRUE)) { |
|
768 |
+ do_reorder = TRUE |
|
769 |
+ reorder = rowMeans(mat, na.rm = TRUE) |
|
770 |
+ } |
|
771 |
+ |
|
772 |
+ if(do_reorder) { |
|
773 |
+ if(length(reorder) != nrow(mat)) { |
|
774 |
+ stop("weight of reordering should have same length as number of rows.\n") |
|
775 |
+ } |
|
776 |
+ for(i in seq_along(row_hclust_list)) { |
|
777 |
+ object@row_hclust_list[[i]] = reorder(object@row_hclust_list[[i]], reorder[which(seq_len(nrow(mat)) %in% o_row_order_list[[i]])]) |
|
778 |
+ row_order_list[[i]] = o_row_order_list[[i]][ order.dendrogram(object@row_hclust_list[[i]]) ] |
|
779 |
+ } |
|
780 |
+ } |
|
699 | 781 |
} |
782 |
+ |
|
783 |
+ |
|
784 |
+ |
|
700 | 785 |
object@row_order_list = row_order_list |
701 | 786 |
object@matrix_param$split = split |
702 | 787 |
|
788 |
+ |
|
703 | 789 |
if(nrow(mat) != length(unlist(row_order_list))) { |
704 | 790 |
stop("Number of rows in the matrix are not the same as the length of\nthe cluster or the row orders.") |
705 | 791 |
} |
706 | 792 |
|
707 |
- # adjust row_names_param$gp is the length of some elements is the same as row slices |
|
793 |
+ # adjust row_names_param$gp if the length of some elements is the same as row slices |
|
708 | 794 |
for(i in seq_along(object@row_names_param$gp)) { |
709 | 795 |
if(length(object@row_names_param$gp[[i]]) == length(object@row_order_list)) { |
710 | 796 |
gp_temp = NULL |
... | ... |
@@ -714,7 +800,6 @@ setMethod(f = "make_row_cluster", |
714 | 800 |
object@row_names_param$gp[[i]] = gp_temp |
715 | 801 |
} |
716 | 802 |
} |
717 |
- |
|
718 | 803 |
return(object) |
719 | 804 |
|
720 | 805 |
}) |
... | ... |
@@ -553,7 +553,7 @@ setMethod(f = "make_layout", |
553 | 553 |
# in the layout. |
554 | 554 |
# |
555 | 555 |
# == value |
556 |
-# This function returns a list of `stats::hclust` objects. |
|
556 |
+# This function returns a list of row dendrograms and column dendrogram. |
|
557 | 557 |
# |
558 | 558 |
# == author |
559 | 559 |
# Zuguang Gu <z.gu@dkfz.de> |
... | ... |
@@ -595,35 +595,21 @@ setMethod(f = "draw", |
595 | 595 |
|
596 | 596 |
upViewport() |
597 | 597 |
|
598 |
- # return a list of hclust object |
|
598 |
+ # return a list of orders |
|
599 | 599 |
n = length(object@ht_list) |
600 |
- hclust_list = vector("list", n) |
|
601 |
- names(hclust_list) = sapply(object@ht_list, function(ht) ht@name) |
|
600 |
+ dend_list = vector("list", n) |
|
601 |
+ names(dend_list) = sapply(object@ht_list, function(ht) ht@name) |
|
602 | 602 |
|
603 | 603 |
for(i in seq_len(n)) { |
604 |
- hclust_list[[i]] = list(row = NULL, column = NULL) |
|
604 |
+ dend_list[[i]] = list(row = NULL, column = NULL) |
|
605 | 605 |
if(inherits(object@ht_list[[i]], "HeatmapAnnotation")) { |
606 | 606 |
} else { |
607 |
- tree = object@ht_list[[i]]@column_hclust |
|
608 |
- # if(is.null(tree)) { |
|
609 |
- # hclust_list[[i]]$column = NULL |
|
610 |
- # } else if(inherits(tree, "dendrogram")) { |
|
611 |
- # hclust_list[[i]]$column = as.hclust(tree) |
|
612 |
- # } else { |
|
613 |
- hclust_list[[i]]$column = tree |
|
614 |
- # } |
|
615 |
- tree_list = object@ht_list[[i]]@row_hclust_list |
|
616 |
- # for(j in seq_along(tree_list)) { |
|
617 |
- # if(is.null(tree_list[[j]])) { |
|
618 |
- # } else if(inherits(tree_list[[j]], "dendrogram")) { |
|
619 |
- # tree_list[[j]] = as.hclust(tree_list[[j]]) |
|
620 |
- # } |
|
621 |
- # } |
|
622 |
- hclust_list[[i]]$row = tree_list |
|
607 |
+ dend_list[[i]]$column = object@ht_list[[i]]@column_hclust |
|
608 |
+ dend_list[[i]]$row = object@ht_list[[i]]@row_hclust_list |
|
623 | 609 |
} |
624 | 610 |
} |
625 | 611 |
|
626 |
- return(invisible(hclust_list)) |
|
612 |
+ return(invisible(dend_list)) |
|
627 | 613 |
}) |
628 | 614 |
|
629 | 615 |
# == title |
... | ... |
@@ -19,10 +19,12 @@ Heatmap(matrix, col, name, na_col = "grey", rect_gp = gpar(col = NA), |
19 | 19 |
cluster_rows = TRUE, clustering_distance_rows = "euclidean", |
20 | 20 |
clustering_method_rows = "complete", row_hclust_side = c("left", "right"), |
21 | 21 |
row_hclust_width = unit(10, "mm"), show_row_hclust = TRUE, |
22 |
+ row_reorder = NULL, |
|
22 | 23 |
row_hclust_gp = gpar(), cluster_columns = TRUE, |
23 | 24 |
clustering_distance_columns = "euclidean", clustering_method_columns = "complete", |
24 | 25 |
column_hclust_side = c("top", "bottom"), column_hclust_height = unit(10, "mm"), |
25 | 26 |
show_column_hclust = TRUE, column_hclust_gp = gpar(), |
27 |
+ column_reorder = NULL, |
|
26 | 28 |
row_order = NULL, column_order = NULL, |
27 | 29 |
row_names_side = c("right", "left"), show_row_names = TRUE, |
28 | 30 |
row_names_max_width = unit(4, "cm"), row_names_gp = gpar(fontsize = 12), |
... | ... |
@@ -73,11 +75,13 @@ Heatmap(matrix, col, name, na_col = "grey", rect_gp = gpar(col = NA), |
73 | 75 |
\item{column_order}{order of column. It makes it easy to adjust column order for both matrix and column annotations.} |
74 | 76 |
\item{row_names_side}{should the row names be put on the left or right of the heatmap?} |
75 | 77 |
\item{show_row_names}{whether show row names.} |
78 |
+ \item{row_reorder}{apply reordering on rows. The value can be a logical value or a vector which contains weight which is used to reorder rows} |
|
76 | 79 |
\item{row_names_max_width}{maximum width of row names viewport. Because some times row names can be very long, it is not reasonableto show them all.} |
77 | 80 |
\item{row_names_gp}{graphic parameters for drawing text.} |
78 | 81 |
\item{column_names_side}{should the column names be put on the top or bottom of the heatmap?} |
79 | 82 |
\item{column_names_max_height}{maximum height of column names viewport.} |
80 | 83 |
\item{show_column_names}{whether show column names.} |
84 |
+ \item{column_reorder}{apply reordering on columns. The value can be a logical value or a vector which contains weight which is used to reorder columns} |
|
81 | 85 |
\item{column_names_gp}{graphic parameters for drawing text.} |
82 | 86 |
\item{top_annotation}{a \code{\link{HeatmapAnnotation}} object which contains a list of annotations.} |
83 | 87 |
\item{top_annotation_height}{total height of the column annotations on the top.} |
... | ... |
@@ -118,6 +122,8 @@ Zuguang Gu <z.gu@dkfz.de> |
118 | 122 |
|
119 | 123 |
} |
120 | 124 |
\examples{ |
125 |
+ |
|
126 |
+ |
|
121 | 127 |
mat = matrix(rnorm(80, 2), 8, 10) |
122 | 128 |
mat = rbind(mat, matrix(rnorm(40, -2), 4, 10)) |
123 | 129 |
rownames(mat) = letters[1:12] |
... | ... |
@@ -20,6 +20,8 @@ This function is only for internal use. |
20 | 20 |
|
21 | 21 |
} |
22 | 22 |
\section{Detials}{ |
23 |
+ |
|
24 |
+ |
|
23 | 25 |
This function is only for internal use.} |
24 | 26 |
\value{ |
25 | 27 |
A \code{\link[grid]{unit}} object. |
... | ... |
@@ -30,4 +32,6 @@ Zuguang Gu <z.gu@dkfz.de> |
30 | 32 |
|
31 | 33 |
} |
32 | 34 |
\examples{ |
35 |
+ |
|
36 |
+ |
|
33 | 37 |
# no example for this internal method} |
... | ... |
@@ -26,7 +26,7 @@ in the layout. |
26 | 26 |
|
27 | 27 |
} |
28 | 28 |
\value{ |
29 |
-This function returns a list of \code{\link[stats]{hclust}} objects. |
|
29 |
+This function returns a list of row dendrograms and column dendrogram. |
|
30 | 30 |
|
31 | 31 |
} |
32 | 32 |
\author{ |
... | ... |
@@ -34,6 +34,8 @@ Zuguang Gu <z.gu@dkfz.de> |
34 | 34 |
|
35 | 35 |
} |
36 | 36 |
\examples{ |
37 |
+ |
|
38 |
+ |
|
37 | 39 |
mat = matrix(rnorm(80, 2), 8, 10) |
38 | 40 |
mat = rbind(mat, matrix(rnorm(40, -2), 4, 10)) |
39 | 41 |
rownames(mat) = letters[1:12] |
... | ... |
@@ -377,6 +377,13 @@ and they want to split rows by splitting the dendrogram into k sub trees. In thi |
377 | 377 |
Heatmap(mat, name = "foo", cluster_rows = dend, split = 2) |
378 | 378 |
``` |
379 | 379 |
|
380 |
+Or they just split rows by specifying `split` as an integer. Note it is different from by `km`. |
|
381 |
+If `split` is an integer, the dendrogram is cut by `cutree`. |
|
382 |
+ |
|
383 |
+```{r} |
|
384 |
+Heatmap(mat, name = "foo", split = 2) |
|
385 |
+``` |
|
386 |
+ |
|
380 | 387 |
### Self define the heatmap body |
381 | 388 |
|
382 | 389 |
`rect_gp` argument provides basic graphic settings for the heatmap body (`fill` parameter is disabled). |
... | ... |
@@ -1011,7 +1018,7 @@ The components that have names are: |
1011 | 1018 |
Following code add annotation names, mark one grid in the heatmap and seperate the first column clusters with two rectangles. |
1012 | 1019 |
|
1013 | 1020 |
```{r, fig.width = 10, fig.height = 7} |
1014 |
-hclust_list = draw(ht_list, row_title = "Heatmap list", column_title = "Heatmap list", |
|
1021 |
+dend_list = draw(ht_list, row_title = "Heatmap list", column_title = "Heatmap list", |
|
1015 | 1022 |
heatmap_legend_side = "right", annotation_legend_side = "left") |
1016 | 1023 |
seekViewport("annotation_points") |
1017 | 1024 |
grid.text("points", unit(0, "npc") - unit(2, "mm"), 0.5, default.units = "npc", just = "right") |
... | ... |
@@ -1023,8 +1030,8 @@ seekViewport("annotation_type") |
1023 | 1030 |
grid.text("type", unit(1, "npc") + unit(2, "mm"), 0.5, default.units = "npc", just = "left") |
1024 | 1031 |
|
1025 | 1032 |
seekViewport("ht1_hclust_column") |
1026 |
-tree = hclust_list$ht1$column |
|
1027 |
-ind = cutree(tree, k = 2)[tree$order] |
|
1033 |
+tree = dend_list$ht1$column |
|
1034 |
+ind = cutree(as.hclust(tree), k = 2)[order.dendrogram(tree)] |
|
1028 | 1035 |
|
1029 | 1036 |
first_index = function(l) which(l)[1] |
1030 | 1037 |
last_index = function(l) { x = which(l); x[length(x)] } |