R语言近期记录(201911)

本文是笔者近期使用R语言的一个简单记录。

本文包括以下五个小节:

  • ggplot2 手动调整线条颜色
  • ggplot2 修改坐标轴
  • ggplot2 组合图形
  • ggplot2 多图
  • dplyr包中 filter 函数的一个坑
ggplot2 手动调整线条颜色

主要是用到scale_color_manual函数,举例来说:
下面代码是生成一幅折线图,但是线条颜色是软件自动设置的。

n <- 5
x0 <- 1:n
y1 <- x0 + 10
y2 <- x0 + 20
y3 <- x0 + 30
d <- data.frame(x=paste0("X", rep(x0, times=3)), y=c(y1, y2, y3), 
                g=rep(c("a", "b", "c"), each=n))
d$x <- factor(d$x, levels=paste0("X", 1:n))
library(ggplot2)
ggplot(d, aes(x=x, y=y, group=g, color=g)) + geom_point() + geom_line()

生成的图片如下:
在这里插入图片描述

可以看出,一共三组数据,每组数据的线条颜色是软件自动设置的。如果添加scale_color_manual语句可以手动设置线条的颜色:

ggplot(d, aes(x=x, y=y, group=g, color=g)) + geom_point() + geom_line() +
  scale_color_manual(values=c("a"="red", "b"="blue", "c"="black"))

修改后的图片如下:
在这里插入图片描述

类似的函数还有scale_fill_manualscale_size_manualscale_shape_manualscale_linetype_manualscale_alpha_manualscale_discrete_manual等。以scale_fill_manual函数为例,从其名字上就可以看出,该函数可以手动设置填充色。

ggplot2 修改坐标轴

修改坐标轴主要用到scale_x_continuousscale_y_continuousscale_x_discretescale_y_discrete这四个函数。从这些函数的名称上可以看出,分别是针对x轴和y轴的,continuous和discrete分别是针对连续型数据和离散型数据来说的。

这些函数可以对坐标轴的诸多细节进行调整,以笔者遇到的问题为例:如果x轴要画100个点,如果这些点的标签都显示出来,会挤在一起不美观。像下面这样:
在这里插入图片描述

其代码如下:

n <- 100
x0 <- 1:n
y1 <- x0 + 10
y2 <- x0 + 20
y3 <- x0 + 30
d <- data.frame(x=paste0("X", rep(x0, times=3)), y=c(y1, y2, y3), 
                g=rep(c("a", "b", "c"), each=n))
d$x <- factor(d$x, levels=paste0("X", 1:n))
library(ggplot2)
ggplot(d, aes(x=x, y=y, group=g, color=g)) + geom_point() + geom_line() +
  scale_color_manual(values=c("a"="red", "b"="blue", "c"="black"))

即使旋转坐标轴标签也没有多少改观:

ggplot(d, aes(x=x, y=y, group=g, color=g)) + geom_point() + geom_line() +
  scale_color_manual(values=c("a"="red", "b"="blue", "c"="black")) +
  theme(axis.text.x=element_text(angle=45, hjust=.9))   # 旋转坐标轴标签

在这里插入图片描述

这个时候可以使用scale_x_discrete语句,只显示其中部分标签:

ggplot(d, aes(x=x, y=y, group=g, color=g)) + geom_point() + geom_line() +
  scale_color_manual(values=c("a"="red", "b"="blue", "c"="black")) +
  scale_x_discrete(breaks=paste0("X", seq(10, 100, 10)))  # 只显示部分标签

效果如下:
在这里插入图片描述

上面的代码用到了scale_x_discrete函数的breaks参数,该参数控制着显示哪些标签。scale_x_discrete函数还有其它参数控制着坐标轴的诸多细节,比如limits参数,该参数可以控制坐标轴标签显示的顺序。比如我们接着上面的代码,将x轴的显示顺序从X1到X100调整为从X100到X1。

ggplot(d, aes(x=x, y=y, group=g, color=g)) + geom_point() + geom_line() +
  scale_color_manual(values=c("a"="red", "b"="blue", "c"="black")) +
  scale_x_discrete(breaks=paste0("X", seq(10, 100, 10)),  # 只显示部分标签
                   limits=paste0("X", n:1))   # 调整x轴标签顺序

在这里插入图片描述

其它的参数也是很有用的,有机会大家可以研究。

ggplot2 组合图形

组合图形就是在一张图上叠加多个图形,这多个图形分别在不同的图层上。实际上ggplot2中的组合图形一般就是叠加图层。比如,最常见的折线图其实就是一个组合图形,它包含了点图和线图这两个图层的叠加。还以上面的数据为例:

n <- 100
x0 <- 1:n
y1 <- x0 + 10
y2 <- x0 + 20
y3 <- x0 + 30
d <- data.frame(x=paste0("X", rep(x0, times=3)), y=c(y1, y2, y3), 
                g=rep(c("a", "b", "c"), each=n))
d$x <- factor(d$x, levels=paste0("X", 1:n))
library(ggplot2)
ggplot(d, aes(x=x, y=y, group=g, color=g)) + 
	geom_point() + 		# 先是点图
	geom_line()  		# 后是线图

图层的顺序是很重要的,因为后面的图层会“盖”住前面的图层,有可能会影响前面图层的显示效果。比如,还是上面的数据,下面的代码先画箱线图,再画线图,正常显示:

ggplot(d, aes(x=g, y=y)) + 
  geom_boxplot() +  # 箱线图
  stat_summary(fun.y="mean", color="red", geom="line", group=1) # 各组平均值的连线

在这里插入图片描述

从图中可以看出,上图一共三组数据,每组数据的箱线图和平均值的连线都可以正常显示。但是,如果颠倒图层的顺序,先画线图,再画箱线图,那么箱线图会盖住线图,从而影响整体的显示效果:

ggplot(d, aes(x=g, y=y)) + 
  stat_summary(fun.y="mean", color="red", geom="line", group=1) + # 各组平均值的连线
  geom_boxplot()  # 箱线图

在这里插入图片描述

我们从上图可以看到,图层的顺序是很重要的。

其实,上面的代码中用stat_summary函数实现了平均值的线图,其实我们可以用一般的geom_line来实现,只不过需要对数据进行一下处理。因为我们是要求各组的平均值,所以先用一个数据框来存储各组平均值。

d_aver <- aggregate(d$y, list(g=d$g), mean)   # 各组平均值

然后,我们对两个不同的数据框分别作箱线图和线图:

d_aver <- aggregate(d$y, list(g=d$g), mean)   # 各组平均值
ggplot() + 
  geom_boxplot(data=d, aes(x=g, y=y)) +  # 箱线图
  geom_line(data=d_aver, aes(x=g, y=x, group=1, color="red"))  # 各组平均值的连线

在这里插入图片描述

其实正如stat_summary函数的名称显示的那样,这是一个统计画图函数,我们当然可以自己先统计然后画图。比如上面的例子,用处理过的数据经geom_line函数画出来的图和stat_summary画出来的图是一样的。

还有一点值得注意,就是上面的代码中,geom_boxplot函数和geom_line函数分别用了不同的数据集,这一点很有意思,也就意味着不同的图层可以用不同的数据来作图。实际上,不光是数据可以不同,不同的图层还可以使用不同的映射(mapping)。ggplot2的设计思路有点像面向对象编程里面的类的继承ggplot函数是“基类”,它的参数值会被下面的各个图层所继承,而各个图层是相互独立的,它们可以修改自己图层中的参数值(或者说是覆盖掉ggplot函数的参数值),而不会影响其它图层以及ggplot函数的参数值。这一点可以用下面的代码来验证以下:

d_aver <- aggregate(d$y, list(g=d$g), mean)   # 各组平均值
p <- ggplot() + 
  geom_boxplot(data=d, aes(x=g, y=y)) +  # 箱线图
  geom_line(data=d_aver, aes(x=g, y=x, group=1), color="red") # 各组平均值的连线
p$layers   # 显示各个图层的参数
ggplot2 多图

我们经常要将多幅图放在一起,ggplot2 处理这个问题常用两种方式,一是用ggplot2本身的facet,二是利用与ggplot2相关的一个包gridExtra
首先看分面的方式(facet),有facet_wrap函数和facet_grid函数。我们看一个例子:

n <- 5
x0 <- 1:n
y1 <- x0 + 10
y2 <- x0 + 20
y3 <- x0 + 30
d <- data.frame(x=paste0("X", rep(x0, times=3)), y=c(y1, y2, y3), 
                g=rep(c("a", "b", "c"), each=n))
d$x <- factor(d$x, levels=paste0("X", 1:n))
library(ggplot2)
# 采用分面(facet)
# 这里用facet_wrap函数比较合适:
ggplot(d, aes(x=x, y=y)) + geom_line(group=1) + facet_wrap(~g, nrow=2)

画出来的效果是:
在这里插入图片描述

上面的例子比较适合facet_wrap函数,当然,使用facet_grid函数也是可以的:

# 当然,用facet_grid函数也可以:
ggplot(d, aes(x=x, y=y)) + geom_line(group=1) + facet_grid(.~g)

画出来的效果是:
在这里插入图片描述

说完了facet,我们再看看gridExtra包。这个R包可以将多个图片组合成一张大图。笔者常用包中的marrangeGrob函数。将要组合的多个图放入一个list中即可,还以上面的数据为例:

n <- 5
x0 <- 1:n
y1 <- x0 + 10
y2 <- x0 + 20
y3 <- x0 + 30
d <- data.frame(x=paste0("X", rep(x0, times=3)), y=c(y1, y2, y3), 
                g=rep(c("a", "b", "c"), each=n))
d$x <- factor(d$x, levels=paste0("X", 1:n))
library(ggplot2)
library(gridExtra)
oneGrob <- function(g) {  # 生成单张小图
  data <- d[d$g == g, ]
  ggplot(data, aes(x=x, y=y)) + geom_line(group=1) + 
    labs(title=paste0("group=", g)) +
    theme(plot.title = element_text(hjust=0.5))
}
lgrob <- lapply(unique(d$g), oneGrob)  # 将多张小图放到一个list中
# 将多张小图组合成一张大图
marrangeGrob(lgrob, nrow=2, ncol=2, top="Example of marrangeGrob function")

其效果是:
在这里插入图片描述

我们可以看到,图片是按照列优先的顺序进行排列的,如果要按照行的顺序进行排列,可以调整marrangeGrob函数的layout_matrix参数,像下面这样:

marrangeGrob(lgrob, nrow=2, ncol=2, 
    layout_matrix = matrix(1:4, 2, 2, byrow=T),  # 设置byrow=T,按行排列小图
    top="Example of marrangeGrob function")

在这里插入图片描述

dplyr包中 filter 函数的一个坑

自从笔者接触dplyr包以来,使用它的频率越来越高。但是,笔者近期在使用该包的filter函数时,遇到一个问题。以上面的数据为例,假设要挑选出"a"组的数据,将其它组的数据过滤掉,可以这样:

n <- 5
x0 <- 1:n
y1 <- x0 + 10
y2 <- x0 + 20
y3 <- x0 + 30
d <- data.frame(x=paste0("X", rep(x0, times=3)), y=c(y1, y2, y3), 
                g=rep(c("a", "b", "c"), each=n))
d$x <- factor(d$x, levels=paste0("X", 1:n))
library(dplyr)
# 过滤前各组数据量统计
table(d$g)
# 过滤
g <- "a"       # 过滤后剩下的组的组名
d1 <- d %>% filter(g == g)
table(d1$g)  # 过滤失败

过滤失败的原因是filter函数中的过滤条件没有发挥作用,因为g == g在filter函数中恒成立,其实是犯了变量重名的一个错误,把代码改动一下即可:

# 重新过滤
g <- "a"       # 过滤后剩下的组的组名
g2 <- g        # 为了避免变量重名,另赋值一个变量
d2 <- d %>% filter(g == g2)
table(d2$g)  # 过滤成功

(公众号:生信了)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值