无论是什么形状的多边形,只要我们依次给出它的各个顶点,就可以利用geom_polygon把它绘制出来。可见,geom_polygon比那些只能绘制有规律的多边形的函数更加灵活。
# install.packages("unikn")
library(unikn) # 使用配色
library(plothelper)
library(ggfittext) # 用于添加文字
library(ggforce) # 使用geom_shape
##绘制矩形和等边三角形
square=data.frame(x=c(0, 4, 4, 0), y=c(0, 0, 2, 2)) # 矩形的四个顶点无需计算即可给出,分别是[0, 0]、[4, 0]、[4, 2]、[0, 2]
triangle=data.frame(x=c(0, 2, 1), y=c(0, 0, 1.732)) # 我们有时要通过计算得到顶点的位置。在本例中,我们通过计算得知第三个顶点的Y坐标为1.732
ggplot()+coord_fixed()+
geom_polygon(data=square, aes(x=x, y=y), fill="purple", color="yellow", linetype=2, size=2)+
geom_polygon(data=triangle, aes(x=x, y=y), fill="orange")
## 通过分组来一次性绘制多个图形
dat=rbind(square, triangle)
dat=data.frame(dat, shape=c(1, 1, 1, 1, 2, 2, 2))
ggplot(dat)+coord_fixed()+
geom_polygon(aes(x, y, fill=factor(shape)))+
scale_fill_manual(values=c("1"="purple", "2"="orange"))
## 另一种分配颜色的方式是aes函数外使用fill参数:矩形有4个顶点,所以要重复4次"purple";三角形有3个顶点,所以要重复3次"orange"
ggplot(dat)+coord_fixed()+
geom_polygon(aes(x, y, group=factor(shape)), fill=c(rep("purple", 4), rep("orange", 3)))
plothelper包中的rotatexy函数可使多边形旋转。
## rotatexy的x参数需使用一个仅有两列的数据框或矩阵(第一列为X坐标,第二列为Y坐标),如果有多个多边形需要处理,就应如本例一样把它们放到列表里,或者把它们合并为一个矩阵,并用f参数指定一列数值用来把它们分割开(在本例中,即为rotatexy(dat[, -3], f=dat$shape, ...)。xmiddle和ymiddle用于指定围绕哪个点旋转,可以指定一个点,或者指定跟多边形一样多的点,在本例中,矩阵和三角形分别围绕[2, 1]和[0, 0]点旋转。angle用于指定角度,可以指定一个角度,也可以指定跟多边形同样多的角度。函数会自动进行分组并生成名为"g"的一列,因此在用geom_polygon画图时,我们可以把aes函数中的fill等参数指向这一列,不过,如果在rotatexy中设置group=FALSE,则g列不会产生。todf参数会使旋转后的多边形合并成单一的数据框,如果要保持列表形式,可设定
todf=FALSE
dat2=list(square, triangle)
dat2=rotatexy(x=dat2, xmiddle=c(2, 0), ymiddle=c(1, 0), angle=c(pi/4, pi/3)) # 结果包含x、y、g三列
ggplot(dat2)+coord_fixed()+
geom_polygon(aes(x, y, fill=factor(g)))
plothelper包中的rectxy函数能够一次性生成多个矩形的顶点坐标(图6-31a)。rectxy与geom_tile/rect的区别在于,后者并不会给出顶点坐标,而是会直接把图形画出来。
## rectxy用x和y来指定矩形的位置。x和y可以指矩形中心点(xytype="middle",默认值)、矩形左边中心点(xytype="left")或矩形左下角的顶点(xytype="bottomleft")。a和b用于设定矩形的宽和高,本例绘制了边长为1和0.7的两个正方形。angle用于指定旋转角度。此外,rectxy还跟rotatexy一样拥有group和todf参数
dat=rectxy(x=c(0, 0.5), y=c(0, -1.1), xytype="middle", a=c(1, 0.7), b=c(1, 0.7), angle=c(0, pi/9))
ggplot(dat)+coord_fixed()+theme_void()+
theme(plot.background=element_rect(colcor=NA, fill="beige"))+
geom_polygon(show.legend=FALSE, aes(x, y, fill=factor(g)),color=NA)+
scale_fill_manual(values=c("1"="black", "2"="red"))
ellipsexy能够一次性生成多个圆形的坐标(图6-3-1b)。
图6-3-1 左=图a 使用rectxy,右=图b 使用ellipsexy
## ellipsexy的x、y、xytype、angle的使用方法与rectxy相同。a和b用来指定椭圆的长半径和短半径(如需绘制圆形,可设置两个相等的值),n用来指定绘制单个图形时使用的点数
an=seq(0, 7*pi/4, by=pi/4)
a=c(2, 1, 2, 1, 2, 1, 2, 1)
b=c(1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5)
dat=ellipsexy(x=0, y=0, xytype="left", a=a, b=b, angle=an, n=50)
fill=rep(c("red", "blue", "yellow", "gray80"), 2)
names(fill)=1: 8
ggplot(dat)+coord_fixed()+theme_void()+
geom_polygon(show.legend=FALSE, aes(x, y, fill=factor(g)), color="black", size=2)+
scale_fill_manual(values=fill)
使用包括ellipsexy在内的函数可以绘制出一些有趣的抽象图案(图6-3-2)
图6-3-2 用geom_polygon绘制抽象图案
# 生成70个圆环或扇形
nx=7; ny=10; seed=123 # 可修改的三个参数
N=nx*ny
dat=expand.grid(1: nx, 1: ny); colnames(dat)=c("x", "y") # 批量生成中心点
# 生成圆环:随机生成起始角度并传递给start和end参数
set.seed(seed); cir_start=runif(N, 0, 2*pi); seed=seed+1
set.seed(seed); cir_end=cir_start+runif(N, pi, 1.5*pi); seed=seed+1
cir=ellipsexy(dat$x, dat$y, a=0.38, b=0.38, start=cir_start, end=cir_end, fan=FALSE)
# 生成扇形
set.seed(seed); fan_start=runif(N, 0, 2*pi); seed=seed+1
set.seed(seed); fan_end=fan_start+runif(N, pi, 1.8*pi); seed=seed+1
fan=ellipsexy(dat$x, dat$y, a=0.25, b=0.25, start=fan_start, end=fan_end, fan=TRUE) # 当fan=TRUE时,输出结果用于画扇形,而当an=FALSE(默认)则用于绘制弓形
# 生成颜色
set.seed(seed); cir_color=sample(c("red", "green", "blue", "gray30","yellow"), N, TRUE); seed=seed+1
set.seed(seed); fan_color=sample(c("red", "green", "blue", "gray30","yellow"), N, TRUE)
# 绘制圆环和扇形,添加半透明矩形和文字
p1=ggplot()+coord_fixed()+theme_void()+
geom_path(show.legend=FALSE, data=cir, aes(x, y, color= factor(g)), size=1.2)+ # 这里生成有缺口的圆环时不能用geom_polygon而要用geom_path
geom_polygon(show.legend=FALSE, data=fan, aes(x, y, fill= factor(g)))+
scale_color_manual(values=cir_color)+
scale_fill_manual(values=fan_color)
p2=geom_rect(aes(xmin=1-0.2, xmax=7+0.2, ymin=2, ymax=9), fill="white", alpha=0.85)
p3=geom_fit_text(aes(xmin=1, xmax=7, ymin=2, ymax=9, label="The\n32nd\n Olympic\n Games"), grow=TRUE, reflow=FALSE, family="mono", fontface=2)
p1+p2+p3+theme(plot.background=element_rect(fill="cornsilk",color="cornsilk"))
接下来我们用geom_polygon绘制条形图,不过,用来表示数量的图形不再是矩形,而是其他图形。比如,我们希望用看起来像三角形,但又不带顶角的图形代替矩形。这样的图形有很多,我们现在就用简单的正态曲线来绘制(图6-33)。示例中的数据为世界银行发布的营商环境评估的总分数。
图6-3-3 用特殊图形绘制条形图
# 本例将使用unikn包中的一组配色。加载后,用seecol("all")查看所有配色及其名称
dat=read.csv("business small.csv", row.names=1) # 课件中的文件
v=dat$DB2019 # 分值
lab=dat$Name # 标签
width=1 # 为图形指定宽度
# 首先生成一个基础图形。我们最好让这个图形关于Y轴对称,因为这样会使对它(www.xing528.com)
进行位移变得方便
x=seq(-3, 3, 0.05)
y=dnorm(x, sd=1) # 读者可尝试用sd参数来调整最终画出的图形
xy=cbind(x, y) # 生成基础图形
# 接下来,根据基础图形生成多个条形
n=length(v)
dat=rep(list(xy), times=n)
for (i in 1: n){
# 用rescale调整水平位置(水平位置的中心点就是1, 2, 3, ...)
dat[[i]][, 1]=scales::rescale(dat[[i]][, 1], to=c(i-width/2, i+width/2))
# 用rescale将图形的Y坐标调整到从0至相应高度的值域中
dat[[i]][, 2]=scales::rescale(dat[[i]][, 2], to=c(0, v[i]))
}
dat=do.call(rbind, dat)
dat=data.frame(dat, g=rep(1: n, each=nrow(dat)/n)) # 添加分组标记
mycolor=seecol(pal=pal_unikn_pref, n=8) # pal=颜色名称,名称不要加引号 names(mycolor)=NULL # 务必去掉用seecol函数生成的各个颜色的名称 ggplot(dat)+
geom_polygon(show.legend=FALSE, aes(x, y, fill=factor(g)))+
scale_fill_manual(values=mycolor)+
scale_x_continuous(breaks=1: n, labels=lab)+
labs(title="Business Environment Evaluation")+
theme_minimal()+
theme(
axis.title=element_blank(),
axis.text.y=element_text(size=16),
axis.text.x=element_text(angle=30, size=16, family="mono", face=2, hjust=0.8),
panel.grid.minor.x=element_blank(),
plot.title=element_text(size=22, family="mono", face=2)
)
下面我们来绘制雷达图。雷达图中,用于表示数值大小的部分可看成是一个多边形,相邻的两条连线之间的夹角相等。我们将使用营商便利程度评估数据来展示作图过程(图6-3-4)。
图6-3-4 绘制雷达图
dat=read.csv("db 5dim.csv", row.names=1) # 课件中的文件
nd=ncol(dat) # 项目数
n=nrow(dat) # 国家数
# 我们先写出生成单个雷达图位置的函数。在这个函数中,r参数是被评估对象在各个维度上的取值,x和y是雷达图的中心点。输出的多边形的坐标将会按照从pi/2的位置开始顺时针排列
radar_pos=function(r, x=0, y=0){
theta=seq(pi/2, -3*pi/2, length.out=length(r)+1)[1: length (r)]
data.frame(x=r*cos(theta)+x, y=r*sin(theta)+y)
}
# 为查看这个函数的效果,我们可以先试着画一个多边形:toy=radar_pos(c(2, 1, 2)); ggplot(toy)+geom_polygon(aes(x, y))+coord_fixed()
# 雷达图的四个中心点的坐标
x0=0; y0=0
# 代表100、80、60分的同心圆
cir=ellipsexy(x=x0, y=y0, a=c(60, 80, 100), b=c(60, 80, 100))
# 将数据框中每行的向量填充到列表中以便让ANYxy函数使用
L=as.list(data.frame(t(dat)))
# 用ANYxy函数生成多边形。ANYxy的第一个参数是需用到的函数(此处即radar_pos),后边的参数均为这个被用到的函数的参数。输出结果中的g列用于分组
ra=ANYxy(myfun=radar_pos, r=L, x=x0, y=y0)
names(ra)[3]="G" # 用于表示数值的多边形所带有的分组编号,跟同心圆的分组编号的作用不一样,因此这里把第三列的"g"改为"G"
# 设置文字
lab=colnames(dat) # 各维度标签
lab=gsub("\\.", " ", lab) # 将句点变成空格
lab=scales::wrap_format(9)(lab) # 设置每行宽度为9
# 仍然使用ANYxy,生成长为100(满分为100分)的轴线并添加标签
ax=ANYxy(myfun=radar_pos, r=list(rep(100, nd)), x=x0, y=y0, group=FALSE)
ax=data.frame(ax, xend=0, yend=0, lab=lab)
# 用于绘制多边形的ra数据框包含G列,我们可以借助这一列及facet_wrap函数把雷达图画到四个分面图中去。我们将在第七章介绍这一操作和该操作涉及的theme函数,所以现在大家只要知道facet_wrap可用来完成分面操作即可
country=rownames(dat)
names(country)=as.character(1: n) # 为向量中的各项赋予名字是为了使用facet_wrap函数
ggplot()+coord_fixed(xlim=c(-200, 200), ylim=c(-160, 160), expand=FALSE)+
facet_wrap(~G, labeller=labeller(G=country))+
geom_polygon(data=cir, aes(x, y, group=g), fill=NA, color="blue", linetype=3)+
geom_segment(data=ax, aes(x=x, y=y, xend=xend, yend=yend), color="blue", linetype=3.5)+
geom_polygon(show.legend=FALSE, data=ra, aes(x, y, fill=factor(G), color=factor(G)), size=1, alpha=0.6)+
scale_color_manual(values=c("#9b1b30", "#F96714", "#2A4B7C","#797B3A"))+
scale_fill_manual(values=c("#9b1b30", "#F96714", "#2A4B7C","#797B3A"))+
geom_text(data=ax, aes(x=x, y=y, label=lab), lineheight=0.7, vjust="outward", hjust="outward", size=3.5, color="gray10")+
labs(title="Business Environment Evaluation")+
theme_void()+
theme(strip.text=element_text(face=4, hjust=0, size=13, family="mono", margin=margin(2, 2, 2, 2, unit="mm")),
plot.background=element_rect(color=NA, fill="#F3E0BE"),
plot.title=element_text(size=17, hjust=0.5, color="grey10")
)
#==========
# 练习:绘制四个角呈弧形的条形图
#==========
# 除geom_polygon外,ggforce包中的geom_shape亦可用于绘制多边形。我们可用它绘制四个角为弧形而非直角的条形图
library(ggforce)
v=c(1, 2, 3, 5, 4)
dat=rectxy(x=0, y=1: 5, xytype="left", a=v, b=0.8)
ggplot(dat)+geom_shape(aes(x=x, y=y, group=factor(g)), fill="red", radius=unit(5, "mm"))
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。