问题
I want to create a plot like the one below:
I know I can use the radarchart function from fmsb
package. I wonder if ggplot2
can do so, using polar coordinate? Thanks.
回答1:
First, we load some packages.
library(reshape2)
library(ggplot2)
library(scales)
Here are the data from the radarchart example you linked to.
maxmin <- data.frame(
total = c(5, 1),
phys = c(15, 3),
psycho = c(3, 0),
social = c(5, 1),
env = c(5, 1)
)
dat <- data.frame(
total = runif(3, 1, 5),
phys = rnorm(3, 10, 2),
psycho = c(0.5, NA, 3),
social = runif(3, 1, 5),
env = c(5, 2.5, 4)
)
We need a little manipulation to make them suitable for ggplot.
Normalise them, add an id column and convert to long format.
normalised_dat <- as.data.frame(mapply(
function(x, mm)
{
(x - mm[2]) / (mm[1] - mm[2])
},
dat,
maxmin
))
normalised_dat$id <- factor(seq_len(nrow(normalised_dat)))
long_dat <- melt(normalised_dat, id.vars = "id")
ggplot also wraps the values so the first and last factors meet up. We add an extra factor level to avoid this. This is no longer true.
levels(long_dat$variable) <- c(levels(long_dat$variable), "")
Here's the plot. It isn't quite the same, but it should get you started.
ggplot(long_dat, aes(x = variable, y = value, colour = id, group = id)) +
geom_line() +
coord_polar(theta = "x", direction = -1) +
scale_y_continuous(labels = percent)
Note that when you use coord_polar
, the lines are curved. If you want straight lines, then you'll have to try a different technique.
回答2:
If you are looking for a non polar coordinate version, I think the following function will help:
###################################
##Radar Plot Code
##########################################
##Assumes d is in the form:
# seg meanAcc sdAcc meanAccz sdAccz meanSpd sdSpd cluster
# 388 -0.038 1.438 -0.571 0.832 -0.825 0.095 1
##where seg is the individual instance identifier
##cluster is the cluster membership
##and the variables from meanACC to sdSpd are used for the clustering
##and thus should be individual lines on the radar plot
radarFix = function(d){
##assuming the passed in data frame
##includes only variables you would like plotted and segment label
d$seg=as.factor(d$seg)
##find increment
angles = seq(from=0, to=2*pi, by=(2*pi)/(ncol(d)-2))
##create graph data frame
graphData= data.frame(seg="", x=0,y=0)
graphData=graphData[-1,]
for(i in levels(d$seg)){
segData= subset(d, seg==i)
for(j in c(2:(ncol(d)-1))){
##set minimum value such that it occurs at 0. (center the data at -3 sd)
segData[,j]= segData[,j]+3
graphData=rbind(graphData, data.frame(seg=i,
x=segData[,j]*cos(angles[j-1]),
y=segData[,j]*sin(angles[j-1])))
}
##completes the connection
graphData=rbind(graphData, data.frame(seg=i,
x=segData[,2]*cos(angles[1]),
y=segData[,2]*sin(angles[1])))
}
graphData
}
If you are plotting by cluster or group you can then use the following:
radarData = ddply(clustData, .(cluster), radarFix)
ggplot(radarData, aes(x=x, y=y, group=seg))+
geom_path(alpha=0.5,colour="black")+
geom_point(alpha=0.2, colour="blue")+
facet_wrap(~cluster)
This should work with the following data sample:
seg meanAccVs sdAccVs meanSpd sdSpd cluster
1470 1.420 0.433 -0.801 0.083 1
1967 -0.593 0.292 1.047 0.000 3
2167 -0.329 0.221 0.068 0.053 7
2292 -0.356 0.214 -0.588 0.056 4
2744 0.653 1.041 -1.039 0.108 5
3448 2.189 1.552 -0.339 0.057 8
7434 0.300 0.250 -1.009 0.088 5
7764 0.607 0.469 -0.035 0.078 2
7942 0.124 1.017 -0.940 0.138 5
9388 0.742 1.289 -0.477 0.301 5
回答3:
I spent several days on this problem and in the end I decided to built my own package atop of ggradar
. The core of it is an improved version of @Tony M.'s function:
CalculateGroupPath4 <- function(df) {
angles = seq(from=0, to=2*pi, by=(2*pi)/(ncol(df)-1)) # find increment
xx<-c(rbind(t(plot.data.offset[,-1])*sin(angles[-ncol(df)]),
t(plot.data.offset[,2])*sin(angles[1])))
yy<-c(rbind(t(plot.data.offset[,-1])*cos(angles[-ncol(df)]),
t(plot.data.offset[,2])*cos(angles[1])))
graphData<-data.frame(group=rep(df[,1],each=ncol(df)),x=(xx),y=(yy))
return(graphData)
}
CalculateGroupPath5 <- function(mydf) {
df<-cbind(mydf[,-1],mydf[,2])
myvec<-c(t(df))
angles = seq(from=0, to=2*pi, by=(2*pi)/(ncol(df)-1)) # find increment
xx<-myvec*sin(rep(c(angles[-ncol(df)],angles[1]),nrow(df)))
yy<-myvec*cos(rep(c(angles[-ncol(df)],angles[1]),nrow(df)))
graphData<-data.frame(group=rep(mydf[,1],each=ncol(mydf)),x=(xx),y=(yy))
return(graphData)
}
microbenchmark::microbenchmark(CalculateGroupPath(plot.data.offset),
CalculateGroupPath4(plot.data.offset),
CalculateGroupPath5(plot.data.offset), times=1000L)
Unit: microseconds
expr min lq mean median uq max neval
CalculateGroupPath(plot.data.offset) 20768.163 21636.8715 23125.1762 22394.1955 23946.5875 86926.97 1000
CalculateGroupPath4(plot.data.offset) 550.148 614.7620 707.2645 650.2490 687.5815 15756.53 1000
CalculateGroupPath5(plot.data.offset) 577.634 650.0435 738.7701 684.0945 726.9660 11228.58 1000
Note that I have actually compared more functions in this benchmark - among others functions from ggradar
. Generally @Tony M's solution is well written - in the sense of logic and that you can use it in many other languages, like e.g. Javascript, with a few tweaks. However R
becomes much faster if you vectorise the operations. Therefore the massive gain in computation time with my solution.
All answers except @Tony M.'s have used the coord_polar
-function from ggplot2
. There are four advantages of staying within the cartesian coordinate system:
- It allows you to transport your solution also to other plotting packages, e.g.
plotly
. - Everybody who has some understanding of the standard cosinus and sinus-function can comprehend how the data transformation works.
- You can extend and customise the plot like you want - hell, you can use it with any plotting package available in R!
- You do not need to load any except your plotting-package. However it mostly makes sense to rescale your data e.g. with Hadley's
scales
-package.
If like me you know nothing about how to do radar plots when you find this thread: The coord_polar()
might create good looking radar-plots. However the implementation is somewhat tricky. When I tried it I had multiple issues:
- The first issue with this approach is that the lines do not stay straight.
- The
coord_polar()
does e.g. not translate into plotly. - The polar coordinate system makes detailed customisation tricky, because annotations and other features will get thrown into the polar coordinates as well.
This guy here made a nice radar-chart using coord_polar
.
However given my experiences - I rather advise against using the coord_polar()
-trick. Instead if you are looking for an 'easy way' to create a static ggplot-radar, maybe use the great ggforce
-package to draw circles of the radar. No guarantees this is easier than using my package, but from adaptility seems neater than coord_polar
. The disadvantage here is that e.g. plotly
does not support the ggforce-extention.
EDIT: Now I found nice example with ggplot2's coord_polar that revised my opinion a bit.
回答4:
Here is an answer that almost does it in ggplot.
I do nor claim anything more than putting the example here, it is based on what Hadley showed here https://github.com/hadley/ggplot2/issues/516
All I did was use deployer/tidyr instead and choose only 3 cars for simplicity
the issues still pending are 1) the last and first point are not connected, this is obvious if you see the coord_polar as a wrapping of the traditional x axis. There is no reason why they should be connected. But that is how radar charts are normally shown 2) to do that you need to add a segment manually between those 2 points. A little manipulation and a few more layers should do it. I will try to work on it if I have some time
library(dplyr);library(tidyr);library(ggplot2)
#make some data
data = mtcars[c(27,19,16),]
data$model=row.names(data)
#connvert data to long format and also rescale it into 0-1 scales
data1 <- data %>% gather(measure,value,-model) %>% group_by(measure) %>% mutate(value1=(value-min(value))/(max(value)-min(value)))
is.linear.polar <- function(coord) TRUE
ggplot(data1,aes(x=measure,y=value1,color=model,group=model))+geom_line()+coord_polar()
回答5:
I came across this great library which give perfect, ggplot-compatible spider plots :
https://github.com/ricardo-bion/ggradar
Very easy to install and to use, as you can see on the github:
devtools::install_github("ricardo-bion/ggradar", dependencies=TRUE)
library(ggradar)
suppressPackageStartupMessages(library(dplyr))
library(scales)
library(tibble)
mtcars %>%
rownames_to_column( var = "group" ) %>%
mutate_at(vars(-group),funs(rescale)) %>%
tail(4) %>% select(1:10) -> mtcars_radar
ggradar(mtcars_radar)
来源:https://stackoverflow.com/questions/9614433/creating-radar-chart-a-k-a-star-plot-spider-plot-using-ggplot2-in-r