R 中的等效案例语句

问题描述 投票:0回答:17

我在数据框中有一个变量,其中一个字段通常有 7-8 个值。我想将它们折叠到数据框中的新变量中的 3 或 4 个新类别。最好的方法是什么?

如果我使用类似 SQL 的工具,但不知道如何在 R 中攻击它,我会使用 CASE 语句。

我们将非常感谢您提供的任何帮助!

r case
17个回答
55
投票

case_when()
于2016年5月添加到dplyr中,以类似于
memisc::cases()
的方式解决了这个问题。

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

原答案

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

31
投票

这是使用

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
等)。在语法上,能够如下定义我们的类别更方便(请参阅非常相似的问题如何在 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

31
投票

查看

cases
包中的
memisc
函数。它通过两种不同的使用方式来实现案例功能。 从包中的示例来看:

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

其中

x
y
是两个向量。

参考资料:memisc 包案例示例


30
投票

我没有看到任何“切换”的建议。代码示例(运行它):

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

26
投票

如果您得到

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

17
投票

恕我直言,最简单和通用的代码:

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

10
投票

有一个

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.numeric
switch.character
。如果您的第一个参数是 R“因子”,您会得到
switch.numeric
行为,这可能会导致问题,因为大多数人看到因子显示为字符,并错误地假设所有函数都会这样处理它们。


8
投票

我在你提到的那些情况下使用

switch()
。它看起来像一个控制语句,但实际上它是一个函数。计算表达式并根据该值返回列表中的相应项目。

switch 以两种不同的方式工作,具体取决于第一个参数的计算结果是字符串还是数字。

下面是一个简单的字符串示例,它解决了将旧类别折叠为新类别的问题。

对于字符串形式,在命名值后面有一个未命名参数作为默认值。

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

6
投票

您可以使用汽车包中的重新编码:

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

5
投票

我不喜欢其中任何一个,读者或潜在用户不清楚它们。我只是使用匿名函数,语法不像 case 语句那么流畅,但是评估类似于 case 语句,并且没有那么痛苦。这也假设您在定义变量的地方对其进行评估。

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

所有这些 () 都是封装和评估匿名函数所必需的。


4
投票

从 data.table v1.13.0 开始,您可以使用函数

fcase()
(快速情况)执行类似 SQL 的
CASE
操作(也类似于
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') ]

3
投票

如果你想要类似 sql 的语法,你可以使用

sqldf
包。要使用的函数也是名称
sqldf
,语法如下

sqldf(<your query in quotation marks>)

2
投票

案例陈述实际上可能不是正确的方法。如果这是一个因素(很可能是),只需适当设置该因素的水平即可。

假设您有一个由字母 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

结果如你所愿。


2
投票

混合

plyr::mutate
dplyr::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]

如果列可以作为因子而不是字符从 mutate 中出来,那就加分了! 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

2
投票

您可以使用

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('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

2
投票
com = '102'
switch (com,
    '110' = (com= '23279'),
    '101' = (com='23276'),
    '102'= (com = '23277'),
    '111' = (com = '23281'),
    '112' = (com = '23283')
)

print(com)

0
投票

使用(小)参考表来映射分类通常更容易,并且使(非 R)人员更容易遵循。您甚至可以设置一个表来映射串联变量。显然,您也可以设置多个列。

© www.soinside.com 2019 - 2024. All rights reserved.