Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add segments of circles to ggplot based on product of x & y

Tags:

r

ggplot2

I want to add shaded areas to a chart to help people understand where bad, ok, and good points can fit.

  • Good = x*y>=.66
  • Ok = x*y>=.34
  • Bad = x*y<.34

Generating the right sequence of data to correctly apply the curved boundaries to the chart is proving tough.

  1. What is the most elegant way to generate the curves?
  2. Bonus Q: How would you do this to produce non-overlapping areas so that different colours could be used?

Updates

I've managed to do in a rather hacky way the drawing of the circle segments. I updated the MRE to use the revised segMaker function.

MRE

library(ggplot2)
pts<-seq(0,1,.02)

x<-sample(pts,50,replace=TRUE)
y<-sample(pts,50,replace=TRUE)

# What function will generate correct sequence of values as these are linear?
segMaker<-function(x,by){
# Original
#  data.frame(x=c(seq(0,x,by),0)
#             ,y=c(seq(x,0,-by),0)
#  )

  zero <- data.frame(x = 0, y = 0)
  rs <- seq(0, pi, by)
  xc <- x * cos(rs)
  yc <- x * sin(rs)
  gr <- data.frame(x = xc, y = yc)
  gr <- rbind(gr[gr$x >= 0, ], zero)
  return(gr)
}

firstSeg  <-segMaker(.34,0.02)
secondSeg <-segMaker(.66,0.02)
thirdSeg  <-segMaker(1,0.02)

ggplot(data.frame(x,y),aes(x,y, colour=x*y))+
  geom_point() +
  geom_polygon(data=firstSeg, fill="blue", alpha=.25)+
  geom_polygon(data=secondSeg, fill="blue", alpha=.25)+
  geom_polygon(data=thirdSeg, fill="blue", alpha=.25)

Current & desired shadings

Shaded areas What I have so far What I have so far v2

like image 296
Steph Locke Avatar asked Feb 19 '16 16:02

Steph Locke


2 Answers

You can create a data frame with the boundaries between each region and then use geom_ribbon to plot it. Here's an example using the conditions you supplied (which result in boundaries that are the reciprocal function, rather than circles, but the idea is the same, whichever function you use for the boundaries):

library(ggplot2)

# Fake data
pts<-seq(0,1,.02)

set.seed(19485)
x<-sample(pts,50,replace=TRUE)
y<-sample(pts,50,replace=TRUE)

df = data.frame(x,y)

# Region boundaries
x = seq(0.001,1.1,0.01)

bounds = data.frame(x, ymin=c(-100/x, 0.34/x, 0.66/x),
                    ymax=c(0.34/x, 0.66/x, 100/x), 
                    g=rep(c("Bad","OK","Good"), each=length(x)))

bounds$g = factor(bounds$g, levels=c("Bad","OK","Good"))

ggplot() +
  coord_cartesian(ylim=0:1, xlim=0:1) +
  geom_ribbon(data=bounds, aes(x, ymin=ymin, ymax=ymax, fill=g), colour="grey50", lwd=0.2) +
  geom_point(data=df, aes(x,y), colour="grey20") +
  scale_fill_manual(values=hcl(c(15, 40, 240), 100, 80)) +
  #scale_fill_manual(values=hcl(c(15, 40, 240), 100, 80, alpha=0.25)) + # If you want the fill colors to be transparent
  labs(fill="") +
  guides(fill=guide_legend(reverse=TRUE))

enter image description here

For circular boundaries, assuming we want boundaries at r=1/3 and r=2/3:

# Calculate y for circle, given r and x
cy = function(r, x) {sqrt(r^2 - x^2)}

n = 200
x = unlist(lapply(c(1/3,2/3,1), function(to) seq(0, to, len=n)))
bounds = data.frame(x, ymin = c(rep(0, n), 
                                cy(1/3, seq(0, 1/3, len=n/2)), rep(0, n/2), 
                                cy(2/3, seq(0, 2/3, len=2*n/3)), rep(0, n/3)),
                    ymax = c(cy(1/3, seq(0,1/3,len=n)), 
                             cy(2/3, seq(0,2/3,len=n)), 
                             rep(1,n)),
                    g=rep(c("Bad","OK","Good"), each=n))
bounds$g = factor(bounds$g, levels=c("Bad","OK","Good"))

enter image description here

like image 168
eipi10 Avatar answered Oct 30 '22 21:10

eipi10


If you can use a github package, ggforce adds geom_arc_bar():

# devtools::install_github('thomasp85/ggforce')

library(ggplot2)
library(ggforce)
pts<-seq(0,1,.02)

x<-sample(pts,50,replace=TRUE)
y<-sample(pts,50,replace=TRUE)

arcs <- data.frame(
  x0 = 0,
  y0 = 0,
  start = 0,
  end = pi / 2,
  r0 = c(0, 1/3, 2/3),
  r = c(1/3, 2/3, 1), 
  fill = c("bad", "ok", "good")
)

ggplot() + 
  geom_arc_bar(data = arcs, 
               aes(x0 = x0, y0 = y0, start = start, end = end, r0 = r0, r = r, 
                   fill = fill), alpha = 0.6) + 
  geom_point(data = data.frame(x = x, y = y),
             aes(x = x, y = y))

enter image description here

like image 44
GregF Avatar answered Oct 30 '22 19:10

GregF