大数跨境
0
0

ggsector:帮助你轻松的绘制扇形

ggsector:帮助你轻松的绘制扇形 R语言数据分析指南
2022-11-27
0
导读:写在最前❝「ggsector」是本人的第二个R包,也是本人第一个面向公众的R包。「ggsector」主要提供

写在最前

「ggsector」是本人的第二个R包,也是本人第一个面向公众的R包。「ggsector」主要提供可以在ggplot2、grid中轻易绘制扇形的函数。
在写「ggsector」的过程中,笔者最先参考了J叔“老俊俊”的jjpie,随后参考了Hadley大佬的geom_point, 最后在「ggsector」接近完成之际,笔者参考了Y叔“余光创”老师的ggfun、treeio等包中DESCRIPTION、 vignette、readme的写法。
总之,「ggsector」是站在巨人的肩膀上才有的成果,感谢以上大佬。

R包安装

目前「ggsector」只能通过github来下载,地址为:
https://github.com/yanpd01/ggsector
下载命令如下:

# github
remotes::install_github("yanpd01/ggsector")
# gitee
remotes::install_git("https://gitee.com/yanpd01/ggsector")

小小吐槽一下,CRAN审核好严格啊,从标题到描述、还有各种案例的使用,都审核的好仔细, 笔者至今还在与CRAN审核斗智斗勇,期待早日上架。

ggplot中的使用案例

在ggplot2中使用「ggsector」非常简单,只需要使用geom_sector即可,参数中去除常规的x、y、fill、color外,还有其他几个可以高度自定义的参数。请看以下案例:

准备阶段

## 加载R包
library(ggsector)
library(reshape2)
## 准备数据
df <- cor(mtcars)[1:31:5] %>%
    abs() %>%
    melt(varnames = c("x""y"))

individual

默认为TRUE,主要是控制,以单个坐标点进行逐个扇形的绘制还是以向量的形式在画板上整体绘制。
individual = TRUE 时,逐个绘图,扇形不会形变,但是当绘制的扇形过多时,整体绘制速度会慢很多。
individual = FALSE 时,以向量形式作图,速度较快,但是需要搭配coord_fixed()来锁定画板长宽比,不然扇形会形变。
案例:

ggplot(df) +
    geom_sector(aes(x, y), theta = 75, fill = 2, individual = FALSE) +
    theme_bw() +
    theme(axis.title = element_blank())
ggplot(df) +
    geom_sector(aes(x, y), theta = 75, fill = 2, individual = FALSE) +
    coord_fixed(ratio = 3 / 5) + ## ratio为画板x轴/y轴的比例
    theme_bw() +
    theme(axis.title = element_blank())
ggplot(df) +
    geom_sector(aes(x, y), theta = 75, fill = 2, individual = TRUE) +
    theme_bw() +
    theme(axis.title = element_blank())

theta

扇形角度参数,与type参数结合使用,type参数默认为 "percent"。
当type = "percent"时,完整的圆周为100个散点组成的多边形,theta取值0-100。
当type = "degree"时,完整的圆周为360个散点组成的多边形,theta取值0-360。
案例:

ggplot(df) +
    ## type = "percent", theta 取值在 0-100
    geom_sector(
        aes(y, x, theta = value * 100),
        type = "percent",
        color = "blue",
        individual = TRUE
    ) +
    ## type = "degree", theta 取值在 0-360
    geom_sector(
        aes(y, x, theta = value * 360),
        type = "degree",
        color = "red",
        alpha = 0.5,
        individual = TRUE
    ) +
    theme_bw() +
    theme(axis.title = element_blank())

观察这个图里圈起来的地方(这里展示的可能不是很清楚,但观众老爷可自行运行尝试), 便能发现:

两种模式下的扇形并没有完全的重合在一起,这是因为:
type = "percent"时,圆周为100个散点,输入的数值会被round()为0-100的整数,
type = "degree"时,圆周为360个散点,输入的数值会被round()为0-360的整数,
圆周点数越多,精度越高,但也意味着越慢的绘图速度。

笔者这里暂时只提供这两种模式, 后续会提供自定义圆周散点数目的功能, 敬请期待!

r

扇形半径参数,r = 0.5为恰好充满整个小方格,默认r为0.45。
案例:

ggplot(df) +
    geom_sector(
        aes(y, x, theta = value * 100),
        r = rep(c(0.150.30.45), 5),
        fill = 2,
        individual = TRUE
    ) +
    theme_bw() +
    theme(axis.title = element_blank())

start

扇形起始角度参数,参数取值受type影响,取值范围与theta一致,默认为0, 表示以y轴正上方为起点,绘制扇形,以下为不同起始角度的展示

ggplot(df) +
    geom_sector(
        aes(y, x, theta = value * 100),
        start = rep(c(604020), 5),
        fill = 2,
        individual = TRUE
    ) +
    theme_bw() +
    theme(axis.title = element_blank())

r_start

扇形半径起始位置参数,取值在0-半径长之间,默认为0,表示画扇形, 若大于0,则是画扇区,以下为不同展示

ggplot(df) +
    geom_sector(
        aes(y, x, theta = value * 100),
        r_start = rep(c(0.150.250.35), 5),
        fill = 2,
        individual = TRUE
    ) +
    theme_bw() +
    theme(axis.title = element_blank())

来点底层的

「ggsector」并不是只能在ggplot中调用,本包的底层从grid写起,也为直接调用grid画扇形提供了方便。

sectorGrob、grid.sector参数与ggplot2中,一模一样,观众老爷可以自行尝试

### sectorGrob
grid.newpage()
gp <- sectorGrob(
    x = c(0.30.50.7),
    y = c(0.30.50.7),
    theta = c(90180270),
    r = 0.1,
    start = c(180180270),
    r_start = c(0.030.060),
    type = "degree",
    group = factor(1:3, levels = c(231)),
    gp = gpar(fill = c("green""red""blue"))
)
grid.draw(gp)

### grid.sector
grid.newpage()
grid.sector(
    x = c(0.10.50.9),
    y = c(0.90.60.1),
    theta = c(255090),
    r = .1,
    start = c(2550100),
    r_start = c(0.060.030),
    type = "percent",
    group = factor(1:3, levels = c(231)),
    gp = gpar(col = c("green""red""blue"), fill = 2:4),
    default.units = "npc"
)

来点进阶的

与ComplexHeatmap对接

「ggsector」与ComplexHeatmap对接有三种方式,由于最终呈现结果基本一致,此处只配一幅图。
(不一致的地方是因为本案例中使用了runif()随机数,加之cell_fun与layer_fun调用方式也不一样,所以呈现出前两幅图一模一样,第三幅不一样)

准备数据
library(magrittr)
library(ComplexHeatmap)
t0 <- cor(mtcars) %>%
    set_colnames(paste("y_", colnames(.))) %>%
    set_rownames(paste("x_", rownames(.)))
mat <- abs(t0)
mat[1:51:5]
方式一:cell_fun + viewport
set.seed(1)
Heatmap(
    mat,
    name = "vp",
    rect_gp = gpar(type = "none"),
    cell_fun = function(j, i, x, y, width, height, fill) {
        grid.rect(
            x = x, y = y, width = width, height = height,
            gp = gpar(col = "grey", fill = NA)
        )
        grid.sector(
            theta = mat[i, j] * 100,
            r = 0.5,
            start = mat[i, j] * 100 * runif(1),
            r_start = mat[i, j] * 0.45 * runif(1),
            vp = viewport(x, y, width, height),
            gp = gpar(fill = fill, col = "transparent")
        )
    },
    width = unit(.7"snpc"),
    height = unit(.7"snpc")
)
方式二:cell_fun + x,y坐标
set.seed(1)
Heatmap(
    mat,
    name = "xy + r",
    rect_gp = gpar(type = "none"),
    cell_fun = function(j, i, x, y, width, height, fill) {
        grid.rect(
            x = x, y = y, width = width, height = height,
            gp = gpar(col = "grey", fill = NA)
        )
        r <- as.numeric(min(width, height)) / 2
        grid.sector(
            x,
            y,
            theta = mat[i, j] * 100,
            r = r,
            start = mat[i, j] * 100 * runif(1),
            r_start = mat[i, j] * r * 0.9 * runif(1),
            vp = NULL,
            gp = gpar(fill = fill, col = "transparent")
        )
    },
    width = unit(.7"snpc"),
    height = unit(.7"snpc")
)
方式三:layer_fun + x,y坐标
set.seed(1)
Heatmap(
    mat,
    name = "layer",
    rect_gp = gpar(type = "none"),
    layer_fun = function(j, i, x, y, width, height, fill) {
        grid.rect(
            x = x, y = y, width = width, height = height,
            gp = gpar(col = "grey", fill = NA)
        )
        r <- as.numeric(min(width, height)) / 2
        grid.sector(
            x,
            y,
            theta = pindex(mat, i, j) * 100,
            r = r,
            start = pindex(mat, i, j) * 100 * runif(nrow(mat) * ncol(mat)),
            r_start = pindex(mat, i, j) * r * 0.9 * runif(nrow(mat) * ncol(mat)),
            vp = NULL,
            gp = gpar(fill = fill, col = "transparent")
        )
    },
    width = unit(.7"snpc"),
    height = unit(.7"snpc")
)

与Seurat对接

笔者之前进行单细胞分析的时候,只想看看某个基因在某个细胞类型中的表达情况(百分比与表达值), Seurat自带的DotPlot能满足我一些需求,但又不是很直观,恰逢「ggsector」诞生,便正好对接一下。
这里的SectorPlot()函数,笔者就不仔细介绍了,感兴趣的小伙伴可以自行研究。
以下以pmnc3K数据为例:

library(Seurat)
library(tibble)
path <- paste0(tempdir(), "/pbmc3k.tar.gz")
file <- paste0(tempdir(), "/filtered_gene_bc_matrices/hg19")
download.file(
    "https://cf.10xgenomics.com/samples/cell/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz",
    path
)
untar(path, exdir = tempdir())
pbmc.data <- Read10X(data.dir = file)
pbmc <- CreateSeuratObject(
    counts = pbmc.data,
    project = "pbmc3k",
    min.cells = 3,
    min.features = 200
)
pbmc <- NormalizeData(pbmc)
pbmc <- FindVariableFeatures(pbmc, selection.method = "vst", nfeatures = 2000)
pbmc <- ScaleData(pbmc, features = rownames(pbmc))
pbmc <- RunPCA(pbmc)
pbmc <- RunUMAP(pbmc, dim = 1:10)
pbmc <- FindNeighbors(pbmc, dims = 1:10)
pbmc <- FindClusters(pbmc, resolution = c(0.310.5))
markers <- tibble::tribble(
    ~type, ~marker,
    "Naive CD4+ T""IL7R,CCR7",
    "CD14+ Mono""CD14,LYZ",
    "Memory CD4+""IL7R,S100A4",
    "B""MS4A1",
    "CD8+ T""CD8A",
    "FCGR3A+ Mono""FCGR3A,MS4A7",
    "NK""GNLY,NKG7",
    "DC""FCER1A,CST3",
    "Platelet""PPBP",
) %>%
    tidyr::separate_rows(marker, sep = ", *") %>%
    dplyr::distinct()
# Dotplot
DotPlot(pbmc, features = unique(markers$marker)) + coord_flip()
# contrast with DotPlot
SectorPlot(
    pbmc, markers$marker,
    features_level = unique(rev(markers$marker))
)

写在最后

「ggsector」的基本雏形就是这样了,当然本人后续还有一些其他打算,比如
1.自定义圆周散点数,增加扇形的精度,
2.在扇形的基础上尝试一下物种组成百分比的pie。

总之使用过程中观众老爷们有何问题, 可以在我的github中
https://github.com/yanpd01/ggsector/issues
留下你的issues,中英文提问都可以的。

新人上路,请大家多多支持。


【声明】内容源于网络
0
0
R语言数据分析指南
R语言重症爱好者,喜欢绘制各种精美的图表,喜欢的小伙伴可以关注我,跟我一起学习
内容 1180
粉丝 0
R语言数据分析指南 R语言重症爱好者,喜欢绘制各种精美的图表,喜欢的小伙伴可以关注我,跟我一起学习
总阅读523
粉丝0
内容1.2k