62 votes

Diviser une variable continue en groupes de taille égale

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

Exemple de tableau 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), j'aurais besoin d'avoir 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

Cela serait appliqué à un grand ensemble de données.

1 votes

Êtes-vous sûr que la réponse de @Ben Bolker n'est pas la bonne ? Vous spécifiez que vous voulez des groupes de tailles égales.

78voto

Ben Bolker Points 50041

Ou voir cut_number du package ggplot2, par exemple.

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

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

70voto

kohske Points 30437

Essayez ceci :

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

de toute façon, vous pouvez le faire en combinant cut, cut2 et split.

À JOUR

si vous souhaitez un index de groupe en tant que 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))

ENCORE MISE À JOUR

essayez ceci :

> 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

11voto

Ben Gorman Points 1075

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

library(mltools)

# Les groupes résultants ont un nombre égal d'observations dans chaque groupe
das[, "wt2"] <- bin_data(das$wt, bins=3, binType = "quantile")

# Les groupes résultants sont également espacés du minimum au maximum
das[, "wt3"] <- bin_data(das$wt, bins=3, binType = "explicit")

# Ou si vous préférez définir les groupes vous-même
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)

11voto

Matt Dancho Points 2211

Si vous voulez 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(). Pour des raisons de complétude, voici les 3 méthodes pour transformer des données continues en catégorielles (binning).

  • cut_number() : Crée n groupes avec un nombre d'observations (approximativement) égal
  • cut_interval() : Crée n groupes avec une plage égale
  • cut_width() : Crée des groupes de largeur

Mon choix est cut_number() car il utilise des quantiles régulièrement espacés pour le binning des observations. Voici un exemple avec des données biaisées.

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
#>                                             
#>  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.  Médiane  Moyenne 3e Qu.    Max. 
#>    1.00    3.00   13.00   25.75   42.00  100.00

# Histogramme montrant le biais
skewed_tbl %>%
    ggplot(aes(counts)) +
    geom_histogram(bins = 30)

# cut_number() distribue uniformément les observations en bacs par quantile
skewed_tbl %>%
    ggplot(aes(counts_cut_number)) +
    geom_bar()

# cut_interval() divise uniformément l'intervalle à travers la plage
skewed_tbl %>%
    ggplot(aes(counts_cut_interval)) +
    geom_bar()

# cut_width() utilise la largeur = 25 pour créer des bacs de 25 de largeur
skewed_tbl %>%
    ggplot(aes(counts_cut_width)) +
    geom_bar()

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

8voto

pedrosaurio Points 824

Alternative sans utiliser cut2.

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

ou

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

Comme l'a souligné @ben-bolker, cela se divise en largeurs égales plutôt qu'en occupation. Je pense qu'en utilisant quantiles, on peut approximer l'égalité d'occupation

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 #pour 1/3, 4 pour 1/4, 100 pour 1/100 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

7 votes

Je pense que cela se divise en bacs de largeur égale plutôt qu'en bacs d'occupation égale ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X