how to decrease relative distance between geom_point() in ggplot - r

I have a graph that shows a lot of information and I don't want to waste space on whitespace. Is there any way to decrease the plotted distance between points on the x axis. Ideally, I want them to almost touch. I can probably do that changing absolutely every size parameter (dots, axis labels, annotations, legend entries etc) in the plot to something huge but I was wondering if there is an easier way to do so along the lines of position.dodge or so?
Here is an example saved in two different sizes to show that they still have high relative distance:
library(ggplot)
plotdata <- data.frame(my_y = rnorm(3),
my_x = 1:3)
pdf("yourpath/test.pdf",
width = 4, height = 4)
ggplot(plotdata, aes(x = my_x, y = my_y)) + geom_point()
dev.off()
pdf("yourpath/test2.pdf",
width = 2, height = 2)
ggplot(plotdata, aes(x = my_x, y = my_y)) + geom_point()
dev.off()

My answer takes a similar approach to #Quinten but builds in a function for any data:
xmax<-max(plotdata$my_x);ymax=max(plotdata$my_y)
xmin<-min(plotdata$my_x);ymin=min(plotdata$my_y)
pdf("C:/temp/test.pdf",
width = 4, height = 4)
ggplot(plotdata, aes(x = my_x, y = my_y)) + geom_point()+
xlim(xmin-0.1,xmax+0.1)+
ylim(ymin-0.1,ymax+0.1)
dev.off()

If I understand you correctly, you can set limits in coord_cartesian. First plot without limits:
ggplot(plotdata, aes(x = my_x, y = my_y)) + geom_point()
Output:
With limits:
ggplot(plotdata, aes(x = my_x, y = my_y)) + geom_point() + coord_cartesian(xlim=c(-1, 3),
ylim=c(-1.5, 0.5))
Output:
As you can see, they are shown closer together.

Related

R Adjusting space between bars after coord_flip() in ggplot

I have created a chart with ggplot.
I have set the width of each bar, but I also want to set the spacing between the bars to a certain value (I want to reduce the spacing marked in red to 0.1, for example)? I know there are options like position_dodge, but that does not seem to work in combination with coord_flip().
In this related post it was suggested to use theme(aspect.ratio = .2), but this does not allow to additionally set the specific width of the bars.
Are there any suggestions to achieve this?
Code:
library(ggplot2)
set.seed(0)
numbers <- runif(5, 0, 10)
names <- LETTERS[seq(1, 5)]
df <- cbind.data.frame(names, numbers)
ggplot(data = df, aes(x = names, y = numbers)) +
geom_bar(stat = "identity", fill = "blue", width = 0.30) +
coord_flip()
I think the solution is in the combination of
the width argument of geom_bar() (which fills the space reserved for a bar)
and the aspect ratio argument of theme(), which squeezes the plot vertically, leading to 'small' bars.
With the following code:
library(ggplot2)
## your data
set.seed(0)
numbers <- runif(5, 0, 10)
names <- LETTERS[seq(1, 5)]
df <- cbind.data.frame(names, numbers) ## corrected args
ggplot(data = df, aes(x = names, y = numbers)) +
geom_bar(stat="identity",
fill = "blue",
width=0.9) + ### increased
theme(aspect.ratio = .2) + ### aspect ratio added
coord_flip()
you get the following graph:
I personally prefer to use the ggstance package to avoid messing around with coord_flip. you need to switch your x and y
library(ggplot2)
library(ggstance)
ggplot(df, aes(x = numbers, y = names)) +
geom_colh(fill = "blue", width = 0.9)

Plotting pixel by pixel with R

For some unfortunate technical reasons, I need to have a pixel plot.
For example, let's take this simple plot:
plot(0,0)
Points(x=1:10, y=rep(0,10), cex=1)
I need each dot to be exactly a pixel wide. The size parameter cex does not seem to allow such precision.
You could use ggplot with the option geom_point(shape = ".").
For example
# generate random dataframe
df <- data.frame(x = runif(100), y = runif(100))
# make the figure
ggplot(df) + aes(x = x, y = y) + geom_point(shape = ".") + theme_void()
This will create something like:

Selectively make one section of axis line thicker in ggplot

I would like to plot a density plot using ggplot2, and make one section of the x-axis line thicker (or colored differently).
For example:
interval <- c(x1,x2)
x <- ggplot(df, aes(x=value)) + geom_density()
Is there any way to selectively make the x-axis segment corresponding to (x1,x2) thicker or colored differently? Thanks.
You can use annotate to add a line segment. Setting the y coordinates to -Inf will place it on the x axis. Since your example isn't reproducible, I've demonstrated on the mtcars data:
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() +
annotate(
geom = "segment",
x = 3, xend = 4,
y = -Inf, yend = -Inf,
color = "blue",
size = 5
)

geom_col does not plot all data points

I have faced a weird behaviour of geom_col and it drives me crazy: basically it does not plot all the data points. When I add geom_point() I clearly see that they are not all represented with a bar.
MWE :
x = sample(1:2000, size = 600, replace = FALSE)
y = 1:600
ggplot(data.frame(x = x, y = y), aes(x,y)) + geom_col() + geom_point()
It is actually plotting all vertical lines, the problem is that it is too thin to show. If you zoom in you will see all the lines.
Try to make the width of the lines thicker, e.g.
ggplot(data.frame(x = x, y = y), aes(x, y)) + geom_col(width = 3) + geom_point()

How to smartly place text labels beside points of different sizes in ggplot2?

I am trying to make a labeled bubble plot with ggplot2 in R. Here is the simplified scenario:
I have a data frame with 4 variables: 3 quantitative variables, x, y, and z, and another variable that labels the points, lab.
I want to make a scatter plot, where the position is determined by x and y, and the size of the points is determined by z. I then want to place text labels beside the points (say, to the right of the point) without overlapping the text on top of the point.
If the points did not vary in size, I could try to simply modify the aesthetic of the geom_text layer by adding a scaling constant (e.g. aes(x=x+1, y=y+1)). However, even in this simple case, I am having a problem with positioning the text correctly because the points do not scale with the output dimensions of the plot. In other words, the size of the points remains constant in a 500x500 plot and a 1000x1000 plot - they do not scale up with the dimensions of the outputted plot.
Therefore, I think I have to scale the position of the label by the size (e.g. dimensions) of the output plot, or I have to get the radius of the points from ggplot somehow and shift my text labels. Is there a way to do this in ggplot2?
Here is some code:
# Stupid data
df <- data.frame(x=c(1,2,3),
y=c(1,2,3),
z=c(1,2,1),
lab=c("a","b","c"), stringsAsFactors=FALSE)
# Plot with bad label placement
ggplot(aes(x=x, y=y), data=df) +
geom_point(aes(size=z)) +
geom_text(aes(label=lab),
colour="red") +
scale_size_continuous(range=c(5, 50), guide="none")
EDIT: I should mention, I tried hjust and vjust inside of geom_text, but it does not produce the desired effect.
# Trying hjust and vjust, but it doesn't look nice
ggplot(aes(x=x, y=y), data=df) +
geom_point(aes(size=z)) +
geom_text(aes(label=lab), hjust=0, vjust=0.5,
colour="red") +
scale_size_continuous(range=c(5, 50), guide="none")
EDIT: I managed to get something that works for now, thanks to Henrik and shujaa. I will leave the question open just in case someone shares a more general solution.
Just a blurb of what I am using this for: I am plotting a map, and indicating the amount of precipitation at certain stations with a point that is sized proportionally to the amount of precipitation observed. I wanted to add a station label beside each point in an aesthetically pleasing manner. I will be making more of these plots for different regions, and my output plot may have a different resolution or scale (e.g. due to different projections) for each plot, so a general solution is desired. I might try my hand at creating a custom position_jitter, like baptiste suggested, if I have time during the weekend.
It appears that position_*** don't have access to the scales used by other layers, so it's a no go. You could make a clone of GeomText that shifts the labels according to the size mapped,
but it's a lot of effort for a very kludgy and fragile solution,
geom_shiftedtext <- function (mapping = NULL, data = NULL, stat = "identity",
position = "identity",
parse = FALSE, ...) {
GeomShiftedtext$new(mapping = mapping, data = data, stat = stat, position = position,
parse = parse, ...)
}
require(proto)
GeomShiftedtext <- proto(ggplot2:::GeomText, {
objname <- "shiftedtext"
draw <- function(., data, scales, coordinates, ..., parse = FALSE, na.rm = FALSE) {
data <- remove_missing(data, na.rm,
c("x", "y", "label"), name = "geom_shiftedtext")
lab <- data$label
if (parse) {
lab <- parse(text = lab)
}
with(coord_transform(coordinates, data, scales),
textGrob(lab, unit(x, "native") + unit(0.375* size, "mm"),
unit(y, "native"),
hjust=hjust, vjust=vjust, rot=angle,
gp = gpar(col = alpha(colour, alpha),
fontfamily = family, fontface = fontface, lineheight = lineheight))
)
}
})
df <- data.frame(x=c(1,2,3),
y=c(1,2,3),
z=c(1.2,2,1),
lab=c("a","b","c"), stringsAsFactors=FALSE)
ggplot(aes(x=x, y=y), data=df) +
geom_point(aes(size=z), shape=1) +
geom_shiftedtext(aes(label=lab, size=z),
hjust=0, colour="red") +
scale_size_continuous(range=c(5, 100), guide="none")
This isn't a very general solution, because you'll need to tweak it every time, but you should be able to add to the x value for the text some value that's linear depending on z.
I had luck with
ggplot(aes(x=x, y=y), data=df) +
geom_point(aes(size=z)) +
geom_text(aes(label=lab, x = x + .06 + .14 * (z - min(z))),
colour="red") +
scale_size_continuous(range=c(5, 50), guide="none")
but, as the font size depends on your window size, you would need to decide on your output size and tweak accordingly. I started with x = x + .05 + 0 * (z-min(z)) and calibrated the intercept based on the smallest point, then when I was happy with that I adjusted the linear term for the biggest point.
Another alternative. Looks OK with your test data, but you need to check how general it is.
dodge <- abs(scale(df$z))/4
ggplot(data = df, aes(x = x, y = y)) +
geom_point(aes(size = z)) +
geom_text(aes(x = x + dodge), label = df$lab, colour = "red") +
scale_size_continuous(range = c(5, 50), guide = "none")
Update
Just tried position_jitter, but the width argument only takes one value, so right now I am not sure how useful that function would be. But I would be happy to find that I am wrong. Example with another small data set:
df3 <- mtcars[1:10, ]
ggplot(data = df3, aes(x = wt, y = mpg)) +
geom_point(aes(size = qsec), alpha = 0.1) +
geom_text(label = df3$carb, position = position_jitter(width = 0.1, height = 0)) +
scale_size_continuous(range = c(5, 50), guide = "none")

Resources