我需要将连续变量拆分/分成3个相等大小的组。
示例数据框
das <- data.frame(anim=1:15,
wt=c(181,179,180.5,201,201.5,245,246.4,
189.3,301,354,369,205,199,394,231.3))
在被切断之后(根据wt
的值),我需要在新变量wt2
下有3个类,如下所示:
> das
anim wt wt2
1 1 181.0 1
2 2 179.0 1
3 3 180.5 1
4 4 201.0 2
5 5 201.5 2
6 6 245.0 2
7 7 246.4 3
8 8 189.3 1
9 9 301.0 3
10 10 354.0 3
11 11 369.0 3
12 12 205.0 2
13 13 199.0 1
14 14 394.0 3
15 15 231.3 2
这将应用于大型数据集
试试这个:
split(das, cut(das$anim, 3))
如果你想根据wt
的值进行拆分,那么
library(Hmisc) # cut2
split(das, cut2(das$wt, g=3))
无论如何,你可以通过组合cut
,cut2
和split
来做到这一点。
更新
如果您想将组索引作为附加列,那么
das$group <- cut(das$anim, 3)
如果列应该像1,2,...那样索引
das$group <- as.numeric(cut(das$anim, 3))
再次更新
试试这个:
> das$wt2 <- as.numeric(cut2(das$wt, g=3))
> das
anim wt wt2
1 1 181.0 1
2 2 179.0 1
3 3 180.5 1
4 4 201.0 2
5 5 201.5 2
6 6 245.0 2
7 7 246.4 3
8 8 189.3 1
9 9 301.0 3
10 10 354.0 3
11 11 369.0 3
12 12 205.0 2
13 13 199.0 1
14 14 394.0 3
15 15 231.3 2
或者参见cut_number
包中的ggplot2
,例如:
das$wt_2 <- as.numeric(cut_number(das$wt,3))
请注意,cut(...,3)
将原始数据的范围划分为三个相等长度的范围;如果数据分布不均匀,它不一定会导致每组观察次数相同(你可以通过恰当地使用cut_number
复制quantile
所做的事情,但这是一个很好的便利功能)。另一方面,使用Hmisc::cut2()
参数的g=
确实按分位数分割,因此或多或少等同于ggplot2::cut_number
。我可能认为到目前为止像cut_number
这样的东西会进入dplyr
,但是as far as I can tell it hasn't。
这是使用bin_data()
包中的mltools函数的另一种解决方案。
library(mltools)
# Resulting bins have an equal number of observations in each group
das[, "wt2"] <- bin_data(das$wt, bins=3, binType = "quantile")
# Resulting bins are equally spaced from min to max
das[, "wt3"] <- bin_data(das$wt, bins=3, binType = "explicit")
# Or if you'd rather define the bins yourself
das[, "wt4"] <- bin_data(das$wt, bins=c(-Inf, 250, 322, Inf), binType = "explicit")
das
anim wt wt2 wt3 wt4
1 1 181.0 [179, 200.333333333333) [179, 250.666666666667) [-Inf, 250)
2 2 179.0 [179, 200.333333333333) [179, 250.666666666667) [-Inf, 250)
3 3 180.5 [179, 200.333333333333) [179, 250.666666666667) [-Inf, 250)
4 4 201.0 [200.333333333333, 245.466666666667) [179, 250.666666666667) [-Inf, 250)
5 5 201.5 [200.333333333333, 245.466666666667) [179, 250.666666666667) [-Inf, 250)
6 6 245.0 [200.333333333333, 245.466666666667) [179, 250.666666666667) [-Inf, 250)
7 7 246.4 [245.466666666667, 394] [179, 250.666666666667) [-Inf, 250)
8 8 189.3 [179, 200.333333333333) [179, 250.666666666667) [-Inf, 250)
9 9 301.0 [245.466666666667, 394] [250.666666666667, 322.333333333333) [250, 322)
10 10 354.0 [245.466666666667, 394] [322.333333333333, 394] [322, Inf]
11 11 369.0 [245.466666666667, 394] [322.333333333333, 394] [322, Inf]
12 12 205.0 [200.333333333333, 245.466666666667) [179, 250.666666666667) [-Inf, 250)
13 13 199.0 [179, 200.333333333333) [179, 250.666666666667) [-Inf, 250)
14 14 394.0 [245.466666666667, 394] [322.333333333333, 394] [322, Inf]
15 15 231.3 [200.333333333333, 245.466666666667) [179, 250.666666666667) [-Inf, 250)
不使用cut2的替代方案。
das$wt2 <- as.factor( as.numeric( cut(das$wt,3)))
要么
das$wt2 <- as.factor( cut(das$wt,3, labels=F))
正如@ ben-bolker所指出的那样,它会分成相等宽度而不是占用率。我认为使用quantiles
可以近似等于占用率
x = rnorm(10)
x
[1] -0.1074316 0.6690681 -1.7168853 0.5144931 1.6460280 0.7014368
[7] 1.1170587 -0.8503069 0.4462932 -0.1089427
bin = 3 #for 1/3 rd, 4 for 1/4, 100 for 1/100th etc
xx = cut(x, quantile(x, breaks=1/bin*c(1:bin)), labels=F, include.lowest=T)
table(xx)
1 2 3 4
3 2 2 3
来自ntile
的dplyr
现在做到这一点,但与NA
的表现奇怪。
我在以下函数中使用了类似的代码,它在base R中运行,并且相当于上面的cut2
解决方案:
ntile_ <- function(x, n) {
b <- x[!is.na(x)]
q <- floor((n * (rank(b, ties.method = "first") - 1)/length(b)) + 1)
d <- rep(NA, length(x))
d[!is.na(x)] <- q
return(d)
}
cut
,当没有给出明确的断点时,将值分成相同宽度的区间,它们通常不包含相同数量的项目:
x <- c(1:4,10)
lengths(split(x, cut(x, 2)))
# (0.991,5.5] (5.5,10]
# 4 1
Hmisc::cut2
和ggplot2::cut_number
使用分位数,如果数据传播得很好并且尺寸合适,它通常会创建相同大小的组(按元素数量),但情况并非总是如此。 mltools::bin_data
可以给出不同的结果,但也基于分位数。
当数据包含少量不同的值时,这些函数并不总是给出整洁的结果:
x <- rep(c(1:20),c(15, 7, 10, 3, 9, 3, 4, 9, 3, 2,
23, 2, 4, 1, 1, 7, 18, 37, 6, 2))
table(x)
# x
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# 15 7 10 3 9 3 4 9 3 2 23 2 4 1 1 7 18 37 6 2
table(Hmisc::cut2(x, g=4))
# [ 1, 6) [ 6,12) [12,19) [19,20]
# 44 44 70 8
table(ggplot2::cut_number(x, 4))
# [1,5] (5,11] (11,18] (18,20]
# 44 44 70 8
table(mltools::bin_data(x, bins=4, binType = "quantile"))
# [1, 5) [5, 11) [11, 18) [18, 20]
# 35 30 56 45
如果在此处找到最佳解决方案,则不清楚。
什么是最好的分箱方法是主观问题,但接近它的一种合理方法是寻找最小化预期箱尺寸附近的方差的箱。
来自(my)package smart_cut
的函数cutr
提出了这样的特性。它虽然计算量很大,但应该保留给切割点和唯一值很少的情况(通常情况下它很重要)。
# devtools::install_github("moodymudskipper/cutr")
table(cutr::smart_cut(x, list(4, "balanced"), "g"))
# [1,6) [6,12) [12,18) [18,20]
# 44 44 33 45
我们看到这些团体更加平衡。
如果基于方差的方法不够,则调用中的"balanced"
实际上可以被自定义函数替换以根据需要优化或限制二进制位。
没有任何额外的包,3是组的数量:
> findInterval(das$wt, unique(quantile(das$wt, seq(0, 1, length.out = 3 + 1))), rightmost.closed = TRUE)
[1] 1 1 1 2 2 2 3 1 3 3 3 2 1 3 2
您可以使用感兴趣的值的代表性样本来加速分位数计算。仔细检查FindInterval
函数的文档。
如果你想分成3个平均分布的组,答案与Ben Bolker's answer above相同 - 使用ggplot2::cut_number()
。为了完成,这里有3种将连续转换为分类(binning)的方法。
cut_number()
:用大约相同数量的观察组成n组cut_interval()
:使n组具有相同的范围cut_width()
:制作宽度宽的组我的go-to是cut_number()因为它使用均匀间隔的分位数来进行分箱观察。这是一个数据偏斜的例子。library(tidyverse)
skewed_tbl <- tibble(
counts = c(1:100, 1:50, 1:20, rep(1:10, 3),
rep(1:5, 5), rep(1:2, 10), rep(1, 20))
) %>%
mutate(
counts_cut_number = cut_number(counts, n = 4),
counts_cut_interval = cut_interval(counts, n = 4),
counts_cut_width = cut_width(counts, width = 25)
)
# Data
skewed_tbl
#> # A tibble: 265 x 4
#> counts counts_cut_number counts_cut_interval counts_cut_width
#> <dbl> <fct> <fct> <fct>
#> 1 1 [1,3] [1,25.8] [-12.5,12.5]
#> 2 2 [1,3] [1,25.8] [-12.5,12.5]
#> 3 3 [1,3] [1,25.8] [-12.5,12.5]
#> 4 4 (3,13] [1,25.8] [-12.5,12.5]
#> 5 5 (3,13] [1,25.8] [-12.5,12.5]
#> 6 6 (3,13] [1,25.8] [-12.5,12.5]
#> 7 7 (3,13] [1,25.8] [-12.5,12.5]
#> 8 8 (3,13] [1,25.8] [-12.5,12.5]
#> 9 9 (3,13] [1,25.8] [-12.5,12.5]
#> 10 10 (3,13] [1,25.8] [-12.5,12.5]
#> # ... with 255 more rows
summary(skewed_tbl$counts)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 1.00 3.00 13.00 25.75 42.00 100.00
# Histogram showing skew
skewed_tbl %>%
ggplot(aes(counts)) +
geom_histogram(bins = 30)
# cut_number() evenly distributes observations into bins by quantile
skewed_tbl %>%
ggplot(aes(counts_cut_number)) +
geom_bar()
# cut_interval() evenly splits the interval across the range
skewed_tbl %>%
ggplot(aes(counts_cut_interval)) +
geom_bar()
# cut_width() uses the width = 25 to create bins that are 25 in width
skewed_tbl %>%
ggplot(aes(counts_cut_width)) +
geom_bar()
由reprex package创建于2018-11-01(v0.2.1)
来自equal_freq
的funModeling
采用矢量和箱数(基于相等的频率):
das <- data.frame(anim=1:15,
wt=c(181,179,180.5,201,201.5,245,246.4,
189.3,301,354,369,205,199,394,231.3))
das$wt_bin=funModeling::equal_freq(das$wt, 3)
table(das$wt_bin)
#[179,201) [201,246) [246,394]
# 5 5 5