R中等效的案例语句
Posted
技术标签:
【中文标题】R中等效的案例语句【英文标题】:Case Statement Equivalent in R 【发布时间】:2011-06-05 01:21:54 【问题描述】:我在数据框中有一个变量,其中一个字段通常有 7-8 个值。我想在数据框中的一个新变量中折叠它们 3 或 4 个新类别。最好的方法是什么?
如果我在一个类似 SQL 的工具中但不确定如何在 R 中攻击它,我会使用 CASE 语句。
您能提供的任何帮助将不胜感激!
【问题讨论】:
a) 它们是整数、数字、分类还是字符串?请发布示例数据 sn-p,使用dput()
b) 您想要在 base R、dplyr、data.table、tidyverse...中找到解决方案吗?
【参考方案1】:
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"
)
)
从 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"
)
)
【讨论】:
你不需要在每列前面加上.$
。
是的,从 dplyr 0.7.0(2017 年 6 月 9 日发布)开始,不再需要 .$
。在最初写这个答案的时候,它是。
很好的解决方案。如果两个陈述都是真的。第二个会覆盖第一个吗?
@JdP 它的工作方式与 SQL 中的 CASE WHEN 类似,因此语句是按顺序计算的,结果是第一个 TRUE 语句。 (所以在上面的例子中,我在末尾添加了一个 TRUE,作为默认值。)
我喜欢这个答案,因为与 switch
不同,它允许您创建表达式序列而不是案例的键。【参考方案2】:
看看memisc
包中的cases
函数。它以两种不同的方式实现案例功能。
来自包中的示例:
z1=cases(
"Condition 1"=x<0,
"Condition 2"=y<0,# only applies if x >= 0
"Condition 3"=TRUE
)
其中x
和y
是两个向量。
参考:memisc package、cases example
【讨论】:
【参考方案3】:这是使用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
【讨论】:
【参考方案4】:如果您有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"))
【讨论】:
不错的答案。我忘了你可以用一个列表作为新旧名称的参数;我的解决方案取决于保持水平顺序,所以这样会更好。 另外,最后一行的x
应该是changelevels
吗?【参考方案5】:
我没有看到关于“切换”的建议。代码示例(运行):
x <- "three"
y <- 0
switch(x,
one = y <- 5,
two = y <- 12,
three = y <- 432)
y
【讨论】:
【参考方案6】:恕我直言,最直接和通用的代码:
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'
)
【讨论】:
我喜欢这种方法。但是,是否有“其他”实现,因为在某些情况下这是必不可少的 @T.Fung 你可以把第一行改成y = 'else'
。不满足任何进一步条件的元素将保持不变。【参考方案7】:
有一个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")
【讨论】:
【参考方案9】:您可以使用汽车包中的重新编码:
library(ggplot2) #get data
library(car)
daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10]
【讨论】:
我就是不支持从文本中解析其参数的函数 是的,但你知道是否有人写过更好的版本吗?sos::findFn("recode")
找到了doBy::recodeVar
、epicalc::recode
、memisc::recode
,但我没有详细查看它们...【参考方案10】:
我不喜欢这些,读者或潜在用户都不清楚。我只是使用了一个匿名函数,语法不像 case 语句那么流畅,但求值类似于 case 语句,没有那么痛苦。这也假设您在定义变量的地方对其进行评估。
result <- ( function() if (x==10 | y< 5) return('foo')
if (x==11 & y== 5) return('bar')
)()
所有这些 () 都是封装和评估匿名函数所必需的。
【讨论】:
1) 函数部分是不必要的;你可以做result <- (if (x==10 | y< 5) 'foo' else if (x==11 & y== 5) 'bar' )
。 2) 这仅适用于 x
和 y
是标量;对于向量,就像在原始问题中一样,嵌套的 ifelse
语句是必要的。【参考方案11】:
如果你想拥有类似 sql 的语法,你可以使用 sqldf
包。要使用的函数也是名称sqldf
,语法如下
sqldf(<your query in quotation marks>)
【讨论】:
【参考方案12】:从 data.table v1.13.0 开始,您可以使用函数 fcase()
(fast-case) 执行类似 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') ]
【讨论】:
【参考方案13】:case 语句实际上可能不是正确的方法。如果这是一个因素(很可能是),只需适当设置因素的水平即可。
假设你有一个由字母 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
结果如你所愿。
【讨论】:
【参考方案14】:混合使用 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 中取出而不是 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
【讨论】:
【参考方案15】:您可以使用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
【讨论】:
以上是关于R中等效的案例语句的主要内容,如果未能解决你的问题,请参考以下文章
ArcGIS风暴ArcGIS中等高线高程标注/注记(打断/消隐)方法案例汇总
ArcGIS风暴ArcGIS中等高线高程标注/注记(打断/消隐)方法案例汇总
选择案例的 Python 等效项 (CDec(variable)