如何将多列数据替换成自定义的区间?

刚刚有个小伙伴提了这样一个问题:

李老师,请教一下,我有很多列数据,现在我想按自己定义的区间将每个数字替换成所在的区间,怎么实现呢?

非常简单,可以先生成一个示例数据:

library(tidyverse)
tibble(x1 = runif(10) * 10,
x2 = runif(10) * 10,
x3 = runif(10) * 10) -> df
df

#> # A tibble: 10 × 3
#> x1 x2 x3
#> <dbl> <dbl> <dbl>
#> 1 7.16 1.31 6.29
#> 2 5.74 2.30 4.19
#> 3 4.18 5.88 8.32
#> 4 4.17 6.39 5.56
#> 5 9.82 4.03 3.33
#> 6 3.73 7.17 0.0762
#> 7 9.08 3.44 2.42
#> 8 4.08 8.47 7.32
#> 9 0.893 2.28 8.77
#> 10 0.651 7.89 7.04

这个示例数据有三列,符合提问者所说的很多列,然后我们需要按照自己定义的区间进行分段,通常会使用 cut() 函数,不过 cut() 函数的分段方式总会让人感觉很困惑,所以我更喜欢用不那么“省事”的 case_when() 函数,例如我们这里可以 0~1 分一段,1~5 分一段,然后其他的分一段,也就是 5~10,可以编写个分段函数:

cut_fun <- function(x) {
case_when(
x < 1 ~ "(0, 1)",
between(x, 1, 5) ~ "[1, 5]",
T ~ "(5, 10]"
)
}

between(x, 1, 5) 表示 1 <= x <= 5 。

然后就可以使用 mutate_all() 对所有列应用这个函数了:

df %>%
mutate_all(cut_fun)

#> # A tibble: 10 × 3
#> x1 x2 x3
#> <chr> <chr> <chr>
#> 1 (5, 10] [1, 5] [1, 5]
#> 2 [1, 5] [1, 5] [1, 5]
#> 3 [1, 5] (5, 10] [1, 5]
#> 4 [1, 5] [1, 5] (5, 10]
#> 5 [1, 5] (5, 10] [1, 5]
#> 6 (5, 10] (5, 10] [1, 5]
#> 7 (5, 10] (5, 10] (0, 1)
#> 8 (5, 10] (5, 10] [1, 5]
#> 9 [1, 5] [1, 5] (5, 10]
#> 10 (5, 10] [1, 5] (5, 10]

也可以使用 mutate_at() 对某几列应用:

df %>%
mutate_at(c("x1", "x2"), cut_fun)

# A tibble: 10 × 3
x1 x2 x3
<chr> <chr> <dbl>
1 (5, 10] [1, 5] 3.73
2 [1, 5] [1, 5] 2.25
3 [1, 5] (5, 10] 2.28
4 [1, 5] [1, 5] 9.86
5 [1, 5] (5, 10] 3.39
6 (5, 10] (5, 10] 3.36
7 (5, 10] (5, 10] 0.552
8 (5, 10] (5, 10] 1.14
9 [1, 5] [1, 5] 5.00
10 (5, 10] [1, 5] 9.53

也可以使用 mutate_if() 对数值变量应用:

df %>%
mutate_if(is.numeric, cut_fun)
#> # A tibble: 10 × 3
#> x1 x2 x3
#> <chr> <chr> <chr>
#> 1 (5, 10] [1, 5] [1, 5]
#> 2 [1, 5] [1, 5] [1, 5]
#> 3 [1, 5] (5, 10] [1, 5]
#> 4 [1, 5] [1, 5] (5, 10]
#> 5 [1, 5] (5, 10] [1, 5]
#> 6 (5, 10] (5, 10] [1, 5]
#> 7 (5, 10] (5, 10] (0, 1)
#> 8 (5, 10] (5, 10] [1, 5]
#> 9 [1, 5] [1, 5] (5, 10]
#> 10 (5, 10] [1, 5] (5, 10]

这样就解决了这个问题。

突发奇想,问问 chatGPT 如何解决这种问题:

挺老套的。。。 就 cut() 函数。

点击这里跳转到 RStata 短书平台获取附件:如何将多列数据替换成自定义的区间?

评论