Déclaration de cas équivalente à R

J’ai une variable dans un dataframe où l’un des champs a généralement 7-8 valeurs. Je souhaite les regrouper en 3 ou 4 nouvelles catégories dans une nouvelle variable du dataframe. Quelle est la meilleure approche?

J’utiliserais une instruction CASE si j’étais dans un outil de type SQL mais je ne savais pas comment l’attaquer dans R.

Toute aide que vous pouvez apporter sera très appréciée!

memisc la fonction de cases partir du paquet memisc . Il implémente la fonctionnalité de cas avec deux manières différentes de l’utiliser. Parmi les exemples dans le package:

 z1=cases( "Condition 1"=x<0, "Condition 2"=y<0,# only applies if x >= 0 "Condition 3"=TRUE ) 

x et y sont deux vecteurs.

Références: paquet memisc , exemple de cas

Si vous avez un factor vous pouvez modifier les niveaux par méthode standard:

 df <- data.frame(name = c('cow','pig','eagle','pigeon'), stringsAsFactors = FALSE) df$type <- factor(df$name) # First step: copy vector and make it factor # Change levels: levels(df$type) <- list( animal = c("cow", "pig"), bird = c("eagle", "pigeon") ) df # name type # 1 cow animal # 2 pig animal # 3 eagle bird # 4 pigeon bird 

Vous pourriez écrire une fonction simple comme un wrapper:

 changelevels <- function(f, ...) { f <- as.factor(f) levels(f) <- list(...) f } df <- data.frame(name = c('cow','pig','eagle','pigeon'), stringsAsFactors = TRUE) df$type <- changelevels(df$name, animal=c("cow", "pig"), bird=c("eagle", "pigeon")) 

Voici un moyen d’utiliser l’instruction switch :

 df <- data.frame(name = c('cow','pig','eagle','pigeon'), stringsAsFactors = FALSE) df$type <- sapply(df$name, switch, cow = 'animal', pig = 'animal', eagle = 'bird', pigeon = 'bird') > df name type 1 cow animal 2 pig animal 3 eagle bird 4 pigeon bird 

Le seul inconvénient est que vous devez continuer à écrire le nom de la catégorie ( animal , etc.) pour chaque article. Il est syntaxiquement plus pratique de pouvoir définir nos catégories comme ci-dessous (voir la question très similaire Comment append une colonne dans un bloc de données en R )

 myMap <- list(animal = c('cow', 'pig'), bird = c('eagle', 'pigeon')) 

et nous voulons en quelque sorte "inverser" cette cartographie. J'écris ma propre fonction invMap:

 invMap <- function(map) { items <- as.character( unlist(map) ) nams <- unlist(Map(rep, names(map), sapply(map, length))) names(nams) <- items nams } 

puis inversez la carte ci-dessus comme suit:

 > invMap(myMap) cow pig eagle pigeon "animal" "animal" "bird" "bird" 

Et puis, il est facile d'utiliser ceci pour append la colonne de type dans le frame de données:

 df <- transform(df, type = invMap(myMap)[name]) > df name type 1 cow animal 2 pig animal 3 eagle bird 4 pigeon bird 

case_when() , qui a été ajouté à dplyr en mai 2016, résout ce problème d’une manière similaire à memisc::cases() .

Par exemple:

 library(dplyr) mtcars %>% mutate(category = case_when( .$cyl == 4 & .$disp < median(.$disp) ~ "4 cylinders, small displacement", .$cyl == 8 & .$disp > median(.$disp) ~ "8 cylinders, large displacement", TRUE ~ "other" ) ) 

Imho, code le plus simple et universel:

 dft=data.frame(x = sample(letters[1:8], 20, replace=TRUE)) dft=within(dft,{ y=NA y[x %in% c('a','b','c')]='abc' y[x %in% c('d','e','f')]='def' y[x %in% 'g']='g' y[x %in% 'h']='h' }) 

Je ne vois aucune proposition pour «switch». Exemple de code (lancez-le):

 x <- "three"; y <- 0; switch(x, one = {y <- 5}, two = {y <- 12}, three = {y <- 432}) y 

Vous pouvez utiliser recode à partir du package de voiture:

 library(ggplot2) #get data library(car) daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10] 

Il y a une déclaration de switch mais je ne peux jamais sembler le faire fonctionner comme je le pense. Comme vous n’avez pas fourni d’exemple, je vais en faire un en utilisant une variable factorielle:

  dft <-data.frame(x = sample(letters[1:8], 20, replace=TRUE)) levels(dft$x) [1] "a" "b" "c" "d" "e" "f" "g" "h" 

Si vous spécifiez les catégories de votre choix dans un ordre approprié à la réaffectation, vous pouvez utiliser les variables factorielles ou numériques comme index:

 c("abc", "abc", "abc", "def", "def", "def", "g", "h")[dft$x] [1] "def" "h" "g" "def" "def" "abc" "h" "h" "def" "abc" "abc" "abc" "h" "h" "abc" [16] "def" "abc" "abc" "def" "def" dft$y <- c("abc", "abc", "abc", "def", "def", "def", "g", "h")[dft$x] str(dft) 'data.frame': 20 obs. of 2 variables: $ x: Factor w/ 8 levels "a","b","c","d",..: 4 8 7 4 6 1 8 8 5 2 ... $ y: chr "def" "h" "g" "def" ... 

J'ai appris plus tard qu'il existe deux fonctions de commutation différentes. Ce n'est pas une fonction générique mais vous devriez y penser comme switch.numeric ou switch.character . Si votre premier argument est un «facteur» R, vous obtenez switch.numeric comportement switch.numeric , susceptible de poser problème, car la plupart des personnes voient les facteurs affichés comme des caractères et supposent que toutes les fonctions les traiteront comme telles.

Je n’aime pas l’un d’eux, ils ne sont pas clairs pour le lecteur ou l’utilisateur potentiel. J’utilise juste une fonction anonyme, la syntaxe n’est pas aussi fine qu’une déclaration de cas, mais l’évaluation est similaire à une déclaration de cas et pas si pénible. cela suppose également que vous l’évaluiez à l’endroit où vos variables sont définies.

 result <- ( function() { if (x==10 | y< 5) return('foo') if (x==11 & y== 5) return('bar') })() 

tous ceux () sont nécessaires pour entourer et évaluer la fonction anonyme.

Mélanger plyr::mutate et dplyr::case_when fonctionne pour moi et est lisible.

 iris %>% plyr::mutate(coolness = dplyr::case_when(Species == "setosa" ~ "not cool", Species == "versicolor" ~ "not cool", Species == "virginica" ~ "super awesome", TRUE ~ "undetermined" )) -> testIris head(testIris) levels(testIris$coolness) ## NULL testIris$coolness <- as.factor(testIris$coolness) levels(testIris$coolness) ## ok now testIris[97:103,4:6] 

Points bonus si la colonne peut sortir de la mutation comme un facteur plutôt que de la char! La dernière ligne de l'instruction case_when, qui détecte toutes les lignes non appariées, est très importante.

  Petal.Width Species coolness 97 1.3 versicolor not cool 98 1.3 versicolor not cool 99 1.1 versicolor not cool 100 1.3 versicolor not cool 101 2.5 virginica super awesome 102 1.9 virginica super awesome 103 2.1 virginica super awesome 

Une déclaration de cas pourrait ne pas être la bonne approche ici. S’il s’agit d’un facteur probable, il suffit de définir les niveaux du facteur de manière appropriée.

Disons que vous avez un facteur avec les lettres A à E, comme ceci.

 > a <- factor(rep(LETTERS[1:5],2)) > a [1] ABCDEABCDE Levels: ABCDE 

Pour joindre les niveaux B et C et le nommer BC, changez simplement les noms de ces niveaux en BC.

 > levels(a) <- c("A","BC","BC","D","E") > a [1] A BC BC DEA BC BC DE Levels: A BC DE 

Le résultat est tel que désiré.

Si vous voulez avoir une syntaxe de type sql, vous pouvez simplement utiliser le paquet sqldf . La fonction à utiliser est également sqldf et la syntaxe est la suivante

 sqldf() 

Vous pouvez utiliser la merge fonctions de base pour les tâches de remappage de casse:

 df <- data.frame(name = c('cow','pig','eagle','pigeon','cow','eagle'), stringsAsFactors = FALSE) mapping <- data.frame( name=c('cow','pig','eagle','pigeon'), category=c('animal','animal','bird','bird') ) merge(df,mapping) # name category # 1 cow animal # 2 cow animal # 3 eagle bird # 4 eagle bird # 5 pig animal # 6 pigeon bird 

J’utilise dans ces cas vous faites référence à switch() . Cela ressemble à une instruction de contrôle mais en réalité, c’est une fonction. L’expression est évaluée et sur la base de cette valeur, l’élément correspondant dans la liste est renvoyé.

Le commutateur fonctionne de deux manières différentes selon que le premier argument est évalué par une chaîne de caractères ou un nombre.

Ce qui suit est un exemple de chaîne simple qui résout votre problème de réduire les anciennes catégories en nouvelles.

Pour la forme chaîne de caractères, définissez un seul argument non nommé comme valeur par défaut après les valeurs nommées.

 newCat <- switch(EXPR = category, cat1 = catX, cat2 = catX, cat3 = catY, cat4 = catY, cat5 = catZ, cat6 = catZ, "not available")