web-dev-qa-db-fra.com

diviser une variable continue en groupes de taille égale

Je dois diviser/diviser une variable continue en 3 groupes de taille égale. 

Exemple de bloc de données

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

Après avoir été découpé (en fonction de la valeur de wt), il me faudrait les 3 classes sous la nouvelle variable wt2 comme ceci:

> 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

Ceci serait appliqué à un grand ensemble de données

43
baz

essaye ça:

split(das, cut(das$anim, 3))

si vous voulez diviser en fonction de la valeur de wt, alors

library(Hmisc) # cut2
split(das, cut2(das$wt, g=3))

quoi qu'il en soit, vous pouvez le faire en combinant cut, cut2 et split.

MIS &AGRAVE; JOUR

si vous voulez un index de groupe comme une colonne supplémentaire, alors

das$group <- cut(das$anim, 3)

si la colonne doit être indexée comme 1, 2, ..., alors

das$group <- as.numeric(cut(das$anim, 3))

MISE À JOUR DE NOUVEAU

essaye ça:

> 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
54
kohske

Ou consultez cut_number dans le package ggplot2, par exemple.

das$wt_2 <- as.numeric(cut_number(das$wt,3))

Notez que cut(...,3) divise la plage des données d'origine en trois plages de longueurs égales; cela ne donne pas nécessairement le même nombre de observations par groupe si les données sont distribuées de manière inégale (vous pouvez reproduire ce que cut_number fait en utilisant quantile de manière appropriée, mais c'est une fonction pratique de Nice). D'autre part, Hmisc::cut2() utilisant l'argument g=fait divisé par quantiles, est plus ou moins équivalent à ggplot2::cut_number. J'aurais peut-être pensé que quelque chose comme cut_number aurait été introduit dans dplyr jusqu'à présent, mais autant que je sache, cela n'a pas été le cas .

29
Ben Bolker

ntile de dplyr le fait maintenant mais se comporte bizarrement avec les NA.

J'ai utilisé un code similaire dans la fonction suivante qui fonctionne en base R et fait l'équivalent de la solution cut2 ci-dessus:

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)
}
3
Dan Lewer

Voici une autre solution utilisant la fonction bin_data() de mltools package.

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)
3
Ben

Alternative sans en utilisant cut2.

das$wt2 <- as.factor( as.numeric( cut(das$wt,3)))

ou 

das$wt2 <- as.factor( cut(das$wt,3, labels=F))
3
pedrosaurio

cut, lorsque les points de rupture ne sont pas explicitement divisés en valeurs de même largeur, ils ne contiennent généralement pas le même nombre d'éléments:

x <- c(1:4,10)
lengths(split(x, cut(x, 2)))
# (0.991,5.5]    (5.5,10] 
#           4           1 

Hmisc::cut2 et ggplot2::cut_number utilisent des quantiles, qui créeront généralement des groupes de même taille (en termes de nombre d'éléments) si les données sont bien réparties et de taille correcte, ce n'est cependant pas toujours le cas. mltools::bin_data peut donner des résultats différents mais est également basé sur des quantiles.

Ces fonctions ne donnent pas toujours des résultats précis lorsque les données contiennent un petit nombre de valeurs distinctes:

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

Ce n'est pas clair si la solution optimale a été trouvée ici.

Quelle est l'approche best de binning est une question subjective, mais une façon raisonnable de l'aborder est de rechercher les bacs minimisant la variance autour de la taille de bac prévue.

La fonction smart_cut de (mon) package cutr propose une telle fonctionnalité. Cependant, il est lourd en calcul et devrait être réservé aux cas où les points de coupe et les valeurs uniques sont peu nombreux (ce qui est généralement le cas où cela compte).

# 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 

Nous constatons que les groupes sont beaucoup mieux équilibrés.

"balanced" dans l'appel peut en fait être remplacé par une fonction personnalisée pour optimiser ou restreindre les bacs à volonté si la méthode basée sur la variance ne suffit pas.

2
Moody_Mudskipper

Si vous souhaitez diviser en 3 groupes également répartis, la réponse est la même que La réponse de Ben Bolker ci-dessus - utilisez ggplot2::cut_number(). Par souci d’achèvement, voici les 3 méthodes de conversion continue en catégorique (binning).

  • cut_number(): Crée n groupes avec un nombre d'observation (approximativement) égal
  • cut_interval(): crée n groupes avec une plage égale
  • cut_width(): crée des groupes de largeur width Mon adresse préférée est cut_number () car elle utilise des quantiles régulièrement espacés pour les observations de tri. Voici un exemple avec des données asymétriques.
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()

Créé le 2018-11-01 par le paquet reprex (v0.2.1)

0
Matt Dancho

Sans forfait supplémentaire, 3 étant le nombre de groupes:

> 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

Vous pouvez accélérer le calcul du quantile en utilisant un échantillon représentatif des valeurs d'intérêt. Vérifiez la documentation de la fonction FindInterval.

0
SamGG