Using unicode characters as shape - r

I'd like to use unicode characters as the shape of plots in ggplot, but for unknown reason they're not rendering. I did find a similar query here, but I can't make the example there work either.
Any clues as to why?
Note that I don't want to use the unicode character as a "palette", I want each item plotted by geom_point() to be the same shape (color will indicate the relevant variable).
Running
Sys.setenv(LANG = "en_US.UTF-8")
and restarting R does not help. Wrapping the unicode in sprintf() also does not help.
This is an example bit of code that illustrates the problem:
library(tidyverse)
library(ggplot2)
library(Unicode)
p1 = ggplot(mtcars, aes(wt, mpg)) +
geom_point(shape="\u25D2", colour="red", size=3) +
geom_point(shape="\u25D3", colour="blue", size=3) +
theme_bw()
plot(p1)
And here's what that renders result.
I use macOS Sierra (10.13.6), R version 3.5.1 & Rstudio 1.0.143.
Grateful for any help! I've been scouting several forums looking for a solution and posted to #Rstats, so far nothing has worked. It may be that the solution is hidden in some thread somewhere, but if so I have failed to detect it and I suspect others have also missed it. So, here I am making my first ever post to stack overflow :)

Might it work to use geom_text instead? It allows control of the font, so you can select one with the glyph you need.
library(tidyverse)
ggplot(mtcars, aes(wt, mpg)) +
geom_text(label = "\u25D2", aes(color = as.character(gear)),
size=10, family = "Arial Unicode MS") +
geom_text(label = "\u25D3", colour="blue",
size=10, family = "Arial Unicode MS") +
scale_color_discrete(name = "gear") +
theme_bw()

It's possible to change the font family using par. The problem is that this will affect base R graphics but not ggplot2 graphics, as they use two different graphics devices (grDevices vs. grid). For instance, we can try to plot your example using base R functions, but at first we see the same issue:
plot(mtcars$wt, mtcars$mpg, pch="\u25D2", col = "red", cex = 2)
points(mtcars$wt, mtcars$mpg, pch="\u25D3", col = "blue", cex = 2)
We can get what we want if we call par first (the font should support the symbols):
par(family = "Arial Unicode MS")
plot(mtcars$wt, mtcars$mpg, pch="\u25D2", col = "red", cex = 2)
points(mtcars$wt, mtcars$mpg, pch="\u25D3", col = "blue", cex = 2)
Changing the font family parameter that specifically affects the points in a ggplot geom_point appears to be a bit more complicated. As far as I can tell, it would involve turning the ggplot object into a grob, editing the parameters, and then drawing it. It probably makes more sense to either use Jon Spring's geom_text solution, or use base R.

Related

Extra spaces on labels including cyrillic symbols in ggplot2

I'm trying to make a simple plot in R using ggplot2. The data is stored in a dataframe with its column written in Russian. The problem is that the contents of the label is shifted from the right border of the latter. These extra spaces appeared whether the label names are defined explicitly (the code below) or implicitly from the dataframe column names.
ggplot(mtcars, aes(x = drat, y =mpg, color = cyl))+
geom_point() +
labs(color = "Русское название") +
theme(legend.background = element_rect(color = "black", linetype = "solid", size = 0.7),
legend.justification = c(1, 1),
legend.position = c(1, 1),
legend.title.align = 0)
The plot with the English title is depicted appropriately.
The encoding of the operational system is set as follows:
"LC_COLLATE=Russian_Russia.1251;LC_CTYPE=Russian_Russia.1251;LC_MONETARY=Russian_Russia.1251;LC_NUMERIC=C;LC_TIME=Russian_Russia.1251"
Is there a way to cope this problem?
I have faced the same issue when needed legend with cyrillic symbols. Looks like it is known issue reported in ggplot2 repo because of the default graphic device on Windows. Actually, if you would try to save your ggplot with ggsave, probably you won't get that issue.
I have not tried to save files by myself yet, but I have followed the reported issue and found some workaround specific for R studio here and it has worked for me, maybe it can solve your issue too. Sample code to run before plotting anything is:
trace(grDevices:::png, quote({
if (missing(type) && missing(antialias)) {
type <- "cairo-png"
antialias <- "subpixel"
}
}), print = FALSE)

ggplot shape = Unicode (e.g. arrows like "\u2198" or LaTeX \searrow)?

I'd like to use Unicode shapes in ggplot2 geom_point() (specifically, arrows like ↘, Unicode "\u2198", or LaTeX \searrow), as in shape = "\u2198", that are not in the default font. In this unanswered post, #Laserhedvig commented "it seems that the problem lies in the font. Apparently, the base default fonts don't contain support for these specific glyphs. Now, how to change the font for the shape argument of geom_point()?"
This solution for Unicode in axes.text uses theme(axis.text.x = element_text(family = "FreeSerif")), and this solution uses theme(text=element_text(size=16, family="Comic Sans MS")) for all text, but how can I do this for shape?
Is there a general solution to use Unicode for shape? (must I somehow use cairo and/or a font family argument?)
If not, is there some other set of arrow shapes? (My search for arrow shapes and glyphs, including in the scale_shape documentation came up empty.)
In my case, I need a ggplot2 layer showing qualitative predictions for the direction of change at points in time across discrete categories.
An example:
library(dplyr)
library(ggplot2)
d <- tibble(year = c(1, 1, 2, 2),
policy = rep( c('policy 1', 'policy 2'), 2),
prediction = c(NA, 'increase', 'decrease', NA),
predictionUnicode = c(NA, '\u2197', '\u2198', NA))
ggplot(d) +
geom_point(aes(x = year, y = policy, color = prediction), shape = "\u2198")
shape = "\u2198" (i.e. "↘") does not work
Edit: Thanks to djangodude's comment about ggplot's font usage, I found the family argument of geom_text, which allows different fonts. Thus, Unicode "shapes" can be plotted as characters with geom_text. However, the legend for geom_text is fixed to "a". And themes only control non-data display, so the base_family argument will not work for shape.
ggplot(d) +
geom_tile( aes(x = year, y = policy), color = "black", fill = "white") +
# geom_point does not allow new fonts?
geom_point(aes(x = year, y = policy,
color = prediction), shape = "\u2198") +
# geom_text does allow new fonts, but the legend text is fixed to "a"
geom_text(aes(x = year, y= policy,
color = prediction,
label = predictionUnicode),
family = "Calibri") +
scale_x_continuous(breaks = c(1,2)) +
theme_gray(base_family = "Calibri")
geom_text plots unicode, but not in the legend
It seems the shape argument is really the correct way to do this, right?
I tried setting Sys.setenv(LANG = "en_US.UTF-8") and Sys.setenv(LANG = "Unicode") to no effect, but perhaps some global language setting would affect shape?
Thank you so much for any help!
Note: These solutions for Unicode skull and crossbones and half-filled points do not have legends and will not work without the right font:
To get the right font:
Look for an installed font that contains the Unicode character you seek. I found these instructions helpful.
Import installed fonts into R
library(extrafont)
font_import()
fonts()
sessionInfo()
R version 3.5.2 (2018-12-20)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.3
Use shape="\u2198" instead of \u8600. When specifying a character with \u notation, the value needs to be hexadecimal. 8600 is the decimal value for Unicode character LOWER RIGHT ARROW; the hex value is 2198.

Change default dot size of geom_point() in ggplot2?

At some release, the dots of geom_point became bigger. It might be 2.0: "geom_point() now uses shape 19 instead of 16."
How can I make the default dot from geom_point smaller like it used to be?
Edit: How do I change ALL plots without adding code to every plot? That is, the default. I looked in get_theme() and didn't see anything about points.
refer http://ggplot2.tidyverse.org/reference/geom_point.html
ggplot(mtcars, aes(disp, hp)) + geom_point(shape = 16)
For updating default ggplot geom parameters:
update_geom_defaults("point", list(shape = 16))

Independent strip themes for ggplot2 facet_wrap using more than one variable

Is it possible to have independent strip themes for each variable used in ggplot2 facet_wrap?
Take this chunk of code as an example:
p1 <- ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(c("cyl", "drv"), labeller = "label_both")
plot(p1)
I would want to have upper strip ('cyl') with a different theme - say, in bold. Additionally, I might want to make 'drv' italic and with a different font type and size. How could I do that?
I was thinking something in the lines of:
p1 <- p1 + theme(strip.text.variable1 = element_text(face = 'bold'),
strip.text.variable2 = element_text(face = 'italic', size = 8)
)
Unfortunately, I couldn't find anything like this in the docs or previous questions.
Cheers
Edit: I made the question a bit more general to be of further help to the community.
Ostensibly you should be able to make a new function based on label_both to return bold labels, but so far my attempts have ended with the dreaded Error in variable[[i]] : subscript out of bounds.
An alternative to this is to build a function to make the label you want. This is much like this answer. In this function you add the prefix to the values of the variable and make them bold.
make_labels = function(string, prefix = "cyl: ") {
x = paste0(prefix, as.character(string))
do.call(expression, lapply(x, function(y) bquote(bold(.(y)))))
}
Now use this function within as_labeller for the "cyl" variable in facet_wrap. You want to change the default labeller within as_labeller to label_parsed so the expression is parsed correctly. Use label_both for the other variable.
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(c("cyl", "drv"),
labeller = labeller(cyl = as_labeller(make_labels, default = label_parsed),
drv = label_both))

Gradient of n colors ranging from color 1 and color 2

I often work with ggplot2 that makes gradients nice (click here for an example). I have a need to work in base and I think scales can be used there to create color gradients as well but I'm severely off the mark on how. The basic goal is generate a palette of n colors that ranges from x color to y color. The solution needs to work in base though. This was a starting point but there's no place to input an n.
scale_colour_gradientn(colours=c("red", "blue"))
I am well aware of:
brewer.pal(8, "Spectral")
from RColorBrewer. I'm looking more for the approach similar to how ggplot2 handles gradients that says I have these two colors and I want 15 colors along the way. How can I do that?
colorRampPalette could be your friend here:
colfunc <- colorRampPalette(c("black", "white"))
colfunc(10)
# [1] "#000000" "#1C1C1C" "#383838" "#555555" "#717171" "#8D8D8D" "#AAAAAA"
# [8] "#C6C6C6" "#E2E2E2" "#FFFFFF"
And just to show it works:
plot(rep(1,10),col=colfunc(10),pch=19,cex=3)
Just to expand on the previous answer colorRampPalettecan handle more than two colors.
So for a more expanded "heat map" type look you can....
colfunc<-colorRampPalette(c("red","yellow","springgreen","royalblue"))
plot(rep(1,50),col=(colfunc(50)), pch=19,cex=2)
The resulting image:
Try the following:
color.gradient <- function(x, colors=c("red","yellow","green"), colsteps=100) {
return( colorRampPalette(colors) (colsteps) [ findInterval(x, seq(min(x),max(x), length.out=colsteps)) ] )
}
x <- c((1:100)^2, (100:1)^2)
plot(x,col=color.gradient(x), pch=19,cex=2)
Edit
Let me try to explain why I think this function is superior to the other suggested solutions.
Let's apply the function suggested by jsol for the exponential data I used for my plot. I try two variations using range and length in the call to colfunc.
Result: It simply does not work as intended.
colfunc <- colorRampPalette(c("red","yellow","springgreen","royalblue"))
x <- c((1:100)^2, (100:1)^2)
plot(x, col=colfunc(range(x)), pch=19,cex=2)
plot(x, col=colfunc(length(x)), pch=19,cex=2)
The above answer is useful but in graphs, it is difficult to distinguish between darker gradients of black. One alternative I found is to use gradients of gray colors as follows
palette(gray.colors(10, 0.9, 0.4))
plot(rep(1,10),col=1:10,pch=19,cex=3))
More info on gray scale here.
Added
When I used the code above for different colours like blue and black, the gradients were not that clear.
heat.colors() seems more useful.
This document has more detailed information and options. pdf
An alternative approach (not necessarily better than the previous answers!) is to use the viridis package. As explained here, it allows for a variety of color gradients that are based on more than two colors.
The package is pretty easy to use - you just need to replace the ggplot2 scale fill function (e.g., scale_fill_gradient(low = "skyblue", high = "dodgerblue4")) with the equivalent viridis function.
So, change the code for this plot:
ggplot(mtcars, aes(wt*1000, mpg)) +
geom_point(size = 4, aes(colour = hp)) +
xlab("Weight (pounds)") + ylab("Miles per gallon (MPG)") + labs(color='Horse power') +
scale_x_continuous(limits = c(1000, 6000),
breaks = c(seq(1000,6000,1000)),
labels = c("1,000", "2,000", "3,000", "4,000", "5,000", "6,000")) +
scale_fill_gradient(low = "skyblue", high = "dodgerblue4") +
theme_classic()
Which produces:
To this, which uses viridis:
ggplot(mtcars, aes(wt*1000, mpg)) +
geom_point(size = 4, aes(colour = factor(cyl))) +
xlab("Weight (pounds)") + ylab("Miles per gallon (MPG)") + labs(color='Number\nof cylinders') +
scale_x_continuous(limits = c(1000, 6000),
breaks = c(seq(1000,6000,1000)),
labels = c("1,000", "2,000", "3,000", "4,000", "5,000", "6,000")) +
scale_color_viridis(discrete = TRUE) +
theme_classic()
The only difference is in the second to last line: scale_color_viridis(discrete = TRUE).
This is the plot that is produced using viridis:
Hoping someone finds this useful, as its the solution I ended up using after coming to this question.

Resources