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!
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
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"
)
)
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"))
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
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'
})
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
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.
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]
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.
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")
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
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>)
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.