Ggplot2 Polar Plot Arrows

ggplot2 polar plot arrows

Set up data (from dput):

polar <- structure(list(degree = c(120L, 30L, -120L, 60L, 150L, -90L, 
-60L, 0L), value = c(0.5, 0.2, 0.2, 0.5, 0.4, 0.14, 0.5, 0.6)), .Names = c("degree",
"value"), class = "data.frame", row.names = c(NA, -8L))

You can get the straight lines fairly easily -- you just have to make sure your segments start at degree rather than 0:

library(ggplot2)
base <- ggplot(polar, aes(x=degree, y=value))
p <- base + coord_polar()
p+ geom_segment(aes(y=0, xend=degree, yend=value))

Sample Image
Adding arrows, however, makes it look like there may be a bug (?) -- the coordinate transform doesn't get taken into account in computing the angle of the arrows:

library(grid)
p+ geom_segment(aes(y=0, xend=degree, yend=value) ,
arrow=arrow(length=unit(0.3,"cm")))

Sample Image
You can (sort of) hack around this by drawing your own arrowheads:

awid <- 2
p + geom_segment(aes(y=0, xend=degree, yend=value))+
geom_segment(aes(y=value-0.05,yend=value,x=degree-awid/value,xend=degree))+
geom_segment(aes(y=value-0.05,yend=value,x=degree+awid/value,xend=degree))

Sample Image

If you look closely, you can see that the arrowheads aren't perfectly straight (the effect is much more obvious if you make awid larger).

ggplot2: How to add closed (and filled) arrowheads to a polar plot

Using the arrow() function from grid, you only need one call to geom_segment.

 require(ggplot2)
require(grid)

# Data
polar <- structure(list(degree = c(120L, 30L, -120L, 60L, 150L, -90L, -60L, 0L),
value = c(0.5, 0.2, 0.2, 0.5, 0.4, 0.14, 0.5, 0.6)),
.Names = c("degree", "value"),
class = "data.frame", row.names = c(NA, -8L))

ggplot(polar, aes(x=degree, y=value)) +
geom_segment(aes(y=0, xend=degree, yend=value), arrow=arrow(type="closed")) +
coord_polar()

Sample Image

draw straight line between any two point when using coord_polar() in ggplot2 (R)

This is going to be a bit more of a pain than it might first appear, I'm afraid. Essentially, you'd have to write a new panel drawing method for the segments that ignores whether a coord system is linear or not. To do so, you can do the following, based on GeomSegment$draw_panel:

library(tidyverse)

geom_segment_straight <- function(...) {
layer <- geom_segment(...)
new_layer <- ggproto(NULL, layer)
old_geom <- new_layer$geom
geom <- ggproto(
NULL, old_geom,
draw_panel = function(data, panel_params, coord,
arrow = NULL, arrow.fill = NULL,
lineend = "butt", linejoin = "round",
na.rm = FALSE) {
data <- ggplot2:::remove_missing(
data, na.rm = na.rm, c("x", "y", "xend", "yend",
"linetype", "size", "shape")
)
if (ggplot2:::empty(data)) {
return(zeroGrob())
}
coords <- coord$transform(data, panel_params)
# xend and yend need to be transformed separately, as coord doesn't understand
ends <- transform(data, x = xend, y = yend)
ends <- coord$transform(ends, panel_params)

arrow.fill <- if (!is.null(arrow.fill)) arrow.fill else coords$colour
return(grid::segmentsGrob(
coords$x, coords$y, ends$x, ends$y,
default.units = "native", gp = grid::gpar(
col = alpha(coords$colour, coords$alpha),
fill = alpha(arrow.fill, coords$alpha),
lwd = coords$size * .pt,
lty = coords$linetype,
lineend = lineend,
linejoin = linejoin
),
arrow = arrow
))

}
)
new_layer$geom <- geom
return(new_layer)
}

Then you can use it like any other geom.

ggplot() +
geom_tile(data = df,
aes(x = x,
y = y,
fill = d)) +
ylim(c(-2, 5)) +
geom_segment_straight(
aes(
x = "o",
y = -1,
xend = "z",
yend = 3
),
arrow = arrow(length = unit(0.2, "cm")),
col = "red",
size = 2
) +
coord_polar()

Sample Image

EDIT: geom_curve()

Here is the same trick applied to geom_curve():

geom_curve_polar <- function(...) {
layer <- geom_curve(...)
new_layer <- ggproto(NULL, layer)
old_geom <- new_layer$geom
geom <- ggproto(
NULL, old_geom,
draw_panel = function(data, panel_params, coord,
curvature = 0.5, angle = 90, ncp = 5,
arrow = NULL, arrow.fill = NULL,
lineend = "butt", linejoin = "round",
na.rm = FALSE) {
data <- ggplot2:::remove_missing(
data, na.rm = na.rm, c("x", "y", "xend", "yend",
"linetype", "size", "shape")
)
if (ggplot2:::empty(data)) {
return(zeroGrob())
}
coords <- coord$transform(data, panel_params)
ends <- transform(data, x = xend, y = yend)
ends <- coord$transform(ends, panel_params)

arrow.fill <- if (!is.null(arrow.fill)) arrow.fill else coords$colour
return(grid::curveGrob(
coords$x, coords$y, ends$x, ends$y,
default.units = "native", gp = grid::gpar(
col = alpha(coords$colour, coords$alpha),
fill = alpha(arrow.fill, coords$alpha),
lwd = coords$size * .pt,
lty = coords$linetype,
lineend = lineend,
linejoin = linejoin
),
curvature = curvature, angle = angle, ncp = ncp,
square = FALSE, squareShape = 1, inflect = FALSE, open = TRUE,
arrow = arrow
))

}
)
new_layer$geom <- geom
return(new_layer)
}

The above yields the following plot after replacing geom_segment_straight() with geom_curve_polar():

Sample Image

Small note: this way of making new geoms is the quick and dirty way of doing it. If you plan to do it properly, you should write the constructors and ggproto classes separately.

ggplot - connecting points in polar coordinates with a straight line

I solved the issue myself and wanted to share my findings:

My problem was in this line:

geom_segment(data=polar, aes(x=0, y=0, xend=Winkel2, yend=value, fill=NA),

The starting point x determines the direction, in which the arrow should head to in the first place. E.g. if set to 0, all arrows will initially head towards my zero mark and turn towards their intended postion by making curves. To solve the issue, I had to set x=Winkel2, thus each arrow starts at its individual mark and goes straight to the final position:

ggplot2 v2.21.9 sec.axis in polar plot

As @henrik mentioned in the comments, this is a bug. It's been patched and is available if you use the development version from GitHub (i.e., devtools::install_github("tidyverse/ggplot2")).

Here's the example after the patch:

require(ggplot2)
#> Loading required package: ggplot2
set.seed(40);
Location <- data.frame(Winkel = round(runif(1000, 0, 24), 0))
Location$BAD <- Location$Winkel %in% c(seq(7, 18))
Abschnitte <- c(0:24)

polar <- data.frame(Winkel2 = c(1.5, 2.34, 1.2, 3.45, 1.67, 2.61, 1.11, 13.2),
value = c(0.1, 0.03, 0.02, 0.015, 0.01, 0.04, 0.09, 0.06))

ggplot(Location, aes(x = Winkel, fill = BAD, y = (..count..)/sum(..count..))) +
geom_histogram(breaks = seq(0,24), colour = "black") +
coord_polar(start = 0) + theme_minimal() +
scale_fill_brewer(type = "seq", palette = 3) +
ylab("Percentual allocation time") +
ggtitle("") +
scale_x_continuous("", limits = c(0, 24), breaks = Abschnitte, labels = Abschnitte) +
scale_y_continuous(labels = scales::percent,
sec.axis = sec_axis(~.*5, name = "mean direction")) +
geom_segment(data = polar, aes(x = Winkel2, y = 0, xend = Winkel2, yend = value, fill = NA),
arrow = arrow(angle = 30, type = "closed", length = unit(0.3, "cm")))
#> Warning: Ignoring unknown aesthetics: fill

Sample Image

r - Using geom_segment with coord_polar

Well, this solution is even hackier than the answer to your first question. Instead of using geom_segment I use geom_path, but this will produce only the last arrow, so we will add the group aesthetic to connect segments one by one. This means our original data frame has to be slightly modified:

tweak_data <- function(df) 
{
if (nrow(df) == 1) return(df)
idx_x2 <- c(1, rep(2:(nrow(df) - 1), each=2), nrow(df))
gr <- rep(seq.int(nrow(df) - 1), each=2)
df_res <- data.frame(r = df$r[idx_x2], theta = df$theta[idx_x2],
label = rownames(df)[idx_x2], group = gr)
df_res
}

example_tw <- tweak_data(example)
myplot2 <- ggplot(example2, aes(r, theta)) + geom_point(size=3.5) +
scale_x_continuous(breaks=seq(0,max(example$r)), lim=c(0, max(example$r))) +
scale_y_continuous(breaks=round(seq(0, 2*pi, by=pi/4), 2),
expand=c(0, 0), lim=c(0, 2*pi)) +
geom_text(aes(label=label), size=4.4, hjust=0.5, vjust=-1)
myplot2 + coord_polar2 +
geom_path(aes(group=group), arrow=arrow(length=unit(0.3,"cm"), type="closed"))

Sample Image

R plot arrows instead of points

Try this

x <- runif(10)
y <- runif(10)
plot(x, y, type="n", xlim=c(0, max(x)), ylim=c(0, max(y)))
arrows(0, 0, x, y)

arrows can only add content to an existing plot, but cannot used not create a new plot. By first calling plot you set up the plot, creating axes, labels, etc., but type="n" tells R to hide all contents in the plot area (i.e. the points). After that you can go ahead and add the arrows.

Sample Image



Related Topics



Leave a reply



Submit