wake-up-neo.com

Fallangabe äquivalent in R

Ich habe eine Variable in einem Datenrahmen, in dem eines der Felder normalerweise 7-8 Werte hat. Ich möchte ihnen 3 oder 4 neue Kategorien in einer neuen Variablen innerhalb des Datenrahmens zusammenfassen. Was ist der beste Ansatz?

Ich würde eine CASE-Anweisung verwenden, wenn ich in einem SQL-ähnlichen Tool wäre, aber nicht sicher bin, wie ich dies in R angreifen soll.

Jede mögliche Hilfe, die Sie zur Verfügung stellen können, wird sehr geschätzt!

69
Btibert3

Schauen Sie sich die Funktion cases aus dem Paket memisc an. Es implementiert die case-Funktionalität auf zwei verschiedene Arten. Aus den Beispielen im Paket:

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

dabei sind x und y zwei Vektoren.

Referenzen: memisc package , cases example

26
Henrico

case_when(), das im Mai 2016 zu dplyr hinzugefügt wurde, löst dieses Problem auf ähnliche Weise wie memisc::cases().

Beispielsweise:

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

Ab 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"
  )
)
25
Evan Cortens

Wenn Sie factor haben, können Sie die Ebenen nach der Standardmethode ändern:

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

Sie könnten eine einfache Funktion als Wrapper schreiben:

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

Hier ist eine Möglichkeit, die Anweisung switch zu verwenden:

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

Der einzige Nachteil dabei ist, dass Sie den Kategorienamen (animal, etc) für jeden Artikel immer wieder schreiben müssen. Es ist syntaktisch bequemer, unsere Kategorien wie folgt zu definieren (siehe die sehr ähnliche Frage Wie füge ich eine Spalte in einem Datenrahmen in R hinz )

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

und wir wollen dieses Mapping irgendwie "invertieren". Ich schreibe meine eigene invMap-Funktion:

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

und invertieren Sie dann die obige Karte wie folgt:

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

Und dann ist es ganz einfach, die type -Spalte in den Datenrahmen einzufügen:

df <- transform(df, type = invMap(myMap)[name])

> df
    name   type
1    cow animal
2    pig animal
3  eagle   bird
4 pigeon   bird
17

Imho, der einfachste und universellste 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'
})
14
Gregory Demin

Ich sehe keinen Vorschlag für "wechseln". Codebeispiel (führe es aus):

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

Es gibt eine switch -Anweisung, aber ich kann niemals scheinen, sie so zum Laufen zu bringen, wie ich es für richtig halte. Da Sie kein Beispiel angegeben haben, werde ich eines mit einer Faktorvariablen erstellen:

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

Wenn Sie die gewünschten Kategorien in einer der Neuzuweisung entsprechenden Reihenfolge angeben, können Sie den Faktor oder die numerischen Variablen als Index verwenden:

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

Ich habe später erfahren, dass es wirklich zwei verschiedene Schalterfunktionen gibt. Es ist keine generische Funktion, aber Sie sollten es als entweder switch.numeric oder switch.character. Wenn Ihr erstes Argument ein R-Faktor ist, erhalten Sie switch.numeric Verhalten, das wahrscheinlich Probleme verursacht, da die meisten Menschen Faktoren als Zeichen anzeigen und die falsche Annahme treffen, dass alle Funktionen sie als solche verarbeiten.

7
42-

Sie können recode aus dem Autopaket verwenden:

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

ich mag keine davon, sie sind nicht klar für den Leser oder den potenziellen Benutzer. Ich benutze nur eine anonyme Funktion, die Syntax ist nicht so geschickt wie eine case-Anweisung, aber die Auswertung ähnelt einer case-Anweisung und ist nicht so schmerzhaft. Dies setzt auch voraus, dass Sie es dort auswerten, wo Ihre Variablen definiert sind.

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

all diese () sind notwendig, um die anonyme Funktion einzuschließen und auszuwerten.

5
jamesM

Mischen plyr::mutate und dplyr::case_when funktioniert bei mir und ist lesbar.

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]

Bonuspunkte, wenn die Spalte als Faktor anstelle von char mutieren kann! Die letzte Zeile der case_when-Anweisung, die alle nicht übereinstimmenden Zeilen abfängt, ist sehr wichtig.

     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

Ich verwende in den Fällen, in denen Sie switch() verweisen. Es sieht aus wie eine Steueranweisung, ist aber eigentlich eine Funktion. Der Ausdruck wird ausgewertet und basierend auf diesem Wert wird das entsprechende Element in der Liste zurückgegeben.

switch funktioniert auf zwei verschiedene Arten, je nachdem, ob das erste Argument eine Zeichenfolge oder eine Zahl ergibt.

Im Folgenden finden Sie ein einfaches Zeichenfolgenbeispiel, mit dem Sie das Problem lösen können, alte Kategorien in neue zu reduzieren.

Verwenden Sie für die Zeichenfolgenform ein einzelnes unbenanntes Argument als Standard nach den benannten Werten.

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

Sie können die base -Funktion merge für die Zuordnung von Groß- und Kleinschreibung verwenden:

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
2
patrickmdnet

Wenn Sie eine SQL-ähnliche Syntax wünschen, können Sie einfach das sqldf -Paket verwenden. Die zu verwendende Funktion ist auch names sqldf und die Syntax lautet wie folgt

sqldf(<your query in quotation marks>)
2
kuba

Eine case-Anweisung ist hier möglicherweise nicht der richtige Ansatz. Wenn dies ein Faktor ist, der wahrscheinlich ist, stellen Sie einfach die Pegel des Faktors entsprechend ein.

Angenommen, Sie haben einen Faktor mit den Buchstaben A bis E.

> 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

Um die Ebenen B und C zu verbinden und als BC zu bezeichnen, ändern Sie einfach die Namen dieser Ebenen 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

Das Ergebnis ist wie gewünscht.

2
Aaron