Connect dots with splines - r

How do I smooth the edges in line plot? I can do I line plot like this:
data <- data.frame(x=1:10, y=c(22,23,21,25,23,24,20,27,22,24))
ggplot(data, aes(x,y)) +
geom_line(colour='forestgreen')
However, I don't like sharp edges. Is there a way to draw a line through those points so that the line is smooth?

This is one way to do it:
library(ggplot2)
library(splines)
library(gridExtra)
dat <- data.frame(x=1:10, y=c(22,23,21,25,23,24,20,27,22,24))
plot.new() # have to do this unfortunately
res <- xspline(dat$x, dat$y, -0.25, draw=FALSE)
gg1 <- ggplot(dat, aes(x,y)) +
geom_line(colour='forestgreen') +
geom_point()
gg2 <- ggplot(data=data.frame(x=res$x, y=res$y), aes(x, y)) +
geom_point(data=dat, aes(x, y), size=1) +
geom_line(color="blue")
grid.arrange(gg1, gg2, ncol=1)
This is using xspline to do the interpolation. Lookup the function to see what tweaking the -0.25 parameter (range is -1 to 1) will do.

Related

ggplotly with geom_ribbon grouping

I'm having some problems in converting a ggplot in to a plotly object, and retaining the same legend attributes. What I want:
For grouped series, a single line for fit, and faded region for ribbon of same colour, with transparency
No lines at the edge of the ribbon
Grouped legends for the lines, points and ribbons
Here is the code showing the 2 approaches I tried based on this answer:
ggplot: remove lines at ribbon edges
Both have an undesirable effect as you can see when running. Any suggestions would be great :)
library(plotly)
library(ggplot2)
# fake data
set.seed(1)
dt <- data.frame(x=rep(1:7,2), group=rep(letters[1:2], each=7), value=runif(14))
dt$lwr <- dt$value*.9
dt$upr <- dt$value*1.1
# build plot in ggplot, don't want lines at the edge
pl <- ggplot(data=dt, aes(y=value, x=x, group=group, colour=group,
fill=group)) +
geom_point() +
geom_line() +
geom_ribbon(aes(ymin=lwr, ymax=upr), alpha=.3, linetype=0) +
theme_minimal()
# looks ok, no lines at the edges
pl
# lines at edges, single group
ggplotly(pl)
# alternative: try reverting colour to NA
pl2 <- ggplot(data=dt, aes(y=value, x=x, group=group, colour=group,
fill=group)) +
geom_point() +
geom_line() +
geom_ribbon(aes(ymin=lwr, ymax=upr), alpha=.3, colour=NA) +
theme_minimal()
# looks ok
pl2
# no lines, but now not grouped, and some weird naming
ggplotly(pl2)
Thanks, Jonny
EDIT:
Addition to the accepted answer, in functional form
# dd: ggplotly object
library(stringi)
library(rvest)
remove_ggplotly_ribbon_lines <- function(dd){
find <- rvest::pluck(dd$x$data, "fillcolor")
w <- which(!sapply(find, is.null))
for(i in w){
dd$x$data[[i]]$line$color <-
stringi::stri_replace_all_regex(dd$x$data[[i]]$line$color, ",[\\d.]*\\)$", ",0.0)")
}
return(dd)
}
remove_ggplotly_ribbon_lines(ggplotly(pl))
Hi this is more a comment than an answer but I do not have right to post comments.
If you investigate the ggplotly object you will see that it is actually just a list. Changing the right elements of the list helps in controlling plot options.
The solution below just changes the alpha of the lines at ribbon edges. Hope this helps
library(plotly)
set.seed(1)
dt <- data.frame(x=rep(1:7,2), group=rep(letters[1:2], each=7), value=runif(14))
dt$lwr <- dt$value*.9
dt$upr <- dt$value*1.1
# build plot in ggplot, don't want lines at the edge
pl <- ggplot(data=dt, aes(y=value, x=x, group=group, colour=group,
fill=group)) +
geom_point() +
geom_line() +
geom_ribbon(aes(ymin=lwr, ymax=upr), alpha=.3, linetype=0) +
theme_minimal()
# looks ok, no lines at the edges
pl
# no lines at edges
dd = ggplotly(pl)
dd$x$data[[3]]$line$color = "rgba(248,118,109,0.0)"
dd$x$data[[4]]$line$color = "rgba(0,191,196,0.0)"
dd

How do I get annotation_custom() grob to display along with scale_y_reverse() using R and ggplot2?

I'm new to ggplot2 and relatively new to R. I can make a picture appear on a plot, and I can make the y axis reverse scale, but I don't know how to do both at once. For example:
library(ggplot2)
y=c(1,2,3)
x=c(0,0,0)
d=data.frame(x=x, y=y)
#following http://stackoverflow.com/questions/9917049/inserting-an-image-to-ggplot2/9917684#9917684
library(png)
library(grid)
img <- readPNG(system.file("img", "Rlogo.png", package="png"))
g <- rasterGrob(img, interpolate=TRUE)
#these work fine - either reversing scale, or adding custom annotation
ggplot(d, aes(x, y)) + geom_point()
ggplot(d, aes(x, y)) + geom_point() + scale_y_reverse()
ggplot(d, aes(x, y)) + geom_point() + annotation_custom(g, xmin=.23, xmax=.27, ymin=1.8, ymax=2.2)
#these don't...combining both reverse scale and custom annotation
ggplot(d, aes(x, y)) + geom_point() + annotation_custom(g, xmin=.23, xmax=.27, ymin=1.8, ymax=2.2) + scale_y_reverse()
ggplot(d, aes(x, y)) + geom_point() + annotation_custom(g, xmin=.23, xmax=.27, ymin=2.2, ymax=1.8) + scale_y_reverse()
I'm sure I'm missing something pretty basic. Where should I start looking both to get my little graphic to display on a reverse scale plot, and also to understand things better under the hood?
CLARIFICATION IN RESPONSE TO COMMENTS:
The example above is me trying to simplify the problem I'm having. I don't know if it matters, but I'm not merely trying to overlay some data on a static image. I actually want to place an image in a certain spot on a plot, based on the data in the plot. However, I can't seem to do that when the axis scale is reversed. And, as it turns out, I can't even put an image in an absolute position when the scale is reversed either, so that's the code example I posted.
With scale_y_reverse, you need to set the y coordinates inside annotation_custom to their negative.
library(ggplot2)
y=c(1,2,3)
x=c(0,0,0)
d=data.frame(x=x, y=y)
library(png)
library(grid)
img <- readPNG(system.file("img", "Rlogo.png", package="png"))
g <- rasterGrob(img, interpolate=TRUE)
ggplot(d, aes(x, y)) + geom_point() +
annotation_custom(g, xmin=.20, xmax=.30, ymin=-2.2, ymax=-1.7) +
scale_y_reverse()
Why negative? The y coordinates are the negative of the original. Check out this:
(p = ggplot(d, aes(x=x, y=y)) + geom_point() + scale_y_reverse())
y.axis.limits = ggplot_build(p)$layout$panel_params[[1]][["y.range"]]
y.axis.limits
OR, set the coordinates and size of the grob in relative units inside rasterGrob.
g <- rasterGrob(img, x = .75, y = .5, height = .1, width = .2, interpolate=TRUE)
ggplot(d, aes(x, y)) + geom_point() +
annotation_custom(g) +
scale_y_reverse()

ggplot2: add conditional density curves describing both dimensions of scatterplot

I have scatterplots of 2D data from two categories. I want to add density lines for each dimension -- not outside the plot (cf. Scatterplot with marginal histograms in ggplot2) but right on the plotting surface. I can get this for the x-axis dimension, like this:
set.seed(123)
dim1 <- c(rnorm(100, mean=1), rnorm(100, mean=4))
dim2 <- rnorm(200, mean=1)
cat <- factor(c(rep("a", 100), rep("b", 100)))
mydf <- data.frame(cbind(dim2, dim1, cat))
ggplot(data=mydf, aes(x=dim1, y=dim2, colour=as.factor(cat))) +
geom_point() +
stat_density(aes(x=dim1, y=(-2+(..scaled..))),
position="identity", geom="line")
It looks like this:
But I want an analogous pair of density curves running vertically, showing the distribution of points in the y-dimension. I tried
stat_density(aes(y=dim2, x=0+(..scaled..))), position="identity", geom="line)
but receive the error "stat_density requires the following missing aesthetics: x".
Any ideas? thanks
You can get the densities of the dim2 variables. Then, flip the axes and store them in a new data.frame. After that it is simply plotting them on top of the other graph.
p <- ggplot(data=mydf, aes(x=dim1, y=dim2, colour=as.factor(cat))) +
geom_point() +
stat_density(aes(x=dim1, y=(-2+(..scaled..))),
position="identity", geom="line")
stuff <- ggplot_build(p)
xrange <- stuff[[2]]$ranges[[1]]$x.range # extract the x range, to make the new densities align with y-axis
## Get densities of dim2
ds <- do.call(rbind, lapply(unique(mydf$cat), function(lev) {
dens <- with(mydf, density(dim2[cat==lev]))
data.frame(x=dens$y+xrange[1], y=dens$x, cat=lev)
}))
p + geom_path(data=ds, aes(x=x, y=y, color=factor(cat)))
So far I can produce:
distrib_horiz <- stat_density(aes(x=dim1, y=(-2+(..scaled..))),
position="identity", geom="line")
ggplot(data=mydf, aes(x=dim1, y=dim2, colour=as.factor(cat))) +
geom_point() + distrib_horiz
And:
distrib_vert <- stat_density(data=mydf, aes(x=dim2, y=(-2+(..scaled..))),
position="identity", geom="line")
ggplot(data=mydf, aes(x=dim2, y=dim1, colour=as.factor(cat))) +
geom_point() + distrib_vert + coord_flip()
But combining them is proving tricky.
So far I have only a partial solution since I didn't manage to obtain a vertical stat_density line for each individual category, only for the total set. Maybe this can nevertheless help as a starting point for finding a better solution. My suggestion is to try with the ggMarginal() function from the ggExtra package.
p <- ggplot(data=mydf, aes(x=dim1, y=dim2, colour=as.factor(cat))) +
geom_point() + stat_density(aes(x=dim1, y=(-2+(..scaled..))),
position="identity", geom="line")
library(ggExtra)
ggMarginal(p,type = "density", margins = "y", size = 4)
This is what I obtain:
I know it's not perfect, but maybe it's a step in a helpful direction. At least I hope so. Looking forward to seeing other answers.

Adding a logo to Multi plot output in R or ggplot2

I have trying to add a logo to the output derived from grid.arrange or arrangeGrob.
I have the below code:
library(ggplot2)
p1 <- ggplot(ChickWeight, aes(x=Time, y=weight, colour=Diet, group=Chick)) +
geom_line() +
ggtitle("Growth curve for individual chicks")
p2 <- ggplot(ChickWeight, aes(x=Time, y=weight, colour=Diet)) +
geom_point(alpha=.3) +
geom_smooth(alpha=.2, size=1) +
ggtitle("Fitted growth curve per diet")
p3 <- ggplot(subset(ChickWeight, Time==21), aes(x=weight, colour=Diet))
+ geom_density() +
ggtitle("Final weight, by diet")
p4 <- ggplot(subset(ChickWeight, Time==21), aes(x=weight, fill=Diet)) +
geom_histogram(colour="black", binwidth=50) +
ggtitle("Final weight, by diet")
I have used grid.arrange(p1,p2,p3,p4,ncol=2,clip=4) to put multiple plots to a single plot.
But I am having issue while inserting a logo to the above grid.arrange output.
I tried the below method, but got the below error message.
b <- rasterGrob(img, width=unit(5,"cm"), x = unit(40,"cm"))
z1 <- ggplotGrob(grid.arrange(p1,p2,p3,p4,ncol=2,clip=4))
z1<- gtable_add_grob(z1,b, t=1,l=1, r=5)
grid.newpage()
grid.draw(z1)
Error: No layers in plot
Is there a way or method to add a logo to the output after arrangeGrob or grid.arrange.
Not a gtable answer, but this is a slightly different way to add the logo that might help
library(ggplot2)
library(grid)
library(png)
library(gridExtra)
# Read png
img <- readPNG(system.file("img", "Rlogo.png", package="png"), FALSE)
# Create grobs to add to plot
my_g <- grobTree(rectGrob(gp=gpar(fill="black")),
textGrob("Some text", x=0, hjust=0, gp=gpar(col="white")),
rasterGrob(img, x=1, hjust=1))
# Plot
p <- ggplot(mtcars , aes(wt , mpg)) +
geom_line() +
theme(plot.margin=unit(c(1, 1, 1,1), "cm"))
# Add as a strip along top
grid.arrange(my_g, arrangeGrob(p,p,p,p, ncol=2), heights=c(1, 9))

Remove grey fill for out of bounds values when using limits in scale_

I am using R (3.0.1) and ggplot2 (ggplot2_0.9.3.1) with geom_tile and scale_fill_gradient. I am using the 'limits' option in scale_fill_gradient, as shown below. The problem is that I want to get rid of the grey background that appears outside of the specified limits. I want that background to be white just like the rest of the graph due to theme_bw(). I have been unable to figure out how to do it. Thanks.
pp <- function (n,r=4) {
x <- seq(-r*pi, r*pi, len=n)
df <- expand.grid(x=x, y=x)
df$r <- sqrt(df$x^2 + df$y^2)
df$z <- cos(df$r^2)*exp(-df$r/6)
df
}
p <- ggplot(pp(20), aes(x=x,y=y))
p + theme_bw() + geom_tile(aes(fill=z)) +
scale_fill_gradient(low="green", high="red", limits = c(-0.1, 0.1))
Add the argument na.value="transparent" to scale_fill_gradient:
p + theme_bw() + geom_tile(aes(fill=z)) +
scale_fill_gradient(low="green", high="red",
limits=c(-0.1, 0.1), na.value="transparent")

Resources