разбиение непрерывной переменной на группы равных размеров - PullRequest
48 голосов
/ 24 мая 2011

Мне нужно разделить / разделить непрерывную переменную на 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

Это будет применяться к большому набору данных

Ответы [ 9 ]

57 голосов
/ 24 мая 2011

попробуйте это:

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
34 голосов
/ 01 ноября 2011

Или см. 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, но, насколько я могу судить, не имеет .

4 голосов
/ 13 июля 2017

Вот еще одно решение с использованием функции 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 голосов
/ 05 октября 2011

Альтернатива без с использованием 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
3 голосов
/ 15 октября 2016

ntile из 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)
}
2 голосов
/ 14 сентября 2018

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" в вызове фактически может быть заменено пользовательской функцией для оптимизации или ограничения бинов по желанию, если недостаточно метода, основанного на дисперсии.

0 голосов
/ 10 апреля 2019

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 
0 голосов
/ 01 ноября 2018

Если вы хотите разделить на 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)

0 голосов
/ 17 декабря 2017

Без дополнительного пакета, 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.

...