Zero's Blog - R
https://l2dy.sourceforge.io/tag/R/
-
编译 R for Data Science
https://l2dy.sourceforge.io/2022/04/30/r4ds-build-book.html
2022-04-30T16:59:00+00:00
首先用 conda 创建一个新环境并安装 R。conda create -n r4ds_r4.1 r=4.1 r-devtools r-terra
conda activate r4ds_r4.1
rstudio然后在 RStudio 的 Console 中执行以下命令,用 devtools 安装编译 r4ds 所需的包。devtools::install_github("hadley/r4ds", dependencies = TRUE)最后在 RStudio 中打开 r4ds.Rproj 项目,在菜单里选择 Build → Build All。Update(2022-05-10): 用 conda 预先安装 DESCRIPTION 文件中提到的包可以减少 devtools 安装命令的耗时,因为可以跳过一部分包的编译。
-
R 时间戳转换为日期
https://l2dy.sourceforge.io/2022/04/24/r-datetime-to-date.html
2022-04-24T15:30:27+00:00
如果时间戳(date-time)都是 UTC 时区的话,可以使用 base 的 as.Date 函数。如果时间戳时区不一致或非 UTC 时区,用 lubridate 包的 as_date 可以获得在对应时区下的日期,但性能比 as.Date 稍差。
-
ggplot2 画百分比饼图
https://l2dy.sourceforge.io/2022/04/22/ggplot2-pie-chart-with-percentage.html
2022-04-22T15:28:00+00:00
首先通过 count() 计算出各种 cut 的钻石各有多少个,存到名为 n 的列里,然后通过 mutate 计算出总和为1的占比。此时如果用 geom_col() 画图可以得出一个高度为1的堆积图。library(tidyverse)
diamonds %>%
count(cut) %>%
mutate(p = n / sum(n)) %>%
ggplot(aes("", p, fill = cut)) +
geom_col() +
scale_fill_brewer(type = "div")再加以 coord_polar() 变换转换为极座标,就可以画出一张饼图了。在此基础上多叠加一层 geom_text() 就实现了百分比的显示,非常灵活。diamonds %>%
count(cut) %>%
mutate(p = n / sum(n)) %>%
mutate(labels = scales::label_percent(accuracy = 0.01)(n / sum(n))) %>%
ggplot(aes("", p, fill = cut)) +
geom_col() +
geom_text(aes(label = labels), position = position_stack(0.5)) +
coord_polar("y") +
scale_fill_brewer(type = "div")如果不想显示极座标轴和灰色背景,可以通过加上 theme_void() 来实现。对于重叠的文字则是可以用 ggrepel 打散,最终效果如图。
-
展开 Tiddle 中的 JSON 字段
https://l2dy.sourceforge.io/2022/04/20/tidyverse-unnest-json.html
2022-04-20T15:47:20+00:00
tidyr 本身就提供了 unnest() 系列函数,可以用于展开嵌套的数据结构。配合 fromJSON() 使用就可以实现 JSON 字段的展开。library(tidyverse)
library(jsonlite)
spread_all <- function(data, cols) {
data %>%
mutate(across(all_of(cols), function(x)
map(x, fromJSON))) %>%
unnest_wider(all_of(cols))
}
# 用法
df %>%
spread_all(c("x", "y"))为了加速 JSON 的解析,我们可以引入 furrr 包来实现并发解析。library(furrr)
# make future_map parallel
plan(multisession)
spread_all <- function(data, cols) {
data %>%
mutate(across(all_of(cols), function(x)
future_map(x, fromJSON))) %>%
unnest_wider(all_of(cols))
}
-
R Markdown 跳过中间变量缓存结果
https://l2dy.sourceforge.io/2022/04/17/knitr-cache-skip-intermediate.html
2022-04-17T04:04:00+00:00
当要从一个很大的数据集中取子集进行分析,且不想生成中间状态的 csv 文件只用于快速生成报告时,你就需要用到 knitr 的缓存功能了。knitr 默认的缓存就是 lazy 的,所以如果后面的代码只使用了这个子集的数据,直接在代码块上加上 cache = TRUE 配置就可以避免每次渲染都加载一次这个大数据集。如果这个代码块的数据和输出格式没有相关性的话,建议还加上 cache.path = "cache/" 这个配置,可以在切换输出格式的情况下也共用缓存。缓存的自动刷新条件可以用 cache.extra 控制,具体用法可参考 rmarkdown-cookbook 的文档。然而,在数据集非常大的情况下,你可能会碰到 long vectors not supported yet 这个报错。这种情况下 knitr 默认的缓存机制已经不能满足需求了,我们需要更灵活的可以定制化的缓存。此时我们可以用 xfun::cache_rds() 来实现:```{r res, cache.path = "cache/"}
res <- xfun::cache_rds({
cars <- reda_csv(files)
cars %>%
filter(model = y)
}, name = "res.rds", hash = list(files, y))
```xfun::cache_rds() 的第一个参数是要缓存的表达式,在首次执行完成后结果会被缓存并赋值给 res,再下一次执行时就可以直接从缓存中加载变量赋值给 res 了。name 参数用于在非 knitr 环境(例如在 RStudio 中跑代码块)下和 knitr 环境复用同一个缓存文件,文件名需要和代码块的标签保持一致。注意还需要保证两种环境下 dir 参数一致,在 knitr 环境下 dir 参数默认值为 cache.path 的值,非 knitr 环境下默认为 cache/。这也是使用 xfun::cache_rds() 独有的优势,可以在 RStudio 的 notebook 环境下复用 knitr 的缓存。hash 参数会影响缓存的文件名,所以一旦 hash 的内容发生变化就会重新计算,相当于 cache.extra。这样就实现了跳过中间生成的大数据集 cars 直接缓存最终结果,非常方便。Update (2022-04-22): 截至目前 RStudio 仍未支持在 R Notebook 环境下复用 knitr 缓存,见 rstudio/rstudio#9291。
-
Tidyverse 读取多个同格式 csv 文件
https://l2dy.sourceforge.io/2022/04/17/tidyverse-read-multiple-csv-files.html
2022-04-17T03:44:00+00:00
如何将多个同样格式的 csv 文件合并读取到一个 tibble 里呢?很自然的会想到先用 read_csv() 分别读取每个文件,再用 reduce(rbind) 聚合到一起。这样确实能用,但对于大数据集来说性能很差。readr 从 2.0.0 开始原生支持同时读取多个文件的功能,只需要把文件名字符串向量里传给 file 参数就行。实测在数据量很大的时候和 rbind 相比可以显著减少读取时间。这个功能在 readr 包的文档中没有描述,导致我走了不少弯路才发现 readr 原生就支持多文件读取。另外 vroom 从 1.0.0 开始就原生支持了这个功能,用法一样不过 vroom 对于文件中的字符数据是懒加载的,在某些场景下可以提升性能。具体能否提升性能要看使用方式了,可以用 system.time({}) 实测一下。