How to create such a figure using ggplot2 in R? - r

I have a matrix with many zero elements. The column names are labeled on the horizontal axis. I'd like to show explictly the nonzero elements as the bias from the vertical line for each column.
So how should construct a figure such as the example using ggplot2?
An example data can be generated as follow:
set.seed(2018)
N <- 5
p <- 40
dat <- matrix(0.0, nrow=p, ncol=N)
dat[2:7, 1] <- 4*rnorm(6)
dat[4:12, 2] <- 2.6*rnorm(9)
dat[25:33, 3] <- 2.1*rnorm(9)
dat[19:26, 4] <- 3.3*rnorm(8)
dat[33:38, 5] <- 2.9*rnorm(6)
colnames(dat) <- letters[1:5]
print(dat)

Here is another option using facet_wrap and geom_col with theme_minimal.
library(tidyverse)
dat %>%
as.data.frame() %>%
rowid_to_column("row") %>%
gather(key, value, -row) %>%
ggplot(aes(x = row, y = value, fill = key)) +
geom_col() +
facet_wrap(~ key, ncol = ncol(dat)) +
coord_flip() +
theme_minimal()
To further increase the aesthetic similarity to the plot in your original post we can
move the facet strips to the bottom,
rotate strip labels,
add "zero lines" in matching colours,
remove the fill legend, and
get rid of the x & y axis ticks/labels/title.
library(tidyverse)
dat %>%
as.data.frame() %>%
rowid_to_column("row") %>%
gather(key, value, -row) %>%
ggplot(aes(x = row, y = value, fill = key)) +
geom_col() +
geom_hline(data = dat %>%
as.data.frame() %>%
gather(key, value) %>%
count(key) %>%
mutate(y = 0),
aes(yintercept = y, colour = key), show.legend = F) +
facet_wrap(~ key, ncol = ncol(dat), strip.position = "bottom") +
coord_flip() +
guides(fill = FALSE) +
theme_minimal() +
theme(
strip.text.x = element_text(angle = 45),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank())

It would be much easier if you can provide some sample data. Thus I needed to create them and there is no guarantee that this will work for your purpose.
set.seed(123)
# creating some random sample data
df <- data.frame(id = rep(1:100, each = 3),
x = rnorm(300),
group = rep(letters[1:3], each = 100),
bias = sample(0:1, 300, replace = T, prob = c(0.7, 0.3)))
# introducing bias
df$bias <- df$bias*rnorm(nrow(df))
# calculate lower/upper bias for errorbar
df$biaslow <- apply(data.frame(df$bias), 1, function(x){min(0, x)})
df$biasupp <- apply(data.frame(df$bias), 1, function(x){max(0, x)})
Then I used kind of hack to be able to print groups in sufficient distance to make them not overlapped. Based on group I shifted bias variable and also lower and upper bias.
# I want to print groups in sufficient distance
df$bias <- as.numeric(df$group)*5 + df$bias
df$biaslow <- as.numeric(df$group)*5 + df$biaslow
df$biasupp <- as.numeric(df$group)*5 + df$biasupp
And now it is possible to plot it:
library(ggplot2)
ggplot(df, aes(x = x, col = group)) +
geom_errorbar(aes(ymin = biaslow, ymax = biasupp), width = 0) +
coord_flip() +
geom_hline(aes(yintercept = 5, col = "a")) +
geom_hline(aes(yintercept = 10, col = "b")) +
geom_hline(aes(yintercept = 15, col = "c")) +
theme(legend.position = "none") +
scale_y_continuous(breaks = c(5, 10, 15), labels = letters[1:3])
EDIT:
To incorporate special design you can add
theme_bw() +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_text(angle = 45, vjust = 0.5, hjust = 1),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
to your plot.
EDIT2:
To incorporate several horizontal lines, you can create different dataset:
df2 <- data.frame(int = unique(as.numeric(df$group)*5),
gr = levels(df$group))
And use
geom_hline(data = df2, aes(yintercept = int, col = gr))
instead of copy/pasting geom_hline for each group level.

Related

Plotting lines in ggplot2

Is it possible to make this graph with ggplot, ordering the graph by the variable "t" in ascending order, distinguishing each "t" according to status (black or white circle, it can be another marker...) and if possible the variable "id " on the ordinate axis.
Id<- c(1,2,3,4)
t<- c(10,5,20,15)
status<- c(0,1,0,1)
df<- data.frame(Id, t, status)
Maybe you want something like this using geom_segment for the lines with geom_vline for the vertical lines. Using shape and fill aesthetics to fill the points with "black" and "white" per status. You can use the following code:
Id<- c(1,2,3,4)
t<- c(10,5,20,15)
status<- c(0,1,0,1)
df<- data.frame(Id, t, status)
library(ggplot2)
library(dplyr)
library(forcats)
df %>%
mutate(Id = as.factor(Id),
status = as.factor(status)) %>%
ggplot(aes(x = t, y = fct_reorder(Id, t, .desc = TRUE), shape = status, fill = status)) +
geom_point() +
geom_segment(aes(x = 0, xend = t, y = Id, yend = Id)) +
geom_vline(xintercept=c(t),linetype="dotted", alpha = 0.4) +
scale_shape_manual(values=c(21, 21), name = "shapes!") +
scale_fill_manual(values=c("black", "white")) +
scale_x_continuous(expand = c(0, 0), limits = c(0, 25)) +
labs(x = "", y = "Id") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position = "none",
axis.title.y = element_text(angle=0))
Created on 2022-07-25 by the reprex package (v2.0.1)

Plotting two way bar chart using ggplot2

I have the following randomly created data:
t<- matrix(sample.int(100,size=20,replace=TRUE),nrow=12,ncol=20)
a = list()
b = list()
for (x in (1:20) ) b[[x]] <- paste0("X_", x)
for (x in (1:12) ) a[[x]] <- paste0("X", x)
row.names(t) <- rbind(a)
colnames(t) <- rbind(b)
t <- as.data.frame(t)
Here t is a hypothetical two way table of frequencies, I am trying to plot a graph like the one given here using ggplot2
I am not sure how can I make t in such a way that it can be used in ggplot2 code given in the link above. Also, I appreciate if you can provide suggestions on how to visualize a larger two way table, for instance, if dimension of t grows to something 30 x 50.
Here's one approach:
EDIT to show values underneath:
t %>%
rownames_to_column() %>%
pivot_longer(-rowname) %>%
mutate(across(rowname:name, fct_inorder)) %>%
ggplot(aes(x = 1, y = value)) +
geom_col() +
geom_text(aes(x = 1, y = 0, label = value), vjust = 1.1, size = 2.5) +
scale_x_continuous(breaks = NULL) +
scale_y_continuous(breaks = NULL) +
facet_grid(rowname~name) +
coord_cartesian(clip = "off")
Here is a modification of Jon Springs code with some "layout" tweaking:
library(tidyverse)
df %>%
rownames_to_column() %>%
pivot_longer(-rowname) %>%
mutate(across(rowname:name, fct_inorder)) %>%
ggplot(aes(x = 1, y = value, fill=value)) +
geom_col(width = 0.5) +
geom_text(aes(x = 1, y = 0, label = value), vjust = 1.1, size = 2.5) +
scale_x_continuous(breaks = NULL) +
scale_y_continuous(breaks = NULL) +
facet_grid(rowname~name, switch = "both") +
coord_cartesian(clip = "off") +
theme_void() +
theme(strip.background = element_blank(),
strip.text.y.left = element_text(angle = 0),
panel.spacing = unit(1, "lines"),
strip.placement = "outside",
strip.switch.pad.grid = unit(0.2, "in"))+
guides(fill="none")

Bar charts connected by lines / How to connect two graphs arranged with grid.arrange in R / ggplot2

At Facebook research, I found these beautiful bar charts which are connected by lines to indicate rank changes:
https://research.fb.com/do-jobs-run-in-families/
I would like to create them using ggplot2. The bar-chart-part was easy:
library(ggplot2)
library(ggpubr)
state1 <- data.frame(state=c(rep("ALABAMA",3), rep("CALIFORNIA",3)),
value=c(61,94,27,10,30,77),
type=rep(c("state","local","fed"),2),
cumSum=c(rep(182,3), rep(117,3)))
state2 <- data.frame(state=c(rep("ALABAMA",3), rep("CALIFORNIA",3)),
value=c(10,30,7,61,94,27),
type=rep(c("state","local","fed"),2),
cumSum=c(rep(117,3), rep(182,3)))
fill <- c("#40b8d0", "#b2d183", "#F9756D")
p1 <- ggplot(data = state1) +
geom_bar(aes(x = reorder(state, value), y = value, fill = type), stat="identity") +
theme_bw() +
scale_fill_manual(values=fill) +
labs(x="", y="Total budget in 1M$") +
theme(legend.position="none",
legend.direction="horizontal",
legend.title = element_blank(),
axis.line = element_line(size=1, colour = "black"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(), panel.background = element_blank()) +
coord_flip()
p2 <- ggplot(data = state2) +
geom_bar(aes(x = reorder(state, value), y = value, fill = type), stat="identity") +
theme_bw() +
scale_fill_manual(values=fill) + labs(x="", y="Total budget in 1M$") +
theme(legend.position="none",
legend.direction="horizontal",
legend.title = element_blank(),
axis.line = element_line(size=1, colour = "black"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank()) +
scale_x_discrete(position = "top") +
scale_y_reverse() +
coord_flip()
p3 <- ggarrange(p1, p2, common.legend = TRUE, legend = "bottom")
But I couldn't come up with a solution to the line-part. When adding lines e.g. to the left side by
p3 + geom_segment(aes(x = rep(1:2, each=3), xend = rep(1:10, each=3),
y = cumSum[order(cumSum)], yend=cumSum[order(cumSum)]+10), size = 1.2)
The problem is that the lines will not be able to cross over to the right side.
It looks like this:
Basically, I would like to connect the 'California' bar on the left with the Caifornia bar on the right.
To do that, I think, I have to get access to the superordinate level of the graph somehow. I've looked into viewports and was able to overlay the two bar charts with a chart made out of geom_segment but then I couldn't figure out the right layout for the lines:
subplot <- ggplot(data = state1) +
geom_segment(aes(x = rep(1:2, each=3), xend = rep(1:2, each=3),
y = cumSum[order(cumSum)], yend =cumSum[order(cumSum)]+10),
size = 1.2)
vp <- viewport(width = 1, height = 1, x = 1, y = unit(0.7, "lines"),
just ="right", "bottom"))
print(p3)
print(subplot, vp = vp)
Help or pointers are greatly appreciated.
This is a really interesting problem. I approximated it using the patchwork library, which lets you add ggplots together and gives you an easy way to control their layout—I much prefer it to doing anything grid.arrange-based, and for some things it works better than cowplot.
I expanded on the dataset just to get some more values in the two data frames.
library(tidyverse)
library(patchwork)
set.seed(1017)
state1 <- data_frame(
state = rep(state.name[1:5], each = 3),
value = floor(runif(15, 1, 100)),
type = rep(c("state", "local", "fed"), times = 5)
)
state2 <- data_frame(
state = rep(state.name[1:5], each = 3),
value = floor(runif(15, 1, 100)),
type = rep(c("state", "local", "fed"), times = 5)
)
Then I made a data frame that assigns ranks to each state based on other values in their original data frame (state1 or state2).
ranks <- bind_rows(
state1 %>% mutate(position = 1),
state2 %>% mutate(position = 2)
) %>%
group_by(position, state) %>%
summarise(state_total = sum(value)) %>%
mutate(rank = dense_rank(state_total)) %>%
ungroup()
I made a quick theme to keep things very minimal and drop axis marks:
theme_min <- function(...) theme_minimal(...) +
theme(panel.grid = element_blank(), legend.position = "none", axis.title = element_blank())
The bump chart (the middle one) is based on the ranks data frame, and has no labels. Using factors instead of numeric variables for position and rank gave me a little more control over spacing, and lets the ranks line up with discrete 1 through 5 values in a way that will match the state names in the bar charts.
p_ranks <- ggplot(ranks, aes(x = as.factor(position), y = as.factor(rank), group = state)) +
geom_path() +
scale_x_discrete(breaks = NULL, expand = expand_scale(add = 0.1)) +
scale_y_discrete(breaks = NULL) +
theme_min()
p_ranks
For the left bar chart, I sort the states by value and turn the values negative to point to the left, then give it the same minimal theme:
p_left <- state1 %>%
mutate(state = as.factor(state) %>% fct_reorder(value, sum)) %>%
arrange(state) %>%
mutate(value = value * -1) %>%
ggplot(aes(x = state, y = value, fill = type)) +
geom_col(position = "stack") +
coord_flip() +
scale_y_continuous(breaks = NULL) +
theme_min() +
scale_fill_brewer()
p_left
The right bar chart is pretty much the same, except the values stay positive and I moved the x-axis to the top (becomes right when I flip the coordinates):
p_right <- state2 %>%
mutate(state = as.factor(state) %>% fct_reorder(value, sum)) %>%
arrange(state) %>%
ggplot(aes(x = state, y = value, fill = type)) +
geom_col(position = "stack") +
coord_flip() +
scale_x_discrete(position = "top") +
scale_y_continuous(breaks = NULL) +
theme_min() +
scale_fill_brewer()
Then because I've loaded patchwork, I can add the plots together and specify the layout.
p_left + p_ranks + p_right +
plot_layout(nrow = 1)
You may want to adjust spacing and margins some more, such as with the expand_scale call with the bump chart. I haven't tried this with axis marks along the y-axes (i.e. bottoms after flipping), but I have a feeling things might get thrown out of whack if you don't add a dummy axis to the ranks. Plenty still to mess around with, but it's a cool visualization project you posed!
Here's a pure ggplot2 solution, which combines the underlying data frames into one & plots everything in a single plot:
Data manipulation:
library(dplyr)
bar.width <- 0.9
# combine the two data sources
df <- rbind(state1 %>% mutate(source = "state1"),
state2 %>% mutate(source = "state2")) %>%
# calculate each state's rank within each data source
group_by(source, state) %>%
mutate(state.sum = sum(value)) %>%
ungroup() %>%
group_by(source) %>%
mutate(source.rank = as.integer(factor(state.sum))) %>%
ungroup() %>%
# calculate the dimensions for each bar
group_by(source, state) %>%
arrange(type) %>%
mutate(xmin = lag(cumsum(value), default = 0),
xmax = cumsum(value),
ymin = source.rank - bar.width / 2,
ymax = source.rank + bar.width / 2) %>%
ungroup() %>%
# shift each data source's coordinates away from point of origin,
# in order to create space for plotting lines
mutate(x = ifelse(source == "state1", -max(xmax) / 2, max(xmax) / 2)) %>%
mutate(xmin = ifelse(source == "state1", x - xmin, x + xmin),
xmax = ifelse(source == "state1", x - xmax, x + xmax)) %>%
# calculate label position for each data source
group_by(source) %>%
mutate(label.x = max(abs(xmax))) %>%
ungroup() %>%
mutate(label.x = ifelse(source == "state1", -label.x, label.x),
hjust = ifelse(source == "state1", 1.1, -0.1))
Plot:
ggplot(df,
aes(x = x, y = source.rank,
xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax,
fill = type)) +
geom_rect() +
geom_line(aes(group = state)) +
geom_text(aes(x = label.x, label = state, hjust = hjust),
check_overlap = TRUE) +
# allow some space for the labels; this may be changed
# depending on plot dimensions
scale_x_continuous(expand = c(0.2, 0)) +
scale_fill_manual(values = fill) +
theme_void() +
theme(legend.position = "top")
Data source (same as #camille's):
set.seed(1017)
state1 <- data_frame(
state = rep(state.name[1:5], each = 3),
value = floor(runif(15, 1, 100)),
type = rep(c("state", "local", "fed"), times = 5)
)
state2 <- data_frame(
state = rep(state.name[1:5], each = 3),
value = floor(runif(15, 1, 100)),
type = rep(c("state", "local", "fed"), times = 5)
)

R: grid.arrange marginal plots to ggplot2 "heatmap" (geom_tile)

I want to add two bar charts to the top and right of a heatmap representing the marginal distributions along the two dimensions of the bivariate distribution that the heatmap represents.
Here is some code:
library(gridExtra)
library(ggExtra)
library(cowplot)
# generate some data
df_hm = cbind(
expand.grid(
rows = sample(letters, 10),
cols = sample(LETTERS, 10)
),
value = rnorm(100)
)
# plot the heatmap
gg_hm = df_hm %>%
ggplot(aes(x = rows, y = cols, fill = value)) +
geom_tile() +
theme(legend.position = "bottom")
gg_rows = df_hm %>%
group_by(rows) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = rows,y = value)) +
geom_bar(stat = "identity", position = "dodge")
gg_cols = df_hm %>%
group_by(cols) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = cols, y = value))+
geom_bar(stat = "identity", position = "dodge") +
coord_flip()
gg_empty = df_hm %>%
ggplot(aes(x = cols, y = value)) +
geom_blank() +
theme(axis.text = element_blank(),
axis.title = element_blank(),
line = element_blank(),
panel.background = element_blank())
# try this with grid.arrange
grid.arrange(gg_rows, gg_empty, gg_hm, gg_cols,
ncol = 2, nrow = 2, widths = c(3, 1), heights = c(1, 3))
which produces this:
What I want to be able to do is to move the graphs to align as indicated by the red arrows:
- the y-axis of (1, 1) should line up with the y-axis of (2, 1)
- the x-axis of (2, 1) should line up with the x-axis of (2, 2)
I tried the accepted answer by renato vitolo and the alignments didn't work on my machine. But I subsequently discoverd a much easier solution: the egg package (available on CRAN). egg provides a version of grid.arrange called ggarrange which takes similar arguments but aligns the axes nicely. In the OP's code I just had to add library(egg), library(dplyr), and then replace grid.arrange with ggarrange (having installed egg with install.packages("egg")).
Full code:
library(gridExtra)
library(cowplot)
library(egg)
library(dplyr)
# generate some data
df_hm = cbind(
expand.grid(
rows = sample(letters, 10),
cols = sample(LETTERS, 10)
),
value = rnorm(100)
)
# plot the heatmap
gg_hm = df_hm %>%
ggplot(aes(x = rows, y = cols, fill = value)) +
geom_tile() +
theme(legend.position = "bottom")
gg_rows = df_hm %>%
group_by(rows) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = rows,y = value)) +
geom_bar(stat = "identity", position = "dodge")
gg_cols = df_hm %>%
group_by(cols) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = cols, y = value))+
geom_bar(stat = "identity", position = "dodge") +
coord_flip()
gg_empty = df_hm %>%
ggplot(aes(x = cols, y = value)) +
geom_blank() +
theme(axis.text = element_blank(),
axis.title = element_blank(),
line = element_blank(),
panel.background = element_blank())
ggarrange(
gg_rows, gg_empty, gg_hm, gg_cols,
nrow = 2, ncol = 2, widths = c(3, 1), heights = c(1, 3)
)
Output:
gtable is extremely useful. scales provides tools to format the axis ticks, to achieve alignment between the text y.ticks of the heatmap (X, F, ...) and the numeric y.ticks of the barplot on top, by formatting the former to a fixed width of 5 chars (to be adapted for your specific barplot).
require(ggplot2)
require(gtable)
require(grid)
library(dplyr)
library(scales)
## To format heatmap y.ticks with appropriate width (5 chars),
## to align with gg_rows y.tics
ytickform <- function(x){
lab <- sprintf("%05s",x)
}
set.seed(123)
## generate some data
df_hm = cbind(
expand.grid(
rows = sample(letters, 10),
cols = sample(LETTERS, 10)
),
value = rnorm(100)
)
# plot the heatmap
gg_hm = df_hm %>%
ggplot(aes(x = rows, y = cols, fill = value)) +
geom_tile() +
scale_y_discrete(label=ytickform) +
theme(legend.position = "bottom",
plot.margin = unit(c(3,3,3,3), "mm"))
gg_rows = df_hm %>%
group_by(rows) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = rows,y = value)) +
geom_bar(stat = "identity", position = "dodge") +
theme(plot.margin = unit(c(3,3,3,3), "mm"))
gg_cols = df_hm %>%
group_by(cols) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = cols, y = value))+
geom_bar(stat = "identity", position = "dodge") +
coord_flip() +
theme(plot.margin = unit(c(3,3,3,3), "mm"))
## extract legend from heatmap
g <- ggplotGrob(gg_hm)$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
## plot heatmap without legend
g <- ggplotGrob(gg_hm + theme(legend.position="none"))
## add column and put column barplot within
g <- gtable_add_cols(g, unit(5,"cm"))
g <- gtable_add_grob(g, ggplotGrob(gg_cols),
t = 1, l=ncol(g), b=nrow(g), r=ncol(g))
## add row and put legend within
g <- gtable_add_rows(g, unit(1,"cm"))
g <- gtable_add_grob(g, legend,
t = nrow(g), l=1, b=nrow(g), r=ncol(g)-1)
## add row on top and put row barplot within
g <- gtable_add_rows(g, unit(5,"cm"), 0)
g <- gtable_add_grob(g, ggplotGrob(gg_rows),
t = 1, l=1, b=1, r=5)
grid.newpage()
grid.draw(g)
References:
Align ggplot2 plots vertically
http://www.cookbook-r.com/Graphs/Axes_(ggplot2)/#tick-mark-label-text-formatters
https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs

Change y axis limits for each row of a facet plot in ggplot2

I have a 3 rows by 5 columns facet plot. Each row show data which spread over different ranges. To properly display my data so everything is shown, I don't set a y axis limit.
Here's my code:
require(reshape2)
library(ggplot2)
library(RColorBrewer)
fileName = paste("./data_test.csv", sep = "")
## data available here: https://dl.dropboxusercontent.com/u/73950/data_test.csv
mydata = read.csv(fileName,sep=",", header=TRUE)
dataM = melt(mydata,c("id"))
dataM = cbind(dataM,
colsplit(dataM$variable,
pattern = "_",
names = c("Network_model", "order", "category")))
dataM$variable <- NULL
dataM <- dcast(dataM, ... ~ category, value.var = "value")
dataM$minCut <- NULL
dataM$nbr_communities <- NULL
dataM$mean_community_size <- NULL
dataM$density <- NULL
my_palette <- colorRampPalette(rev(brewer.pal(11, "Spectral")))
dataM = melt(dataM, id.vars = c("Network_model", "order", "nodesRemoved", "id"))
my_palette = c(brewer.pal(5, "Blues")[c(4)], brewer.pal(5, "Set1")[c(3)])
ggplot(dataM, aes(x= nodesRemoved ,y= value, group= order, color= order)) +
geom_point(size = .6,alpha = .15,position="jitter") + ## increased size
stat_smooth(se = FALSE, size = .5, alpha = .1, method = "loess") +
scale_color_manual(values=my_palette) +
theme_bw() +
theme(plot.background = element_blank(),
axis.line = element_blank(),
legend.key = element_blank(),
legend.title = element_blank(),
axis.text.x = element_text(size = 8),
axis.text.y = element_text(size = 8)
) +
scale_y_continuous("Value") +
scale_x_continuous("Time", limits=c(0, 100)) +
facet_grid(variable ~ Network_model,scales="free")
Which produces this:
Now, I'd like to selectively set limits for each of the three rows, so that the first row is limits=c(1.9, 3), the second is limits=c(0, 1) and the third is limits=c(.3, .7).
How can I achieve this in ggplot2 of faceting?
I think your best option will be to trim the data before plotting it, e.g. with dplyr,
library(dplyr)
limits <- data.frame(variable = levels(dataM$variable),
min = c(1.9,0,0.3),
max = c(3,1,0.7))
dataC <- inner_join(dataM, limits) %>% filter(value > min, value < max)
last_plot() %+% dataC
(I initially made the points bigger to see the culprits more clearly)

Resources