White-line artifacts in ggplot2 plots - r

(Edit: now submitted as ggplot2 issue #4183.)
I'm not sure exactly what conditions cause this to happen, but here's a pretty small example derived from the real plot that I was working on when I ran into this:
library(ggplot2)
d = data.frame(x = seq(0, 2, len = 2500))
d$y = d$x^2
ggsave("~/plot.png", width = 3, height = 4, dpi = 90,
ggplot(d) +
geom_col(aes(x, y), width = max(diff(d$x))) +
theme_bw() +
theme(
panel.border = element_blank(),
panel.grid = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank()))
With ggplot2 3.3.2 on R 3.6.2 on Linux, I get this:
How do I reliably avoid getting vertical white lines like the one at about x = 0.8 above?

ggplot has decreed this a won't-fix, but it can be worked around by increasing the width with something like geom_col(aes(x, y), width = max(diff(d$x)) * 1.05, position = "identity"):

Related

Breaking change in ggsave() in 3.3.6 update: cannot reproduce old plots

The problem
I have some code that creates a map with numerous points, annotated with some stats, on a monthly basis. This worked fine until I updated ggplot2 to 3.3.6, after which the plots have broken and I've not been able to figure out the solution. As far as I can tell, the issue lies in the ggsave() call, but I don't know what has changed and how to fix it.
There are two main issues that have come up. But to illustrate, I will attach below comparison images of the "good" and "bad" versions (click links to see full-size).
Good points
Bad points
The good version has uniformly coloured points/squares while the latter has strange points coloured irregularly.
Good text
Bad text
The non-breaking space is formatted properly in the good version, while it appears as a box in the bad version.
Debugging attempts
One potential cause I noted for the irregular points was some updates in the works for the "size" parameter (see this blog post). Such things have happened in the past as well (see this for example). However, this update is claimed to be for the next release, and moreover like I said, I have a hunch the issue I'm facing has something to do with ggsave(). And regardless, I already tried tweaking the size and stroke of the geom_point() but haven't been able to recover the old version properly.
The RStudio plot device doesn't indicate any issue with the points, and using the png() method instead of ggsave() to write produces the correct/"good" version.
png(filename = "map_cov_plain.png",
units = "in", width = 8, height = 11, bg = "transparent", res = 300)
print(map_cov_plain)
dev.off()
I tried reverting to ggplot 3.3.5 but this did not fix the issue. Moreover, two others tried the same code on their separate systems, both with ggplot 3.3.6, but only one replicated my issue while the other produced the good version. Nevertheless, the code was certainly working fine until July, only after which I updated several packages and the code broke.
For the record, I have ensured that the issue is not with the data. So, although I have used data until June to illustrate the good version, that same dataset generates the bad maps when the code is run now (i.e., after the updates).
I am hoping someone with a better understanding of the package and the update will be able to figure out what exactly the breaking change was!
Other links
ggsave() doesn't render custom fonts when saving (+workaround)
I also posted this as an issue in the ggplot2 repo; apologies for cross-posting, but wasn't sure which one was more appropriate, and felt a SO post might still be useful for future users with the same problem.
Reprex
There are two files required for the reprex below to work:
.RData file with the necessary data objects (link)
logo image required in one of the maps (link)
library(lubridate)
library(tidyverse)
library(glue)
library(magick)
library(scales) # for comma format of numbers
library(grid)
# loading objects
load("reprex.RData")
map_cov_logo <- image_convert(image_read("bcilogo-framed.png"), matte = T)
map_cov_text <- glue::glue("{label_comma()(data_cov$LOCATIONS)} locations
{label_comma()(data_cov$LISTS)} lists
{label_comma()(data_cov$HOURS)} hours
{label_comma()(data_cov$PEOPLE)} people
{label_comma()(data_cov$STATES)} states/UTs
{label_comma()(data_cov$DISTRICTS)} districts
{label_comma()(data_cov$SPECIES)} species
{round(data_cov$OBSERVATIONS, 1)} million observations")
map_cov_footer <- glue::glue("Data until September 2022")
### map with annotations of stats and BCI logo ###
map_cov_annot <- ggplot() +
geom_polygon(data = indiamap, aes(x = long, y = lat, group = group),
colour = NA, fill = "black")+
geom_point(data = data_loc, aes(x = LONGITUDE, y = LATITUDE),
colour = "#fcfa53", size = 0.05, stroke = 0) +
# scale_x_continuous(expand = c(0,0)) +
# scale_y_continuous(expand = c(0,0)) +
theme_bw() +
theme(axis.line = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
# panel.border = element_blank(),
plot.background = element_rect(fill = "black", colour = NA),
panel.background = element_rect(fill = "black", colour = NA),
plot.title = element_text(hjust = 0.5)) +
coord_cartesian(clip = "off") +
theme(plot.margin = unit(c(2,2,0,23), "lines")) +
annotation_raster(map_cov_logo,
ymin = 4.5, ymax = 6.5,
xmin = 46.5, xmax = 53.1) +
annotation_custom(textGrob(label = map_cov_text,
hjust = 0,
gp = gpar(col = "#FCFA53", cex = 1.5)),
ymin = 19, ymax = 31,
xmin = 40, xmax = 53) +
annotation_custom(textGrob(label = map_cov_footer,
hjust = 0,
gp = gpar(col = "#D2D5DA", cex = 1.0)),
ymin = 15, ymax = 16,
xmin = 40, xmax = 53)
ggsave(map_cov_annot, file = "map_cov_annot.png", device = "png",
units = "in", width = 13, height = 9, bg = "transparent", dpi = 300)
### plain map without annotations ###
map_cov_plain <- ggplot() +
geom_polygon(data = indiamap, aes(x = long, y = lat, group = group),
colour = NA, fill = "black")+
geom_point(data = data_loc, aes(x = LONGITUDE, y = LATITUDE),
colour = "#fcfa53", size = 0.05, stroke = 0.1) +
# scale_x_continuous(expand = c(0,0)) +
# scale_y_continuous(expand = c(0,0)) +
theme_bw() +
theme(axis.line = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
plot.margin = unit(c(0, 0, 0, 0), "cm"),
# panel.border = element_blank(),
plot.background = element_rect(fill = "black", colour = NA),
panel.background = element_rect(fill = "black", colour = NA),
plot.title = element_text(hjust = 0.5)) +
coord_map()
ggsave(map_cov_plain, file = "map_cov_plain.png", device = "png",
units = "in", width = 8, height = 11, bg = "transparent", dpi = 300)
I think this comes down to taste, and you prefer the result of the rendering without anti-aliasing. It looks bolder and sharper because the cells that are partially activated are shown at full brightness, in contrast with fully black adjacent pixels. Mssr. Pederson argues that the anti-aliased version is in some respects a truer depiction of the underlying data, since it will perceptually give the dots more consistent weight and spacing that corresponds to their actual size and placement.
Two examples:
set.seed(42)
library(ggplot2)
df_rand <- data.frame(x = runif(1000), y = runif(1000))
ggplot(df_rand, aes(x,y)) +
geom_point(colour = "#fcfa53", size = 0.01, stroke = 0) +
theme_void() +
theme(plot.background = element_rect(fill = "black", colour = NA),
panel.background = element_rect(fill = "black", colour = NA))
ggsave("rand_no_anti-alias.png", device = png,
units = "px", width = 100, height = 100)
ggsave("rand_ragg_anti-alias.png", device = ragg::agg_png(),
units = "px", width = 100, height = 100)
Of these two, you prefer the former for your use case and particular settings.
df_grid <- data.frame(expand.grid(x = 1:30, y = 1:30))
ggplot(df_grid, aes(x,y)) +
geom_point(colour = "#fcfa53", size = 0.01, stroke = 0) +
theme_void() +
theme(plot.background = element_rect(fill = "black", colour = NA),
panel.background = element_rect(fill = "black", colour = NA))
ggsave("grid_no_anti-alias.png", device = png,
units = "px", width = 100, height = 100)
ggsave("grid_ragg_anti-alias.png", device = ragg::agg_png(),
units = "px", width = 100, height = 100)
Note here that the version without anti-aliasing creates phantom groupings out of a uniform grid. Its algorithm shows full intensity even when the point covers a tiny part of the pixel. The anti-aliased version reflects that the points cover a very small part of each pixel, and it attempts to even out the error by sometimes depicting a point in one pixel and sometimes using two adjacent pixels, but dimmer. While the uniform data creates some moire effect in this contrived example, in my view the perceptual distortion is smaller.
If I make the point size much larger (e.g. 0.3), it makes for a closer match in brightness with the non-anti-aliased version. This version avoids the phantom groupings that the non-anti-aliased version has, but at the cost of the pixels looking smudged at the pixel level. That's anti-aliasing for ya.
Technical arguments aside, use the rendering method that gives you the output you want.

R ggplot color bin widths appear unequal

Hello I am ploting a graph which will have the legend,width of legend items not same can any one help me to resolve my problem
data file
< https://drive.google.com/file/d/1EKhRwup3vUC3KVFOOh4XtKERIr8FQj3x/view?usp=sharing>
code I have used
df=read.table("test.txt",sep='\t', header=TRUE)
df = data.frame(df)
nCol <- 15
myCol <- viridis(n = nCol)
myCol
ggplot(df, aes(log(data1), log(data2),color=data3),cex=1.9)+
geom_point() +
scale_colour_stepsn(colours = c(myCol),breaks = seq(0,100,by=10))+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))+
theme(text = element_text(size = 12, face="bold"),
legend.text = element_text(size = 7, face="bold"), legend.position="top",
legend.key.size = unit(10, "mm"),
legend.key.width=unit(10, "mm"))
at the end of legend 90-100 width is high compare to others
There are a couple of asides before addressing your main concern:
You can simplify your code by using scale_color_binned(type = "viridis"), which gives the same result as creating a vector of
viridis colors and interpolating them as you are doing, with the added advantage that you don't need to load the viridis library.
You can use log scales on the x and y axis without having to transform your
data, by using scale_x_log10() and scale_y_log10()
You can simplify your theme call by first calling theme_classic(), which gets rid of the need to specify all those element_blank() components.
For the specific problem that you encountered though, the reason for the appearance is that breaks are for the "internal" breaks of the binned scale. The outer edges of your scale are not breaks, but limits. By default, limits are not shown on binned scales, but you can turn them on using show.limits = TRUE after setting the limits to the desired value of c(0, 100)
So your above code can be rewritten as:
df <- read.table("test.txt", sep = "\t", header = TRUE)
ggplot(df, aes(log(data1), log(data2), color = data3), cex = 1.9) +
geom_point() +
scale_colour_binned(type = "viridis",
breaks = 1:9 * 10,
limits = c(0, 100),
show.limits = TRUE,
labels = function(x) round(x)) +
theme_classic() +
theme(text = element_text(size = 12, face = "bold"),
legend.text = element_text(size = 7, face = "bold"),
legend.position = "top",
legend.key.size = unit(10, "mm"))

R ggplot2 hexbin plot graphics bug?

I always spot a handful of strange white horizontal dotted lines that seem to appear on the borders between vertically adjacent hexagons on every hexbin density plot that I create using ggplot2 in the R environment. They certainly do not belong there... This behavior (bug) is reproducible on several R installations on different computers with different MS Windows versions and different graphics drivers. I don't have a clue on how to get rid of these dotted lines.
Below is a plot showing the problem, and the code that produced the plot.
Is this a known problem? Any hint to overcome this? Thanks a bunch for any help!
## Load libraries
library(ggplot2)
library(hexbin)
## Create some data
set.seed(222)
myx <- data.frame(x = 0.4 + rnorm(10000, mean = 0, sd = 0.1))
myy <- data.frame(y = 0.5 + rnorm(10000, mean = 0, sd = 0.1))
MyData <- data.frame(xvariable=myx$x*100, yvariable=myy$y*100)
## Prepare the plot window
dev.off(2)
windows.options(width=6, height=6)
op <- par(mfrow = c(1,1))
op <- par(oma = c(0,0,0,0) + 0.1,
mar = c(5.1, 5.1, 4.1, 2.1))
## Creaate the plot
ggplot(MyData, aes(x=xvariable, y=yvariable) ) +
geom_hex(bins=66) +
xlim(0, 100) +
ylim(0, 100) +
ggtitle("My plot") +
scale_fill_continuous(type = "viridis") +
theme_bw() +
theme(plot.title = element_text(color="black", size=17, face="bold"),
axis.title.x = element_text(color="black", size=17, face="bold"),
axis.title.y = element_text(color="black", size=17, face="bold"),
axis.text=element_text(color="black", size=15, face="bold"),
legend.title = element_text(color = "black", size = 15),
legend.text = element_text(color = "black", size=12),
legend.position = c(0.9,0.2), legend.direction = "vertical")
Looks like if you save as pdf and then convert that to image, there will be no line.
I have this issue too, RStudio version 1.4.1717 "Juliet Rose" (df86b69e, 2021-05-24) for Windows. Looks like white lines going across the 'Plots' tab if I use geom_hex(). If I resize the output window by a pixel at a time (vertically) , the lines will disappear.
Maybe there's too many hex bins to begin with and the resolution causes gaps every so often.
before
after

ggplot2 x - y axis intersect while keeping axis labels

I posted my original question yesterday which got solved perfectly here
Original post
I made a few addition to my code
library(lubridate)
library(ggplot2)
library(grid)
### Set up dummy data.
dayVec <- seq(ymd('2016-01-01'), ymd('2016-01-10'), by = '1 day')
dayCount <- length(dayVec)
dayValVec1 <- c(0,-0.22,0.15,0.3,0.4,0.10,0.17,0.22,0.50,0.89)
dayValVec2 <- c(0,0.2,-0.17,0.6,0.16,0.41,0.55,0.80,0.90,1.00)
dayValVec3 <- dayValVec2
dayDF <- data.frame(Date = rep(dayVec, 3),
DataType = factor(c(rep('A', dayCount), rep('B', dayCount), rep('C', dayCount))),
Value = c(dayValVec1, dayValVec2, dayValVec3))
ggplot(dayDF, aes(Date, Value, colour = DataType)) +
theme_bw() +
ggtitle("Cumulative Returns \n") +
scale_color_manual("",values = c("#033563", "#E1E2D2", "#4C633C"),
labels = c("Portfolio ", "Index ", "In-Sample ")) +
geom_rect(aes(xmin = ymd('2016-01-01'),
xmax = ymd('2016-01-06'),
ymin = -Inf,
ymax = Inf
), fill = "#E1E2D2", alpha = 0.03, colour = "#E1E2D2") +
geom_line(size = 2) +
scale_x_datetime(labels = date_format('%b-%d'),
breaks = date_breaks('1 day'),
expand = c(0,0)) +
scale_y_continuous( expand = c(0,0), labels = percent) +
theme(axis.text.x = element_text(angle = 90),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank(),
axis.line = element_line(size = 1),
axis.ticks = element_line(size = 1),
axis.text = element_text(size = 20, colour = "#033563"),
axis.title.x = element_text(hjust = 2),
plot.title = element_text(size = 40, face = "bold", colour = "#033563"),
legend.position = 'bottom',
legend.text = element_text(colour = "#033563", size = 20),
legend.key = element_blank()
)
which produces this output
The only thing that I still cannot get working is the position of the x axis. I want the x axis to be at y = 0 but still keep the x axis labels under the chart, exactly as in the excel version of it. I know the data sets are not the same but I didn't have the original data at hand so I produced some dummy data. Hope this was worth a new question, thanks.
> grid.ls(grid.force())
GRID.gTableParent.12660
background.1-5-7-1
spacer.4-3-4-3
panel.3-4-3-4
grill.gTree.12619
panel.background.rect.12613
panel.grid.minor.y.zeroGrob.12614
panel.grid.minor.x.zeroGrob.12615
panel.grid.major.y.polyline.12617
panel.grid.major.x.zeroGrob.12618
geom_rect.rect.12607
GRID.polyline.12608
panel.border.rect.12610
axis-l.3-3-3-3
axis.line.y.polyline.12631
axis
axis-b.4-4-4-4
axis.line.x.polyline.12624
axis
xlab.5-4-5-4
ylab.3-2-3-2
guide-box.6-4-6-4
title.2-4-2-4
> grid.gget("axis.1-1-1-1", grep=T)
NULL
ggplot2 doesn't make this easy. Below is one-way to approach this interactively. Basically, you just grab the relevant part of the plot (the axis line and ticks) and reposition them.
If p is your plot
p
grid.force()
# grab the relevant parts - have a look at grid.ls()
tck <- grid.gget("axis.1-1-1-1", grep=T)[[2]] # tick marks
ax <- grid.gget("axis.line.x", grep=T) # x-axis line
# add them to the plot, this time suppressing the x-axis at its default position
p + lapply(list(ax, tck), annotation_custom, ymax=0) +
theme(axis.line.x=element_blank(),
axis.ticks.x=element_blank())
Which produces
A quick note: the more recent versions of ggplot2 have the design decision to not show the axis. Also changes to axis.line are not automatically passed down to the x and y axis. Therefore, I tweaked your theme to define axis.line.x and axis.line.y separately.
That siad, perhaps its easier (and more robust??) to use geom_hline as suggested in the comments, and geom_segment for the ticks.

How to change default font size in R chart

I am using the R package cooccur and cannot figure out how to change the font size in the associated graphics. The par() method does not seem to work.
Here is the example given by the package:
data(finches)
cooccur.finches <- cooccur(mat=finches,
type="spp_site",
thresh=TRUE,
spp_names=TRUE)
plot(cooccur.finches)
I am trying to change the font size of the species, the title and the legend to no avail on the heat map that is produced. Any help would be MUCH appreciated. Thanks!
Unfortunately the author didn't use defined theme inside the function, so if you want to not mess up the other customizations in place, this should work:
p <- plot(cooccur.finches)
p + theme_bw(base_size = 28) +
theme(axis.text = element_blank(),
axis.ticks = element_blank(),
plot.title = element_text(vjust = -4, face = "bold"),
panel.background = element_rect(fill = "white", colour = "white"),
panel.grid = element_blank()
legend.position = c(0.9, 0.5))
You can also use this code to set the size of the legend or title independently, e.g.
p + theme(plot.title = element_text(vjust = -4, face = "bold", size = 36))
Most unfortunately, this won't change the size of the species labels because they are set with geom_text(). To alter them, you'll have to hack the function yourself cooccur:::plot.cooccur. You only need to modify the last line:
p + geom_text(data = dfids, aes(label = X1), hjust = 1, vjust = 0,
angle = -22.5)
# change to
p + geom_text(data = dfids, aes(label = X1), hjust = 1, vjust = 0,
angle = -22.5, size = 24)
It is a ggplot2 plot not a base one. So par will not work.
p <- plot(cooccur.finches)
p + theme(text = element_text(size = 10)) ## change text font size
or
p + theme_grey(base_size = 18) ## chnage all font size.
Author of Cooccur here. Sorry for the hassle with the text sizes being hard to adjust. I will deal with this when I get a chance.
Not a permanent solution, but easier than changing the function each time for the species labels, is to just directly re-assign the value in the ggplot object:
p$layers[[2]]$geom_params$size <- 10
Hope that helps. I might be a bit late to the scene...

Resources