Skip to content

Is it possible to get a downward pointing solid triangle plotting symbol in R?

9 messages · Jan van der Laan, Rui Barradas, Jeff Newmiller +1 more

#
The reason I am asking is that I would like to mark areas on a plot 
using geom_polygon() and aes(fill = variable) to fill various polygons 
forming the background of a plot with different colours. Then I would 
like to overlay that with points representing direction of change: 
improved, no reliable change, deteriorated. The obvious symbols to use 
for those three directions are an upward arrow, a circle or square and a 
downward pointing arrow.? There is a solid upward point triangle symbol 
in R (ph = 17) and there are both upward and downward pointing open 
triangle symbols (pch 21 and 25) but to fill those with a solid colour 
so they will be visible over the background requires that I use a fill 
aesthetic and that gets me a mess with the legend as I will have used a 
different fill mapping to fill the polygons.? This silly reprex shows 
the issue I think.

library(tidyverse)
tibble(x = 2:9, y = 2:9, c = c(rep("A", 5), rep("B", 3))) -> tmpTibPoints
tibble(x = c(1, 5, 5, 1), y = c(1, 1, 5, 5), a = rep("a", 4)) -> tmpTibArea1
tibble(x = c(5, 10, 10, 5), y = c(1, 1, 5, 5), a = rep("b", 4)) -> 
tmpTibArea2
tibble(x = c(1, 5, 5, 1), y = c(5, 5, 10, 10), a = rep("c", 4)) -> 
tmpTibArea3
tibble(x = c(5, 10, 10, 5), y = c(5, 5, 10, 10), a = rep("d", 4)) -> 
tmpTibArea4
bind_rows(tmpTibArea1,
 ????????? tmpTibArea2,
 ????????? tmpTibArea3,
 ????????? tmpTibArea4) -> tmpTibAreas
ggplot(data = tmpTib,
 ?????? aes(x = x, y = y)) +
 ? geom_polygon(data = tmpTibAreas,
 ?????????????? aes(x = x, y = y, fill = a)) +
 ? geom_point(data = tmpTibPoints,
 ???????????? aes(x = x, y = y, fill = c),
 ???????????? pch = 24,
 ???????????? size = 6)

Does anyone know a way to create a solid downward pointing symbol?? Or 
another workaround?

TIA,

Chris
#
Does adding

, show.legend = c("color"=TRUE, "fill"=FALSE)

to the geom_point do what you want?

Best,
Jan
On 06-10-2023 11:09, Chris Evans via R-help wrote:
#
Sadly, no.? Still shows the same legend with both sets of fill 
mappings.? I have found a workaround, sadly
much longer than yours (!) that does get me what I want but it is a real 
bodge.? Still interested to see
if there is a way to create a downward pointing solid symbol but here is 
my bodge using new_scale_fill()
and new_scale_color() from the ggnewscale package (many thanks to Elio 
Campitelli for that).

library(tidyverse)
library(ggnewscale) # allows me to change the scales used
tibble(x = 2:9, y = 2:9,
 ?????? ### I have used A:C to ensure the changes sort in the correct 
order to avoid the messes of using shape to scale an ordinal variable
 ?????? ### have to say that seems a case where it is perfectly sensible 
to map shapes to an ordinal variable, scale_shape_manual() makes
 ?????? ### this difficult hence this bodge
 ?????? c = c(rep("A", 5), "B", rep("C", 2)),
 ?????? change = c(rep("Deteriorated", 5), "No change", rep("Improved", 
2))) %>%
 ? ### this is just keeping the original coding but not used below
 ? mutate(change = ordered(change,
 ????????????????????????? levels = c("Deteriorated", "No change", 
"Improved"))) -> tmpTibPoints
### create the area mapping
tibble(x = c(1, 5, 5, 1), y = c(1, 1, 5, 5), a = rep("a", 4)) -> tmpTibArea1
tibble(x = c(5, 10, 10, 5), y = c(1, 1, 5, 5), a = rep("b", 4)) -> 
tmpTibArea2
tibble(x = c(1, 5, 5, 1), y = c(5, 5, 10, 10), a = rep("c", 4)) -> 
tmpTibArea3
tibble(x = c(5, 10, 10, 5), y = c(5, 5, 10, 10), a = rep("d", 4)) -> 
tmpTibArea4
bind_rows(tmpTibArea1,
 ????????? tmpTibArea2,
 ????????? tmpTibArea3,
 ????????? tmpTibArea4) -> tmpTibAreas
### now plot
ggplot(data = tmpTib,
 ?????? aes(x = x, y = y)) +
 ? geom_polygon(data = tmpTibAreas,
 ?????????????? aes(x = x, y = y, fill = a),
 ?????????????? alpha = .5) +
 ? scale_fill_manual(name = "Areas",
 ??????????????????? values = c("orange", "purple", "yellow", "brown"),
 ??????????????????? labels = letters[1:4]) +
 ? ### next two lines use ggnewscale functions to reset the scale mappings
 ? new_scale_fill() +
 ? new_scale_colour() +
 ? ### can now use the open triangles and fill aesthetic to map them
 ? geom_point(data = tmpTibPoints,
 ???????????? aes(x = x, y = y, shape = c, fill = c, colour = c),
 ???????????? size = 6) +
 ? ### use the ordered variable c to get mapping in desired order
 ? ### which, sadly, isn't the alphabetical order!
 ? scale_shape_manual(name = "Change",
 ?????????????????? values = c("A" = 24,
 ????????????????????????????? "B" = 23,
 ????????????????????????????? "C" = 25),
 ?????????????????? labels = c("Deteriorated",
 ????????????????????????????? "No change",
 ????????????????????????????? "Improved")) +
 ? scale_colour_manual(name = "Change",
 ?????????????????? values = c("A" = "red",
 ????????????????????????????? "B" = "grey",
 ????????????????????????????? "C" = "green"),
 ?????????????????? labels = c("Deteriorated",
 ????????????????????????????? "No change",
 ????????????????????????????? "Improved")) +
 ? scale_fill_manual(name = "Change",
 ?????????????????? values = c("A" = "red",
 ????????????????????????????? "B" = "grey",
 ????????????????????????????? "C" = "green"),
 ?????????????????? labels = c("Deteriorated",
 ????????????????????????????? "No change",
 ????????????????????????????? "Improved"))

That gives the attached plot which is really what I want.? Long bodge 
though!*
*
On 06/10/2023 11:50, Jan van der Laan wrote:
#
You are right, sorry.

Another possible solution then: use geom_text instead of geom_point and 
use a triangle shape as text:

ggplot(data = tmpTibPoints,
        aes(x = x, y = y)) +
   geom_polygon(data = tmpTibAreas,
                aes(x = x, y = y, fill = a)) +
   geom_text(data = tmpTibPoints,
              aes(x = x, y = y, label = "?", color = c),
              size = 6) + guides(color = FALSE)
On 06-10-2023 12:11, Chris Evans via R-help wrote:
#
Thanks again Jan.? That is lovely and clean and I probably should have 
seen that option.

I had anxieties about the portability of using text.? (The function will 
end up in my
https://github.com/cpsyctc/CECPfuns package so I'd like it to be fairly 
immune to character
sets and different platforms in different countries.

I'm morphing this question a lot now but I guess it's still on topic 
really.? I know
I need to put in some time to understand the complexities of R and 
platforms (I'm
pretty exclusively on Linux, Ubuntu or Debian now so have mostly done 
the ostrich thing
about these issues though I do hit problems exchanging things with my 
Spanish speaking
colleagues).? Jan or anyone: any simple reassurance or pointers to 
resources I should
best use for homework about these issues?

TIA (again!)

Chris
On 06/10/2023 12:55, Jan van der Laan wrote:
[much snipped]
#
Another thing that I considered, but doesn't seem to be supported, is 
rotating the symbols. I noticed that that does work with text. So you 
could use a arrow symbol and then specify the angle aesthetic. But this 
still relies on text and unfortunately there are no arrowlike symbols in 
ASCII: except perhaps 'V'.

I can't say how the support for non-ascii text is over different OS-es 
and localities. 
https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Encoding-issues 
gives some 'hints'
On 06-10-2023 14:21, Chris Evans via R-help wrote:
#
?s 10:09 de 06/10/2023, Chris Evans via R-help escreveu:
Hello,

Maybe you can solve the problem with unicode characters.
See the two scale_*_manual at the end of the plot.



# Unicode characters for black up- and down-pointing characters
pts_shapes <- c("\U25B2", "\U25BC") |> setNames(c("A", "B"))
pts_colors <- c("blue", "red") |> setNames(c("A", "B"))

ggplot(data = tmpTibAreas,
        aes(x = x, y = y)) +
   geom_polygon(data = tmpTibAreas,
                aes(x = x, y = y, fill = a)) +
   geom_point(data = tmpTibPoints,
              aes(x = x, y = y, color = c, shape = c),
              size = 6) +
   scale_shape_manual(values = pts_shapes) +
   scale_color_manual(values = pts_colors)
#
Doesn't the outcome of this suggestion still depend on which fonts and output device you are using? ... and that is to some degree still system dependent...
On October 6, 2023 7:50:00 AM PDT, Rui Barradas <ruipbarradas at sapo.pt> wrote:

  
    
1 day later
#
SO helpful. Thanks to all.? I _think_ the answer to Jeff's question may 
be "It should only be problematical on R earlier than 2.10". At least,
that's how I read this:

There is a portable way to have arbitrary text in character strings 
(only) in your R code, which is to supply them in Unicode as ?\uxxxx?
escapes (or, rarely needed except for emojis, ?\Uxxxxxxxx? escapes). If 
there are any characters not in the current encoding the parser
will encode the character string as UTF-8 and mark it as such. This 
applies also to character strings in datasets: they can be prepared
using ?\uxxxx? escapes or encoded in UTF-8 in a UTF-8 locale, or even 
converted to UTF-8 /via/ |iconv()|. If you do this, make sure you have
?R (>= 2.10)? (or later) in the ?Depends? field of the DESCRIPTION file.

(Quoting from 
https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Encoding-issues. 
Thanks for that pointer Jan.)

### using UTF to solve my issue (for R versions >= 2.10 I think)
library(tidyverse)
tibble(x = 2:9, y = 2:9, c = c(rep("A", 5), rep("B", 3))) %>%
 ? mutate(y1 = y + 1,
 ???????? y2 = y + 2) -> tmpTibPoints
tibble(x = c(1, 5, 5, 1), y = c(1, 1, 5, 5), a = rep("a", 4)) -> tmpTibArea1
tibble(x = c(5, 10, 10, 5), y = c(1, 1, 5, 5), a = rep("b", 4)) -> 
tmpTibArea2
tibble(x = c(1, 5, 5, 1), y = c(5, 5, 10, 10), a = rep("c", 4)) -> 
tmpTibArea3
tibble(x = c(5, 10, 10, 5), y = c(5, 5, 10, 10), a = rep("d", 4)) -> 
tmpTibArea4
bind_rows(tmpTibArea1,
 ?????????? tmpTibArea2,
 ?????????? tmpTibArea3,
 ?????????? tmpTibArea4) -> tmpTibAreas

# Unicode characters for black up- and down-pointing characters
pts_shapes <- c("\U25B2", "\U25BC") |> setNames(c("A", "B"))
pts_colors <- c("blue", "red") |> setNames(c("A", "B"))

pts_shapes

ggplot() +
 ? ### this was the suggestion from Rui Barradas
 ? geom_point(data = tmpTibPoints,
 ???????????? aes(x = x, y = y, color = c, shape = c),
 ???????????? size = 6) +
 ? ### checking what happens using geom_text (for amusement really)
 ? geom_text(data = tmpTibPoints,
 ??????????? aes(x = x, y = y1, label = c)) +
 ? ### and checking using the UTF characters in annotate() too (ditto)
 ? annotate(geom = "text", x = 2.5, y = 8.5, label = paste(pts_shapes, 
collapse = "? ")) +
 ? scale_shape_manual(values = pts_shapes) +
 ? scale_color_manual(values = pts_colors)

Output attached.? Thanks to Jan and Rui particularly.? R-help providing 
wide and deep R education as ever.
On 06/10/2023 17:05, Jeff Newmiller wrote: