2022年4月

首先用 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 安装命令的耗时,因为可以跳过一部分包的编译。

虽然最理想的情况是所有依赖的项目都分别打包到不同的 port,但不同包对同一个依赖项目可能有不兼容的要求。OpenSSL、Boost 这种基础包可以通过拆包来解决兼容问题,例如 Boost 在 MacPorts 就打包了 boost169boost171boost173 等等很多个版本,但这也要求所有这些子包的使用方都要对编译 flags 进行修改,才能让编译器找到正确版本的依赖,导致整个过程非常复杂,维护也非常耗费时间。

一般使用方不多的库不会有分版本的 subport,为了提供一个小众软件要求的版本实现多版本并存有点得不偿失。此时如果项目的构建系统本来就把依赖 vendor 到 Git 子模块里,我们就可以通过 distfiles 的手段把依赖库的源代码拉下来放到子模块的文件夹里。通过 master_sitesdistfiles-appendgithub 1.0 等关键字进行组合搜索可以找到 macports-ports 中可以参考的方案,最终效果如下。

PortGroup           github  1.0
PortGroup           meson   1.0

github.setup        joshkunz ashuffle 3.13.3 v
github.tarball_from archive
master_sites        ${github.master_sites}:ashuffle
distfiles           ${distname}${extract.suffix}:ashuffle

...

# BEGIN abseil (requires C++17 build)
set abseil_project  abseil-cpp
set abseil_version  20211102.0

master_sites-append https://github.com/abseil/${abseil_project}/archive/${abseil_version}:abseil
distfiles-append    ${abseil_project}-${abseil_version}${extract.suffix}:abseil
checksums-append    ${abseil_project}-${abseil_version}${extract.suffix} \
                    rmd160  bca4a16eaab1602cdc7ace8dd1ff82467b71b59e \
                    sha256  dcf71b9cba8dc0ca9940c4b316a0c796be8fab42b070bb6b7cab62b48f0e66c4 \
                    size    1884080

post-extract {
    foreach submodule [list subprojects/absl:${abseil_project}] {
        set submodule_target [lindex [split ${submodule} :] 0]
        set submodule_package [lindex [split ${submodule} :] 1]
        delete ${worksrcpath}/${submodule_target}
        move {*}[glob ${workpath}/${submodule_package}-*] ${worksrcpath}/${submodule_target}
    }
}
# END abseil

如果时间戳(date-time)都是 UTC 时区的话,可以使用 base 的 as.Date 函数。

如果时间戳时区不一致或非 UTC 时区,用 lubridate 包的 as_date 可以获得在对应时区下的日期,但性能比 as.Date 稍差。

首先通过 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")

p1.png

再加以 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")

p2.png

如果不想显示极座标轴和灰色背景,可以通过加上 theme_void() 来实现。对于重叠的文字则是可以用 ggrepel 打散,最终效果如图。

p3.png

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))
}