Разделение непрерывной переменной на группы равного размера
Мне нужно разделить/разделить непрерывную переменную на 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
) мне нужно будет иметь 3 класса под новой переменной wt2
следующим образом:
> 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
Это применимо к большому набору данных
Ответы
Ответ 1
попробуйте следующее:
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
Ответ 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
, но насколько я могу сказать, что он не.
Ответ 3
Вот еще одно решение с использованием функции 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)
Ответ 4
Альтернатива без использования 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
Ответ 5
ntile
from dplyr
теперь делает это, но ведет себя странно с NA
.
Я использовал аналогичный код в следующей функции, которая работает в базе 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)
}
Ответ 6
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
Это не ясно, было ли найдено оптимальное решение здесь.
То, что является лучшим подходом биннинга, является субъективным вопросом, но один разумный способ приблизиться к нему - это искать бины, которые минимизируют дисперсию вокруг ожидаемого размера бинов.
Функция 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"
в вызове фактически может быть заменен пользовательской функцией для оптимизации или ограничения бинов по желанию, если недостаточно метода, основанного на дисперсии.
Ответ 7
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
Ответ 8
Для этого вы также можете использовать функцию bin
с method = "content"
из пакета OneR
:
library(OneR)
das$wt_2 <- as.numeric(bin(das$wt, nbins = 3, method = "content"))
das
## anim wt wt_2
## 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
Ответ 9
Без дополнительного пакета, 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
.
Ответ 10
Если вы хотите разделить на 3 равномерно распределенные группы, ответ такой же, как и у ответа Бена Болкера выше - используйте ggplot2::cut_number()
. Для завершенности здесь приведены 3 метода преобразования непрерывного в категориальный (биннинг).
-
cut_number()
: делает n групп с (приблизительно) равным количеством наблюдений -
cut_interval()
: делает n групп с одинаковым диапазоном -
cut_width()
: создает группы ширины ширины. Мое движение - это 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()
![]()
Создано 2018-11-01 пакетом представлением (v0.2.1)