J'ai une variable dans un dataframe où l'un des champs a généralement 7-8 valeurs. Je veux les réduire de 3 ou 4 nouvelles catégories dans une nouvelle variable dans le dataframe. Quelle est la meilleure approche?
J'utiliserais une instruction CASE si j'étais dans un outil de type SQL mais je ne sais pas comment attaquer cela dans R.
Toute aide que vous pouvez fournir sera très appréciée!
dput()
b) Voulez-vous une solution dans la base R, dplyr, data.table, tidyverse ...?Réponses:
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" ) )
À partir de dplyr 0.7.0,
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" ) )
la source
.$
devant chaque colonne..$
n'est plus nécessaire. Au moment où cette réponse a été écrite à l'origine, elle l'était.switch
cela, elle vous permet de créer une séquence d'expressions au lieu de clés pour les cas.Jetez un œil à la
cases
fonction dumemisc
package. Il implémente la fonctionnalité de cas avec deux manières différentes de l'utiliser. À partir des exemples du package:z1=cases( "Condition 1"=x<0, "Condition 2"=y<0,# only applies if x >= 0 "Condition 3"=TRUE )
où
x
ety
sont deux vecteurs.Références: package memisc , exemple de cas
la source
Si c'est le cas, vous
factor
pouvez changer les niveaux par la 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 pouvez écrire une fonction simple comme 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"))
la source
x
dans la dernière lignechangelevels
?Voici un moyen d'utiliser l'
switch
instruction: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 élément. Il est syntaxiquement plus pratique de pouvoir définir nos catégories comme ci-dessous (voir la question très similaire Comment ajouter une colonne dans un bloc de données dans 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 ajouter la
type
colonne dans la trame 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
la source
Je ne vois aucune proposition de «changement». Exemple de code (exécutez-le):
x <- "three" y <- 0 switch(x, one = {y <- 5}, two = {y <- 12}, three = {y <- 432}) y
la source
Imho, le code le plus simple et le plus 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' })
la source
y = 'else'
. Les éléments qui ne satisfont à aucune autre condition resteront inchangés.Il y a une
switch
déclaration, mais je n'arrive jamais à la faire fonctionner comme je pense qu'elle devrait. Puisque vous n'avez pas fourni d'exemple, je vais en créer un en utilisant une variable de facteur: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 souhaitées dans un ordre approprié à la réaffectation, vous pouvez utiliser le facteur ou les variables 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 y avait vraiment deux fonctions de commutation différentes. Ce n'est pas une fonction générique mais vous devriez y penser comme étant soit
switch.numeric
ouswitch.character
. Si votre premier argument est un «facteur» R, vous obtenez unswitch.numeric
comportement, qui est susceptible de causer des problèmes, car la plupart des gens voient les facteurs affichés comme des caractères et font l'hypothèse erronée que toutes les fonctions les traiteront comme tels.la source
Vous pouvez utiliser recode à partir du package voiture:
library(ggplot2) #get data library(car) daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10]
la source
sos::findFn("recode")
découvertesdoBy::recodeVar
,epicalc::recode
,memisc::recode
mais je n'ai pas regardé en détail les ...Je n'aime aucun de ceux-ci, ils ne sont pas clairs pour le lecteur ou l'utilisateur potentiel. J'utilise juste une fonction anonyme, la syntaxe n'est pas aussi lisse qu'une instruction de cas, mais l'évaluation est similaire à une instruction de cas et pas si pénible. cela suppose également que vous l'évaluez à l'intérieur de 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 ces () sont nécessaires pour enfermer et évaluer la fonction anonyme.
la source
result <- (if (x==10 | y< 5) 'foo' else if (x==11 & y== 5) 'bar' )
. 2) Cela ne fonctionne que six
ety
sont des scalaires; pour les vecteurs, comme dans la question initiale, desifelse
déclarations imbriquées seraient nécessaires.J'utilise dans les cas dont vous parlez
switch()
. Cela ressemble à une instruction de contrôle, mais en fait, c'est une fonction. L'expression est évaluée et en fonction de cette valeur, l'élément correspondant dans la liste est renvoyé.Ce qui suit est un exemple de chaîne simple qui résout votre problème pour réduire les anciennes catégories en nouvelles.
newCat <- switch(EXPR = category, cat1 = catX, cat2 = catX, cat3 = catY, cat4 = catY, cat5 = catZ, cat6 = catZ, "not available")
la source
Si vous voulez avoir une syntaxe de type SQL, vous pouvez simplement utiliser
sqldf
package. La fonction à utiliser est également des nomssqldf
et la syntaxe est la suivantesqldf(<your query in quotation marks>)
la source
Une déclaration de cas n'est peut-être pas la bonne approche ici. S'il s'agit d'un facteur, ce qui est probablement le cas, définissez simplement 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] A B C D E A B C D E Levels: A B C D E
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 D E A BC BC D E Levels: A BC D E
Le résultat est comme souhaité.
la source
Mixage
plyr::mutate
etdplyr::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]
Des points bonus si la colonne peut sortir de la mutation en tant que facteur au lieu de caractère! La dernière ligne de l'instruction case_when, qui capture toutes les lignes sans correspondance, 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
la source
Vous pouvez utiliser la
base
fonctionmerge
pour les tâches de remappage de type 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('mammal','mammal','bird','bird') ) merge(df,mapping) # name category # 1 cow mammal # 2 cow mammal # 3 eagle bird # 4 eagle bird # 5 pig mammal # 6 pigeon bird
la source
Depuis data.table v1.13.0, vous pouvez utiliser la fonction
fcase()
(fast-case) pour effectuer desCASE
opérations de type SQL (également similaires àdplyr::case_when()
):require(data.table) dt <- data.table(name = c('cow','pig','eagle','pigeon','cow','eagle')) dt[ , category := fcase(name %in% c('cow', 'pig'), 'mammal', name %in% c('eagle', 'pigeon'), 'bird') ]
la source