2011-01-07 35 views
51

我在數據框中有一個變量,其中一個字段通常具有7-8個值。我想在數據框內的一個新變量中將它們分成3或4個新類別。什麼是最好的方法?R中的案例陳述等效

如果我是在一個類似SQL的工具,但不知道如何在R.

任何幫助,您可以提供將非常感激攻擊這個我會用一個CASE語句!

回答

4

您可以使用重新編碼從車上包:

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

我只是不支持從文本中解析參數的函數 – hadley 2011-01-07 16:23:11

+0

是的,但是你知道是否有人寫過更好的版本嗎? `sos :: findFn(「recode」)`發現`doBy :: recodeVar`,`epicalc :: recode`,`memisc :: recode`,但我沒有詳細地看過它們...... – 2011-09-12 16:35:33

4

有一個switch聲明,但我似乎從來沒有得到它的工作,我認爲它應該的方式。既然你沒有提供一個例子,我會用一個因子變量做出一個:

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

如果你指定要在訂單中適當就可以使用因子或數值變量作爲索引重新分配的類別:

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

我後來才知道實際上有兩種不同的開關功能。這不是通用功能,但您應該將其想象爲switch.numericswitch.character。如果你的第一個參數是一個R'因素',你會得到switch.numeric行爲,這很可能會導致問題,因爲大多數人會將因素顯示爲字符,並作出不正確的假設,即所有函數都會如此處理它們。

12

恕我直言,最直接,最普遍的代碼:

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

這很多'='標誌在那裏... – 2017-01-05 23:30:14

14

下面是一個使用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 

這樣做的一個缺點是,你必須保持寫作的類別名稱( animal等)爲每個項目。這是能夠如下定義我們的類語法更方便(見非常類似的問題How do add a column in a data frame in R

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

,我們希望以某種方式「反轉」這個映射。我寫我自己的invMap功能:

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

,然後反轉上述圖如下:

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

然後可以很容易地使用這個數據幀添加type列:

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

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

查看memisc軟件包中的cases函數。它通過兩種不同的方式來實現案例功能。 從封裝中的例子:

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

其中xy是兩個矢量。

4

我不喜歡這些,他們不清楚讀者或潛在的用戶。我只是使用一個匿名函數,語法不像案例語句那麼漂亮,但是評估類似於一個case語句,而不是那麼痛苦。這也假定你在你的變量定義的地方進行評估。

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

所有這些()都必須包含和評估匿名函數。

+5

1)功能部分是不必要的;你可以做`result < - (if(x == 10 | y <5)'foo'else if(x == 11&y == 5)'bar')``。 2)這隻適用於`x`和`y`是標量;對於向量,與原始問題一樣,嵌套的`ifelse`語句是必要的。 – Aaron 2011-09-10 19:58:22

1

一個案例陳述實際上可能不是正確的方法。如果這是一個可能的因素,那麼只需適當設置因子的水平。

假設你有一個字母A到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 

要加入B級和C級並命名爲BC,只需將這些級別的名稱更改爲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 

結果如期望的那樣。

17

如果你有factor,那麼你可以用標準的方法更改級別:

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 

你可以寫簡單的函數作爲包裝:

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

很好的答案。我忘了你可以使用一個列表作爲參數來與新舊名稱進行比較;我的解決方案取決於保持關卡順序的順序,所以這樣做更好。 – Aaron 2011-09-12 17:10:26

1

如果你想擁有類似於SQL的你的語法只能使用sqldf包。要使用的函數也是名稱sqldf,語法如下

sqldf(<your query in quotation marks>) 
9

我沒有看到'switch'的提議。代碼示例(運行):

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

case_when(),向其中加入2016年5月到dplyr,以類似於memisc::cases()的方式解決了這一問題。

例如:

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

可以使用base功能merge的情況下,風格重新映射任務:

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 
1

混合plyr::mutatedplyr::case_when作品對我來說,是可讀的。

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] 

如果列可以作爲因子而不是char變異出來,那麼可以得到獎勵點! case_when語句的最後一行捕獲所有不匹配的行是非常重要的。

 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