Equivalent van casus in R

Ik heb een variabele in een dataframe waar een van de velden meestal 7-8 waarden heeft. Ik wil ze 3 of 4 nieuwe categorieën samenvoegen binnen een nieuwe variabele binnen het dataframe. Wat is de beste aanpak?

Ik zou een CASE-instructie gebruiken als ik in een SQL-achtige tool zat, maar niet zeker wist hoe ik dit in R moest aanvallen.

Alle hulp die u kunt bieden, wordt zeer op prijs gesteld!


Antwoord 1, autoriteit 100%

case_when(), dat in mei 2016 aan dplyr werd toegevoegd, lost dit probleem op een vergelijkbare manier op als memisc::cases().

Bijvoorbeeld:

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

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

Antwoord 2, autoriteit 64%

Bekijk de functie casesuit het pakket memisc. Het implementeert case-functionaliteit met twee verschillende manieren om het te gebruiken.
Uit de voorbeelden in het pakket:

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

waar xen ytwee vectoren zijn.

Referenties: memisc pakket , Gevallen Voorbeeld


Antwoord 3, Autoriteit 55%

Hier is een manier met behulp van de switchverklaring:

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

De enige nadeel hiervan is dat u de naam van de categorie (animal, ETC) voor elk artikel moet blijven schrijven. Het is syntactisch handiger om onze categorieën zoals hieronder te kunnen definiëren (zie de vergelijkbare vraag Hoe voegt een kolom toe in een gegevensframe in R )

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

En we willen deze mapping op de een of andere manier “invert”. Ik schrijf mijn eigen INVMAP-functie:

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

en bekeer de bovenstaande kaart vervolgens als volgt:

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

En dan is het gemakkelijk om dit te gebruiken om de kolom typetoe te voegen aan het dataframe:

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

Antwoord 4, autoriteit 55%

Als u factorheeft, kunt u de niveaus wijzigen met de standaardmethode:

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

Je zou een eenvoudige functie als een wrapper kunnen schrijven:

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

Antwoord 5, autoriteit 55%

Ik zie geen voorstel voor ‘overstappen’. Codevoorbeeld (voer het uit):

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

Antwoord 6, autoriteit 36%

Imho, meest eenvoudige en universele code:

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

Antwoord 7, autoriteit 23%

Er is een switch-statement, maar ik krijg het nooit voor elkaar om het te laten werken zoals ik denk dat het zou moeten. Aangezien je geen voorbeeld hebt gegeven, zal ik er een maken met behulp van een factorvariabele:

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

Als u de gewenste categorieën opgeeft in een volgorde die past bij de hertoewijzing, kunt u de factor- of numerieke variabelen als index gebruiken:

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" ...

Later leerde ik dat er echt twee verschillende schakelfuncties zijn. Het is geen algemene functie, maar je zou het moeten zien als switch.numericof switch.character. Als je eerste argument een R-factor is, krijg je switch.numeric-gedrag, wat waarschijnlijk problemen zal veroorzaken, aangezien de meeste mensen factoren als karakter zien en de onjuiste veronderstelling maken dat alle functies zullen worden verwerkt ze als zodanig.


Antwoord 8, autoriteit 14%

U kunt Recode uit het carpakket gebruiken:

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

Antwoord 9, Autoriteit 11%

Ik hou niet van deze, ze zijn niet duidelijk voor de lezer of de potentiële gebruiker. Ik gebruik gewoon een anonieme functie, de syntaxis is niet zo glad als een case-verklaring, maar de evaluatie is vergelijkbaar met een case-statement en niet die pijnlijk. Dit veronderstelt ook dat uw evaluatie het wordt geëvalueerd binnen waar uw variabelen worden gedefinieerd.

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

Allen () zijn nodig om de anonieme functie te omsluiten en te evalueren.


Antwoord 10, Autoriteit 11%

Ik gebruik in die gevallen die u verwijst switch(). Het ziet eruit als een controleverklaring, maar eigenlijk is het een functie. De uitdrukking wordt geëvalueerd en op basis van deze waarde, het bijbehorende item in de lijst wordt geretourneerd.

Schakelaar werkt in twee verschillende manieren, afhankelijk van of het eerste argument evalueert op een tekenreeks of een getal.

Wat volgt is een eenvoudig snarig voorbeeld dat uw probleem oplost om oude categorieën naar nieuwe te instorten.

Neem voor het tekenreeksformulier een enkel naamloos argument als standaard na de benoemde waarden.

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

Antwoord 11, autoriteit 7%

Als je een sql-achtige syntaxis wilt hebben, kun je gewoon het sqldf-pakket gebruiken. De functie die moet worden gebruikt, is ook de naam sqldfen de syntaxis is als volgt

sqldf(<your query in quotation marks>)

Antwoord 12, autoriteit 7%

Vanaf data.table v1.13.0u kunt de functie fcase()(fast-case) gebruiken om SQL-achtige CASE-bewerkingen uit te voeren (ook vergelijkbaar met 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') ]

Antwoord 13, autoriteit 5%

Een case-statement is hier misschien niet de juiste benadering. Als dit een factor is, wat waarschijnlijk het geval is, stelt u de niveaus van de factor op de juiste manier in.

Stel dat je een factor hebt met de letters A tot E, zoals deze.

> 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

Als je de niveaus B en C wilt toevoegen en het BC wilt noemen, verander je gewoon de namen van die niveaus in 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

Het resultaat is naar wens.


Antwoord 14, autoriteit 5%

Het mengen van plyr::mutateen dplyr::case_whenwerkt voor mij en is leesbaar.

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]

Bonuspunten Als de kolom uitmuteert als een factor in plaats van Char! De laatste regel van de Case_When-verklaring, die alle niet-overeenkomende rijen vangt, is erg belangrijk.

    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

Antwoord 15, Autoriteit 5%

U kunt de baseFUNCTION mergegebruiken voor Case-Style RECARMENT-taken:

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

Other episodes