Plotting ambient functions with ggplot2 - r

I'm using the ambient package in R to generate graphs. It provides a custom base plot method.
library(ambient)
library(dplyr)
grid <- long_grid(x = seq(0, 1, length.out = 1000),
y = seq(0, 1, length.out = 1000))
grid <- grid %>%
mutate(
noise = fracture(gen_perlin, fbm, octaves = 4, x = x, y = y, freq_init = 5)
)
plot(grid, noise)
However I'd like to plot this image using ggplot2 and geom_raster instead of base plotting. I tried doing
ggplot(grid, aes(x = x, y = y, color = noise)) +
geom_raster()
But that just produced
Any idea how I can make this with ggplot2?

Wrong aesthetic: raster draws a bunch of same-size tiles, where the color is the border and the fill is the color of the tile. You want fill=noise here:
ggplot(grid, aes(x,y, fill=noise)) + geom_raster()

Related

How do you make plotly error bars follow a colorscale?

Let me start by saying I have tried the methods outlined in Color coding error bars in a plotly scatter plot, but I have not been able to get them to work. From what I can tell there were changes in how coloraxes are treated in plotly version 4, which might be why this no longer solves the problem.
I want to make a simple scatterplot of two variables with error bars for each variable. I would like the color of the points and their error bars to follow a scale determined by a third variable. I have been unable to make the error bar colors match the markers. Below are a couple simple methods I tried and their results.
set.seed(1)
x.data <- rnorm(20, 0, 1)
y.data <- rnorm(20, 2, 1)
x.err <- runif(20, 0.2, 0.8)
y.err <- runif(20, 0.2, 0.8)
z.data <- runif(20, 1.7, 2.8)
p <- plot_ly() %>%
add_markers(x=x.data, y=y.data,
error_x=list(array=x.err, color=z.data),
error_y=list(array=y.err, color=z.data),
marker=list(color=z.data, colorscale='Viridis',
colorbar=list(title='Z', limits=range(z.data)))) %>%
layout(xaxis=list(title='X'), yaxis=list(title='Y'))
z.norm <- (z.data - min(z.data))/(max(z.data)-min(z.data))
mycramp<-colorRamp(c("darkblue","yellow"))
mycolors<-rgb(mycramp(z.norm), maxColorValue = 255)
p <- plot_ly() %>%
add_markers(x=x.data, y=y.data,
error_x=list(array=x.err, color=mycolors),
error_y=list(array=y.err, color=mycolors),
marker=list(color=mycolors,
colorbar=list(title='Z', limits=range(z.data)))) %>%
layout(xaxis=list(title='X'), yaxis=list(title='Y'))
The documentation on r plotly can be a bit lacking. I think this is what you're looking for though. You want to use the name argument. Both color and name have to be included. We have to define the name argument's levels manually. We can set color to factor as well but then we lose the color ramp. colors let's us change the color palette using RColorBrewer palettes.
plot_ly() %>%
add_markers(x = x.data,
y = y.data,
showlegend = F, # must hide legend here or it shows up twice
name = factor(z.data, levels = z.data, labels = z.data), # this is missing
color = z.data,
colors = "Set1",
error_x = list(array = x.err),
error_y = list(array = y.err))
I had to install the latest dev-versions of ggplot2 and plotly to get this to work (not sure which one did the trick, I just installed both from source). But the solution seems pretty straightforward after that.
#get the latest ggplot2 and plotly from github
# devtools::install_github("tidyverse/ggplot2")
# devtools::install_github("ropensci/plotly")
#first, create a static ggplot2 charts
p1 <-
ggplot( df, aes( x = x.data, y = y.data, color = z.data ) ) +
#plot points
geom_point( ) +
#plot horizontal errorbars
geom_errorbarh( aes( xmin = x.data - x.err, xmax = x.data + x.err ),
height = 0.1 ) +
geom_errorbar( aes( ymin = y.data - y.err, ymax = y.data + y.err ),
width = 0.1 ) +
#set color scale
scale_color_gradient2( low = "purple4", mid = "darkcyan", high = "yellow",
midpoint = min(df$z.data) + ( max(df$z.data) - min(df$z.data) ) / 2 )
#and then plot the chart as plotly
ggplotly(p1)
This is quite a terrible solution, but it might generate some ideas.
Basically it involves creating a vector with the colours that each point/error-bar will have and adding each point to the plot separately.
# create the base plot
# if you uncomment the marker line it will also show the colourbar on the side
# but the formatting is messed up, haven't figured out yet how to solve that
p <- plot_ly(
type='scatter',
mode='markers'
# , marker=list(colorscale='Viridis', colorbar=list(title='Z', limits=range(z.data)))
)
# create a vector of colours per point
z <- (z.data - min(z.data)) / (max(z.data) - min(z.data))
z <- viridis::viridis(1001)[round(z * 1e3) + 1]
# add each point separately
for (i in seq_along(x.err)){
p <- p %>% add_markers(
x=x.data[i],
y=y.data[i],
error_x=list(array=x.err[i], color=z[i]),
error_y=list(array=y.err[i], color=z[i]),
marker=list(color=z[i]),
showlegend=F
)
}
p %>% layout(xaxis=list(title='X'), yaxis=list(title='Y'))
Which generates:

ggplot "glow" effect for line similar to Excel

Does ggplot have a similar line effect as Excel's "Glow"? Based on all my searching I don't think it does, but any other suggestions on how to make one line "stand out" from another line on a plot (other than just alpha)? My plot is basically 2 time-series of the same xy data, except that one series is the raw 15-min data, the other is the daily average of those data. I would like to bring the daily average data visually to the foreground, and push the 15min data to the background.
Thanks!
How's this?
library(ggplot2)
df <- data.frame(x = c(1:6), y = c(1,4,3,4,5,2))
ggplot(df) +
geom_line(aes(x = x, y = y), size = 3, colour = 'red', alpha = 0.1) +
geom_line(aes(x = x, y = y), size = 2, colour = 'red', alpha = 0.2) +
geom_line(aes(x = x, y = y), size = 1, colour = 'red', alpha = 0.5)

Plotting Curved Lines on Polar Charts

I am trying to create a graph that plots points, labels, and lines that connect the points given a start and end position. Then transform it into a polar chart. I can plot the points, labels, and lines, but my issue is when I transform my chart into polar. I have used both geom_curve and geom_segment.
In using geom_curve I get an error because geom_curve is not implemented for non-linear coordinates. Therefore the furthest I can get is this:
In using geom_segment I get it closer to my desired effect, but it draws the lines along the cirlce's circumfrence, which makes sense given how I pass through the coordinates. Here is a photo:
I essentially need a geom_curve for polar coordinates, but I have been unable to find one. I would like the lines on the inside of the circle and curved, there will be some overlap but anyway suggestions it look nice with spacing or something would be welcomed.
Data:
k<-18
ct<-12
q<-6
x_vector1<-seq(1,k,1)
x_vector2<-seq(1,3,1)
x_vector3<-seq(k-2,k,1)
x_vector<-c(x_vector1,x_vector2,x_vector3)
n<-9 ## sets first level radius
radius1<-rep(n,k)
b<-13 ## sets second level radius
radius2<-rep(b,q)
radius<-c(radius1,radius2)
name<-c('Alice','Bob','Charlie','D','E','F','G','H','I','J','K','L',
'M','N','O','Peter','Quin','Roger','Alice2','Bob2','Charlie2',
'Peter2','Quin2','Roger2')
dframe<-data.frame(x_vector,radius,name)
dframe$label_radius<-dframe$radius+1
from<-c('Alice2','Bob','Charlie','D','E','Alice2','Charlie2','Charlie',
'I','J','K','L','M','N','O','Peter','Quin','Alice')
to<-c('Alice','Alice','Alice','Alice','Alice','Bob',
'Bob','Bob','Bob','Charlie','Charlie','Peter',
'Peter','Quin','Quin','Quin','Roger','Roger')
amt<-c(3,8,8,8,6,2,2,4,2,4,8,1,10,5,9,5,2,1)
linethick<-c(0.34,0.91,0.91,0.91,0.68,0.23,0.23,0.45,0.23,0.45,
0.91,0.11,1.14,0.57,1.02,0.57,0.23,0.11)
to_x<-c(1,1,1,1,1,2,2,2,2,3,3,16,16,17,17,17,18,18)
to_rad<-c(9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9)
from_x<-c(1,2,3,4,5,1,3,3,9,10,11,12,13,14,15,16,17,1)
from_rad<-c(13,9,9,9,9,13,13,9,9,9,9,9,9,9,9,9,9,9)
stats<-data.frame(from,to,amt,linethick,to_x,to_rad,from_x,from_rad)
p<-ggplot()+
geom_point(data=dframe,aes(x=x_vector,y=radius),size=3,shape=19)+
geom_text(data=dframe,aes(x=x_vector,y=label_radius,label=name))+
geom_segment(data=stats,aes(x=from_x,y=from_rad,xend=to_x,yend=to_rad, color=to), ## I need arrows starting at TO and going to FROM. ##
arrow=arrow(angle=15,ends='first',length=unit(0.03,'npc'), type='closed'))+
## transform into polar coordinates coord_polar(theta='x',start=0,direction=-1)
## sets up the scale to display from 0 to 7 scale_y_continuous(limits=c(0,14))+
## Used to 'push' the points so all 'k' show up. expand_limits(x=0) p
As others have commented, you can mimic the desired positions produced by coord_polar() by calculating them yourself, in Cartesian coordinates. I.e.:
x = radius * cos(theta)
y = radius * sin(theta)
# where theta is the angle in radians
Manipulate the 2 data frames:
dframe2 <- dframe %>%
mutate(x_vector = as.integer(factor(x_vector))) %>%
mutate(theta = x_vector / n_distinct(x_vector) * 2 * pi + pi / 2) %>%
mutate(x = radius * cos(theta),
y = radius * sin(theta),
y.label = label_radius * sin(theta),
name = as.character(name))
stats2 <- stats %>%
select(from, to, amt, linethick) %>%
mutate_at(vars(from, to), as.character) %>%
left_join(dframe2 %>% select(name, x, y),
by = c("from" = "name")) %>%
rename(x.start = x, y.start = y) %>%
left_join(dframe2 %>% select(name, x, y),
by = c("to" = "name")) %>%
rename(x.end = x, y.end = y)
Plot using geom_curve():
# standardize plot range in all directions
plot.range <- max(abs(c(dframe2$x, dframe2$y, dframe2$y.label))) * 1.1
p <- dframe2 %>%
ggplot(aes(x = x, y = y)) +
geom_point() +
geom_text(aes(y = y.label, label = name)) +
# use 2 geom_curve() layers with different curvatures, such that all segments align
# inwards inside the circle
geom_curve(data = stats2 %>% filter(x.start > 0),
aes(x = x.start, y = y.start,
xend = x.end, yend = y.end,
color = to),
curvature = -0.3,
arrow = arrow(angle=15, ends='first',
length=unit(0.03,'npc'),
type='closed')) +
geom_curve(data = stats2 %>% filter(x.start <= 0),
aes(x = x.start, y = y.start,
xend = x.end, yend = y.end,
color = to),
curvature = 0.3,
arrow = arrow(angle=15, ends='first',
length=unit(0.03,'npc'),
type='closed')) +
expand_limits(x = c(-plot.range, plot.range),
y = c(-plot.range, plot.range)) +
coord_equal() +
theme_void()
p
If you want polar grid lines, these can be mimicked as well using geom_spoke() and ggfortify package's geom_circle():
library(ggforce)
p +
geom_spoke(data = data.frame(x = 0,
y = 0,
angle = pi * seq(from = 0,
to = 2,
length.out = 9), # number of spokes + 1
radius = plot.range),
aes(x = x, y = y, angle = angle, radius = radius),
inherit.aes = FALSE,
color = "grey") +
geom_circle(data = data.frame(x0 = 0,
y0 = 0,
r = seq(from = 0,
to = plot.range,
length.out = 4)), # number of concentric circles + 1
aes(x0 = x0, y0 = y0, r = r),
inherit.aes = FALSE,
color = "grey", fill = NA)
(Note: If you really want these pseudo-grid lines, plot them before the other geom layers.)
Do yo have to do everything in ggplot2?
If not, then one option would be to create the plot with the points (potentially using ggplot2, or just straight grid graphics, maybe even base graphics), then push to the appropriate viewport and use xsplines to add curves between the points (see this answer: Is there a way to make nice "flow maps" or "line area" graphs in R? for a basic example of using xspline).
If you insist on doing everything using ggplot2 then you will probably need to create your own geom function that plots the curves in the polar coordinate plot.

inheritance of aesthetics in ggplot2 0.9.3 & the behavior of annotation_custom

Following up on a recent question of mine, this one is a bit different and illustrates the problem more fully using simpler examples. Below are two data sets and three functions. The first one draws some points and a circle as expected:
library("ggplot2")
library("grid")
td1 <- data.frame(x = rnorm(10), y = rnorm(10))
tf1 <- function(df) { # works as expected
p <- ggplot(aes(x = x, y = y), data = df)
p <- p + geom_point(color = "red")
p <- p + annotation_custom(circleGrob())
print(p)
}
tf1(td1)
This next one seems to ask for the exact sample plot but the code is slightly different. It does not give an error but does not draw the circle:
tf2 <- function(df) { # circle isn't draw, but no error either
p <- ggplot()
p <- p + geom_point(data = df, aes(x = x, y = y), color = "red")
p <- p + annotation_custom(circleGrob())
print(p)
}
tf2(td1)
Finally, this one involves a more complex aesthetic and gives an empty layer when you try to create the circle:
td3 <- data.frame(r = c(rnorm(5, 5, 1.5), rnorm(5, 8, 2)),
f1 = c(rep("L", 5), rep("H", 5)), f2 = rep(c("A", "B"), 5))
tf3 <- function(df) {
p <- ggplot()
p <- p + geom_point(data = df,
aes(x = f1, y = r, color = f2, group = f2))
# p <- p + annotation_custom(circleGrob()) # comment out and it works
print(p)
}
tf3(td3)
Now, I suspect the problem here is not the code but my failure to grasp the inner workings of ggplot2. I could sure use an explanation of why the circle is not drawn in the 2nd case and why the layer is empty in the third case. I looked at the code for annotation_custom and it has a hard-wired inherit.aes = TRUE which I think is the problem. I don't see why this function needs any aesthetic at all (see the docs on it). I did try several ways to override it and set inherit.aes = FALSE but I was unable to fully penetrate the namespace and make it stick. I tried to example the objects created by ggplot2 but these proto objects are nested very deeply and hard to decipher.
To answer this :
"I don't see why this function needs any aesthetic at all".
In fact annotation_custom need x and y aes to scale its grob, and to use after the native units.
Basically it did this :
x_rng <- range(df$x, na.rm = TRUE) ## ranges of x :aes x
y_rng <- range(df$y, na.rm = TRUE) ## ranges of y :aes y
vp <- viewport(x = mean(x_rng), y = mean(y_rng), ## create a viewport
width = diff(x_rng), height = diff(y_rng),
just = c("center","center"))
dd <- editGrob(grod =circleGrob(), vp = vp) ##plot the grob in this vp
To illustrate this I add a grob to a dummy plot used as a scale for my grob. The first is a big scale and the second is a small one.
base.big <- ggplot(aes(x = x1, y = y1), data = data.frame(x1=1:100,y1=1:100))
base.small <- ggplot(aes(x = x1, y = y1), data = data.frame(x1=1:20,y1=1:1))
I define my grob, see I use the native scales for xmin,xmax,ymin,ymax
annot <- annotation_custom(grob = circleGrob(), xmin = 0,
xmax = 20,
ymin = 0,
ymax = 1)
Now see the scales difference(small point / big circle) between (base.big +annot) and (base.small + annot).
library(gridExtra)
grid.arrange(base.big+annot,
base.small+annot)

Non-linear color distribution over the range of values in a geom_raster

I'm faced with the following problem: a few extreme values are dominating the colorscale of my geom_raster plot. An example is probably more clear (note that this example only works with a recent ggplot2 version, I use 0.9.2.1):
library(ggplot2)
library(reshape)
theme_set(theme_bw())
m_small_sd = melt(matrix(rnorm(10000), 100, 100))
m_big_sd = melt(matrix(rnorm(100, sd = 10), 10, 10))
new_xy = m_small_sd[sample(nrow(m_small_sd), nrow(m_big_sd)), c("X1","X2")]
m_big_sd[c("X1","X2")] = new_xy
m = data.frame(rbind(m_small_sd, m_big_sd))
names(m) = c("x", "y", "fill")
ggplot(m, aes_auto(m)) + geom_raster() + scale_fill_gradient2()
Right now I solve this by setting the values over a certain quantile equal to that quantile:
qn = quantile(m$fill, c(0.01, 0.99), na.rm = TRUE)
m = within(m, { fill = ifelse(fill < qn[1], qn[1], fill)
fill = ifelse(fill > qn[2], qn[2], fill)})
This does not really feel like an optimal solution. What I would like to do is have a non-linear mapping of colors to the range of values, i.e. more colors present in the area with more observations. In spplot I could use classIntervals from the classInt package to calculate the appropriate class boundaries:
library(sp)
library(classInt)
gridded(m) = ~x+y
col = c("#EDF8B1", "#C7E9B4", "#7FCDBB", "#41B6C4",
"#1D91C0", "#225EA8", "#0C2C84", "#5A005A")
at = classIntervals(m$fill, n = length(col) + 1)$brks
spplot(m, at = at, col.regions = col)
To my knowledge it is not possible to hardcode this mapping of colors to class intervals like I can in spplot. I could transform the fill axis, but as there are negative values in the fill variable that will not work.
So my question is: are there any solutions to this problem using ggplot2?
Seems that ggplot (0.9.2.1) and scales (0.2.2) bring all you need (for your original m):
library(scales)
qn = quantile(m$fill, c(0.01, 0.99), na.rm = TRUE)
qn01 <- rescale(c(qn, range(m$fill)))
ggplot(m, aes(x = x, y = y, fill = fill)) +
geom_raster() +
scale_fill_gradientn (
colours = colorRampPalette(c("darkblue", "white", "darkred"))(20),
values = c(0, seq(qn01[1], qn01[2], length.out = 18), 1)) +
theme(legend.key.height = unit (4.5, "lines"))

Resources