Browse code

Commit made by the Bioconductor Git-SVN bridge.

Commit id: 7c6eb6f90218928633147306f8bacd86a5cdd944

mask & scale_x_ggtree function



git-svn-id: file:///home/git/hedgehog.fhcrc.org/bioconductor/trunk/madman/Rpacks/ggtree@104466 bc3139a8-67e5-0310-9ffc-ced21a209358

Guangchuang Yu authored on 02/06/2015 10:18:54
Showing 13 changed files

... ...
@@ -1,7 +1,7 @@
1 1
 Package: ggtree
2 2
 Type: Package
3 3
 Title: a phylogenetic tree viewer for different types of tree annotations
4
-Version: 1.1.7
4
+Version: 1.1.8
5 5
 Author: Guangchuang Yu and Tommy Tsan-Yuk Lam
6 6
 Maintainer: Guangchuang Yu <guangchuangyu@gmail.com>
7 7
 Description: ggtree extends the ggplot2 plotting system which implemented the
... ...
@@ -47,6 +47,7 @@ export(groupClade)
47 47
 export(groupOTU)
48 48
 export(gzoom)
49 49
 export(hilight)
50
+export(mask)
50 51
 export(merge_tree)
51 52
 export(msaplot)
52 53
 export(phylopic)
... ...
@@ -62,7 +63,7 @@ export(read.tree)
62 63
 export(rtree)
63 64
 export(scaleClade)
64 65
 export(scale_color)
65
-export(scale_x_heatmap)
66
+export(scale_x_ggtree)
66 67
 export(theme_tree)
67 68
 export(theme_tree2)
68 69
 export(write.jplace)
... ...
@@ -1,3 +1,8 @@
1
+CHANGES IN VERSION 1.1.8
2
+------------------------
3
+ o mv scale_x_gheatmap to scale_x_ggtree, which also support msaplot <2015-06-02, Tue>
4
+ o add mask function <2015-06-02, Tue>
5
+ 
1 6
 CHANGES IN VERSION 1.1.7
2 7
 ------------------------
3 8
  o add example of msaplot in vignette <2015-05-22, Fri>
... ...
@@ -5,7 +5,7 @@
5 5
 ##' @param p tree view
6 6
 ##' @param data matrix or data.frame
7 7
 ##' @param offset offset of heatmap to tree
8
-##' @param width width of each cell in heatmap
8
+##' @param width total width of heatmap, compare to width of tree
9 9
 ##' @param low color of lowest value
10 10
 ##' @param high color of highest value
11 11
 ##' @param color color of heatmap cell border
... ...
@@ -15,13 +15,20 @@
15 15
 ##' @importFrom reshape2 melt
16 16
 ##' @importFrom ggplot2 geom_tile
17 17
 ##' @importFrom ggplot2 geom_text
18
+##' @importFrom ggplot2 theme
19
+##' @importFrom ggplot2 element_blank
20
+##' @importFrom ggplot2 guides
21
+##' @importFrom ggplot2 guide_legend
18 22
 ##' @export
19 23
 ##' @author Guangchuang Yu
20
-gheatmap <- function(p, data, offset=0, width=NULL, low="green", high="red",
24
+gheatmap <- function(p, data, offset=0, width=1, low="green", high="red",
21 25
                      color="white", colnames=TRUE, font.size=4) {
22
-    if (is.null(width)) {
23
-        width <- (p$data$x %>% range %>% diff)/30
24
-    }
26
+    ## if (is.null(width)) {
27
+    ##     width <- (p$data$x %>% range %>% diff)/30
28
+    ## }
29
+
30
+    ## convert width to width of each cell
31
+    width <- width * (p$data$x %>% range %>% diff) / ncol(data)
25 32
     
26 33
     isTip <- x <- y <- variable <- value <- from <- to <- NULL
27 34
  
... ...
@@ -56,6 +63,9 @@ gheatmap <- function(p, data, offset=0, width=NULL, low="green", high="red",
56 63
     if (colnames) {
57 64
         p2 <- p2 + geom_text(data=mapping, aes(x=to, label=from), y=0, size=font.size)
58 65
     }
66
+
67
+    p2 <- p2 + theme(legend.position="right", legend.title=element_blank())
68
+    p2 <- p2 + guides(fill = guide_legend(override.aes = list(colour = NULL)))
59 69
     
60 70
     attr(p2, "mapping") <- mapping
61 71
     return(p2)
... ...
@@ -68,7 +78,7 @@ gheatmap <- function(p, data, offset=0, width=NULL, low="green", high="red",
68 78
 ##' @param p tree view
69 79
 ##' @param fasta fasta file, multiple sequence alignment
70 80
 ##' @param offset offset of MSA to tree
71
-##' @param width width of each character
81
+##' @param width total width of alignment, compare to width of tree
72 82
 ##' @param color color 
73 83
 ##' @param window specific a slice to display
74 84
 ##' @return tree view
... ...
@@ -80,7 +90,7 @@ gheatmap <- function(p, data, offset=0, width=NULL, low="green", high="red",
80 90
 ##' @importFrom ggplot2 geom_rect
81 91
 ##' @importFrom ggplot2 scale_fill_manual
82 92
 ##' @author Guangchuang Yu
83
-msaplot <- function(p, fasta, offset=0, width=NULL, color=NULL, window=NULL){
93
+msaplot <- function(p, fasta, offset=0, width=1, color=NULL, window=NULL){
84 94
     aln <- readBStringSet(fasta)
85 95
     if (is.null(window)) {
86 96
         window <- c(1, width(aln)[1])
... ...
@@ -101,16 +111,20 @@ msaplot <- function(p, fasta, offset=0, width=NULL, color=NULL, window=NULL){
101 111
     if(is.null(color)) {
102 112
         alphabet <- unlist(seqs) %>% unique
103 113
         alphabet <- alphabet[alphabet != '-']
104
-        color <- rainbow_hcl(length(alphabet))
114
+        ## color <- rainbow_hcl(length(alphabet))
115
+        color <- getCols(length(alphabet))
105 116
         names(color) <- alphabet
106 117
         color <- c(color, '-'=NA)
107 118
     }
108 119
 
109 120
     df <- p$data
110
-    if (is.null(width)) {
111
-        width <- (df$x %>% range %>% diff)/500
112
-    }
121
+    ## if (is.null(width)) {
122
+    ##     width <- (df$x %>% range %>% diff)/500
123
+    ## }
113 124
 
125
+    ## convert width to width of each cell
126
+    width <- width * (df$x %>% range %>% diff) / diff(window)
127
+    
114 128
     df=df[df$isTip,]
115 129
     start <- max(df$x) * 1.02 + offset
116 130
 
... ...
@@ -119,7 +133,7 @@ msaplot <- function(p, fasta, offset=0, width=NULL, color=NULL, window=NULL){
119 133
 
120 134
     h <- ceiling(diff(range(df$y))/length(df$y))
121 135
     xmax <- start + seq_along(slice) * width
122
-    xmin <- xmax -width
136
+    xmin <- xmax - width
123 137
     y <- sort(df$y)
124 138
     ymin <- y - 0.4 *h
125 139
     ymax <- y + 0.4 *h
... ...
@@ -145,13 +159,18 @@ msaplot <- function(p, fasta, offset=0, width=NULL, color=NULL, window=NULL){
145 159
                            ymin=ymin, ymax=ymax, fill=seq)) +
146 160
                                scale_fill_manual(values=color)
147 161
 
162
+    breaks <- hist(seq_along(slice), breaks=10, plot=FALSE)$breaks
163
+    pos <- start + breaks * width
164
+    mapping <- data.frame(from=breaks+1, to=pos)
165
+    attr(p, "mapping") <- mapping
166
+    
148 167
     return(p)
149 168
 }
150 169
 
151 170
 ##' scale x for tree with heatmap
152 171
 ##'
153 172
 ##' 
154
-##' @title scale_x_heatmap
173
+##' @title scale_x_ggtree
155 174
 ##' @param p tree view
156 175
 ##' @param breaks breaks for tree
157 176
 ##' @param labels lables for corresponding breaks
... ...
@@ -159,12 +178,16 @@ msaplot <- function(p, fasta, offset=0, width=NULL, color=NULL, window=NULL){
159 178
 ##' @importFrom ggplot2 scale_x_continuous
160 179
 ##' @export
161 180
 ##' @author Guangchuang Yu
162
-scale_x_heatmap <- function(p, breaks, labels=NULL) {
181
+scale_x_ggtree <- function(p, breaks=NULL, labels=NULL) {
182
+    if (is.null(breaks)) {
183
+        breaks <- hist(p$data$x, breaks=5, plot=FALSE)$breaks
184
+    }
163 185
     m <- attr(p, "mapping")
164 186
     if (is.null(labels)) {
165 187
         labels <- breaks
166 188
     }
167
-    p + scale_x_continuous(breaks=c(breaks, m$to), labels=c(labels, as.character(m$from)))
189
+    breaks <- c(breaks, m$to)
190
+    p + scale_x_continuous(breaks=breaks, labels=c(labels, as.character(m$from)))
168 191
 }
169 192
 
170 193
 
... ...
@@ -1,3 +1,70 @@
1
+##' site mask
2
+##'
3
+##' 
4
+##' @title mask
5
+##' @param tree_object tree object 
6
+##' @param field selected field
7
+##' @param site site
8
+##' @param mask_site if TRUE, site will be masked.
9
+##'                  if FALSE, selected site will not be masked, while other sites will be masked.
10
+##' @return updated tree object
11
+##' @export
12
+##' @author Guangchuang Yu
13
+mask <- function(tree_object, field, site, mask_site=FALSE) {
14
+    has_field <- has.field(tree_object, field)
15
+    if (has_field == FALSE) {
16
+        stop("'field' is not available in 'tree_object'...")
17
+    }
18
+
19
+    has_slot <- attr(has_field, "has_slot")
20
+    is_codeml <- attr(has_field, "is_codeml")
21
+
22
+    if (has_slot) {
23
+        if (is_codeml) {
24
+            field_info <- slot(tree_object@rst, field)
25
+        } else {
26
+            field_info <- slot(tree_object, field)
27
+        }
28
+        field_data <- field_info[,2]
29
+    } else {
30
+        field_data <- tree_object@extraInfo[, field]
31
+    }
32
+
33
+    field_data <- sapply(field_data, gsub, pattern="\n", replacement="/")
34
+    
35
+    if (mask_site == FALSE) {
36
+        x <- field_data[field_data != ""]
37
+        x <- x[!is.na(x)]
38
+        pos <- strsplit(x, " / ") %>% unlist %>%
39
+            gsub("^[a-zA-Z]+", "", . ) %>%
40
+                gsub("[a-zA-Z]\\s*$", "", .) %>%
41
+                    as.numeric
42
+        pos2 <- 1:max(pos)
43
+        pos2 <- pos2[-site]
44
+        site <- pos2
45
+    }
46
+    
47
+    for (i in seq_along(field_data)) {
48
+        for (j in seq_along(site)) {
49
+            pattern <- paste0("/*\\s*[a-zA-Z]", site[j], "[a-zA-Z]\\s*")
50
+            field_data[i] <- gsub(pattern, "",  field_data[i])
51
+        }
52
+        field_data[i] <- gsub("^/\\s", "", field_data[i]) %>% .add_new_line
53
+    }
54
+
55
+    if (has_slot) {
56
+        field_info[,2] <- field_data
57
+        if (is_codeml) {
58
+            slot(tree_object@rst, field) <- field_info
59
+        } else {
60
+            slot(tree_object, field) <- field_info
61
+        }
62
+    } else {
63
+        tree_object@extraInfo[, field] <- field_data
64
+    }
65
+    tree_object
66
+}
67
+
1 68
 
2 69
 read.tip_seq_mlc <- function(mlcfile) {
3 70
     info <- getPhyInfo(mlcfile)
... ...
@@ -2,9 +2,47 @@ has.slot <- function(object, slotName) {
2 2
     if (!isS4(object)) {
3 3
         return(FALSE)
4 4
     }
5
+    .hasSlot(object, slotName)
6
+    ## slot <- tryCatch(slot(object, slotName), error=function(e) NULL)
7
+    ## ! is.null(slot)
8
+}
9
+
10
+has.field <- function(tree_object, field) {
11
+    if ( ! field %in% get.fields(tree_object) ) {
12
+        return(FALSE)
13
+    }
14
+    
15
+    if (is(tree_object, "codeml")) {
16
+        is_codeml <- TRUE
17
+        tree <- tree_object@rst
18
+    } else {
19
+        is_codeml <- FALSE
20
+        tree <- tree_object
21
+    }
22
+    
23
+    if (.hasSlot(tree, field)) {
24
+        has_slot <- TRUE
25
+    } else {
26
+        has_slot <- FALSE
27
+    }
5 28
     
6
-    slot <- tryCatch(slot(object, slotName), error=function(e) NULL)
7
-    ! is.null(slot)
29
+    if (has_slot == FALSE) {
30
+        if (has.extraInfo(tree_object) == FALSE) {
31
+            return(FALSE)
32
+        }
33
+        
34
+        if (nrow(tree_object@extraInfo) == 0) {
35
+            return(FALSE)
36
+        }
37
+        
38
+        if (!field %in% colnames(tree_object@extraInfo)) {
39
+            return(FALSE)
40
+        }
41
+    }
42
+    res <- TRUE
43
+    attr(res, "has_slot") <- has_slot
44
+    attr(res, "is_codeml") <- is_codeml
45
+    return(res)
8 46
 }
9 47
 
10 48
 has.extraInfo <- function(object) {
... ...
@@ -12,7 +50,7 @@ has.extraInfo <- function(object) {
12 50
         return(FALSE)
13 51
     }
14 52
 
15
-    if (! has.slot(object, "extraInfo")) {
53
+    if (! .hasSlot(object, "extraInfo")) {
16 54
         return(FALSE)
17 55
     }
18 56
 
... ...
@@ -108,6 +146,15 @@ plot.subs <- function(x, layout, show.tip.label,
108 146
     p + theme_tree2()
109 147
 }
110 148
 
149
+.add_new_line <- function(res) {
150
+    if (nchar(res) > 50) {
151
+        idx <- gregexpr("/", res)[[1]]
152
+        i <- idx[floor(length(idx)/2)]
153
+        res <- paste0(substring(res, 1, i-1), "\n", substring(res, i+1))
154
+    }
155
+    return(res)
156
+}
157
+
111 158
 get.subs_ <- function(tree, fasta, translate=TRUE, removeGap=TRUE) {
112 159
     N <- getNodeNum(tree)
113 160
     node <- 1:N
... ...
@@ -121,13 +168,7 @@ get.subs_ <- function(tree, fasta, translate=TRUE, removeGap=TRUE) {
121 168
         if (is.null(res)) {
122 169
             return('')
123 170
         }
124
-        if (nchar(res) > 50) {
125
-            idx <- gregexpr("/", res)[[1]]
126
-            i <- idx[floor(length(idx)/2)]
127
-            res <- paste0(substring(res, 1, i-1), "\n", substring(res, i+1))
128
-        }
129
-        
130
-        return(res)
171
+        .add_new_line(res)
131 172
     })
132 173
     
133 174
     dd <- data.frame(node=node, parent=parent, label=label, subs=subs)
... ...
@@ -407,3 +448,16 @@ roundDigit <- function(d) {
407 448
 NULL
408 449
 
409 450
 
451
+## from ChIPseeker
452
+getCols <- function (n) {
453
+    col <- c("#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", 
454
+             "#fdb462", "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd", 
455
+             "#ccebc5", "#ffed6f")
456
+    col2 <- c("#1f78b4", "#ffff33", "#c2a5cf", "#ff7f00", "#810f7c", 
457
+              "#a6cee3", "#006d2c", "#4d4d4d", "#8c510a", "#d73027", 
458
+              "#78c679", "#7f0000", "#41b6c4", "#e7298a", "#54278f")
459
+    col3 <- c("#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", 
460
+              "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6", "#6a3d9a", 
461
+              "#ffff99", "#b15928")
462
+    colorRampPalette(col3)(n)
463
+}
... ...
@@ -1,14 +1,12 @@
1 1
 #  ggtree: a phylogenetic tree viewer for different types of tree annotations
2 2
 
3 3
 [![platform](http://www.bioconductor.org/shields/availability/devel/ggtree.svg)](http://www.bioconductor.org/packages/devel/bioc/html/ggtree.html#archives)
4
-[![Build Status](https://travis-ci.org/GuangchuangYu/ggtree.svg?branch=master)](https://travis-ci.org/GuangchuangYu/ggtree)
5
-[![Build Status](https://ci.appveyor.com/api/projects/status/github/GuangchuangYu/ggtree?svg=true)](https://ci.appveyor.com/project/GuangchuangYu/ggtree/branch/master)
4
+<!--[![Build Status](https://travis-ci.org/GuangchuangYu/ggtree.svg?branch=master)](https://travis-ci.org/GuangchuangYu/ggtree)
5
+[![Build Status](https://ci.appveyor.com/api/projects/status/github/GuangchuangYu/ggtree?svg=true)](https://ci.appveyor.com/project/GuangchuangYu/ggtree/branch/master)-->
6 6
 [![Build Status](http://www.bioconductor.org/shields/build/devel/bioc/ggtree.svg)](http://bioconductor.org/checkResults/devel/bioc-LATEST/ggtree/)
7 7
 [![Bioc](http://www.bioconductor.org/shields/years-in-bioc/ggtree.svg)](http://www.bioconductor.org/packages/devel/bioc/html/ggtree.html#since)
8
-
9
-
10
-[![download](http://www.bioconductor.org/shields/downloads/ggtree.svg)](http://bioconductor.org/packages/stats/bioc/ggtree.html)
11 8
 [![commit](http://www.bioconductor.org/shields/commits/bioc/ggtree.svg)](http://www.bioconductor.org/packages/devel/bioc/html/ggtree.html#svn_source)
9
+[![download](http://www.bioconductor.org/shields/downloads/ggtree.svg)](http://bioconductor.org/packages/stats/bioc/ggtree.html)
12 10
 [![post](http://www.bioconductor.org/shields/posts/ggtree.svg)](https://support.bioconductor.org/t/ggtree/)
13 11
 
14 12
 
15 13
new file mode 100644
... ...
@@ -0,0 +1,82 @@
1
+65
2
+66
3
+69
4
+70
5
+78
6
+79
7
+91
8
+94
9
+95
10
+96
11
+97
12
+98
13
+99
14
+107
15
+108
16
+110
17
+137
18
+138
19
+139
20
+140
21
+141
22
+142
23
+143
24
+145
25
+147
26
+148
27
+149
28
+150
29
+151
30
+152
31
+153
32
+154
33
+156
34
+158
35
+159
36
+160
37
+161
38
+162
39
+171
40
+172
41
+173
42
+174
43
+175
44
+176
45
+183
46
+202
47
+204
48
+205
49
+206
50
+208
51
+209
52
+210
53
+212
54
+213
55
+214
56
+215
57
+217
58
+218
59
+219
60
+220
61
+221
62
+222
63
+223
64
+230
65
+232
66
+233
67
+234
68
+235
69
+236
70
+238
71
+239
72
+241
73
+242
74
+243
75
+258
76
+262
77
+263
78
+287
79
+289
80
+291
81
+292
82
+294
... ...
@@ -4,7 +4,7 @@
4 4
 \alias{gheatmap}
5 5
 \title{gheatmap}
6 6
 \usage{
7
-gheatmap(p, data, offset = 0, width = NULL, low = "green", high = "red",
7
+gheatmap(p, data, offset = 0, width = 1, low = "green", high = "red",
8 8
   color = "white", colnames = TRUE, font.size = 4)
9 9
 }
10 10
 \arguments{
... ...
@@ -14,7 +14,7 @@ gheatmap(p, data, offset = 0, width = NULL, low = "green", high = "red",
14 14
 
15 15
 \item{offset}{offset of heatmap to tree}
16 16
 
17
-\item{width}{width of each cell in heatmap}
17
+\item{width}{total width of heatmap, compare to width of tree}
18 18
 
19 19
 \item{low}{color of lowest value}
20 20
 
21 21
new file mode 100644
... ...
@@ -0,0 +1,28 @@
1
+% Generated by roxygen2 (4.1.1): do not edit by hand
2
+% Please edit documentation in R/paml.R
3
+\name{mask}
4
+\alias{mask}
5
+\title{mask}
6
+\usage{
7
+mask(tree_object, field, site, mask_site = FALSE)
8
+}
9
+\arguments{
10
+\item{tree_object}{tree object}
11
+
12
+\item{field}{selected field}
13
+
14
+\item{site}{site}
15
+
16
+\item{mask_site}{if TRUE, site will be masked.
17
+if FALSE, selected site will not be masked, while other sites will be masked.}
18
+}
19
+\value{
20
+updated tree object
21
+}
22
+\description{
23
+site mask
24
+}
25
+\author{
26
+Guangchuang Yu
27
+}
28
+
... ...
@@ -4,7 +4,7 @@
4 4
 \alias{msaplot}
5 5
 \title{msaplot}
6 6
 \usage{
7
-msaplot(p, fasta, offset = 0, width = NULL, color = NULL, window = NULL)
7
+msaplot(p, fasta, offset = 0, width = 1, color = NULL, window = NULL)
8 8
 }
9 9
 \arguments{
10 10
 \item{p}{tree view}
... ...
@@ -13,7 +13,7 @@ msaplot(p, fasta, offset = 0, width = NULL, color = NULL, window = NULL)
13 13
 
14 14
 \item{offset}{offset of MSA to tree}
15 15
 
16
-\item{width}{width of each character}
16
+\item{width}{total width of alignment, compare to width of tree}
17 17
 
18 18
 \item{color}{color}
19 19
 
20 20
similarity index 72%
21 21
rename from man/scale_x_heatmap.Rd
22 22
rename to man/scale_x_ggtree.Rd
... ...
@@ -1,10 +1,10 @@
1 1
 % Generated by roxygen2 (4.1.1): do not edit by hand
2 2
 % Please edit documentation in R/gplot.R
3
-\name{scale_x_heatmap}
4
-\alias{scale_x_heatmap}
5
-\title{scale_x_heatmap}
3
+\name{scale_x_ggtree}
4
+\alias{scale_x_ggtree}
5
+\title{scale_x_ggtree}
6 6
 \usage{
7
-scale_x_heatmap(p, breaks, labels = NULL)
7
+scale_x_ggtree(p, breaks = NULL, labels = NULL)
8 8
 }
9 9
 \arguments{
10 10
 \item{p}{tree view}
... ...
@@ -763,13 +763,13 @@ The _width_ parameter is to control the width of each cell in the heatmap. It su
763 763
 gheatmap(p+geom_tiplab(size=3), genotype, offset = 2, width=0.5)
764 764
 ```
765 765
 
766
-For time scaled tree, as in this example, it's more often to use x axis by using `theme_tree2`. But with this solution, the heatmap is just another layer and will change the x axis. To over come this issue, we implemented `scale_x_heatmap` to set the x axis more reasonable. User can also use `gplot` and tweak the positions of two plot to align properly.
766
+For time scaled tree, as in this example, it's more often to use x axis by using `theme_tree2`. But with this solution, the heatmap is just another layer and will change the x axis. To over come this issue, we implemented `scale_x_ggtree` to set the x axis more reasonable. User can also use `gplot` and tweak the positions of two plot to align properly.
767 767
 
768 768
 ```{r fig.width=20, fig.height=16, fig.align="center"}
769 769
 p <- ggtree(beast_tree, time_scale=TRUE) + geom_tiplab(size=3, align=TRUE) + theme_tree2()
770 770
 pp <- (p + scale_y_continuous(expand=c(0, 0.3))) %>%
771 771
     gheatmap(genotype, offset=4, width=0.5, colnames=FALSE) %>%
772
-        scale_x_heatmap(breaks=seq(1992, 2013, by=5))
772
+        scale_x_ggtree()
773 773
 pp + theme(legend.position="right")
774 774
 ```
775 775
 
... ...
@@ -789,6 +789,11 @@ A specific slice of the alignment can also be displayed by specific _window_ par
789 789
 + [updating a tree view using %<% operator](http://ygc.name/2015/02/10/ggtree-updating-a-tree-view/)
790 790
 + [an example of drawing beast tree using ggtree](http://ygc.name/2015/04/01/an-example-of-drawing-beast-tree-using-ggtree/)
791 791
 
792
+# Bugs/Feature requests
793
+
794
+If you have any, [let me know](https://github.com/GuangchuangYu/ggtree/issues). Thx!
795
+
796
+
792 797
 # Session info
793 798
 Here is the output of `sessionInfo()` on the system on which this document was compiled:
794 799
 ```{r echo=FALSE}