我在一个数据帧中有一个变量,其中一个字段通常具有7-8个值。我想在数据框内的新变量中合并3个或4个新类别。最好的方法是什么?
如果我使用的是类似SQL的工具,但不知道如何在R中进行攻击,我将使用CASE语句。
您能提供的任何帮助将不胜感激!
Answers:
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"
)
)
.$
每列的前面。
.$
不再需要。这个答案最初是写的。
switch
,它允许您创建一个表达式序列而不是案例的键。
如果可以,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
吗?
这是使用该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
恕我直言,最简单,通用的代码:
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'
})
y = 'else'
。不满足任何其他条件的元素将保持不变。
有一个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
行为,这很可能会引起问题,因为大多数人都将因子显示为字符,并且错误地假设所有函数将按原样处理它们。
您可以从汽车包装中使用重新编码:
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
,但我还没有详细的看着他们...
我不喜欢其中任何一个,但对于读者或潜在用户并不清楚。我只是使用一个匿名函数,其语法不如case语句那么狡猾,但是评估类似于case语句,并且不会那么痛苦。这也假定您在定义变量的地方对其进行评估。
result <- ( function() { if (x==10 | y< 5) return('foo')
if (x==11 & y== 5) return('bar')
})()
所有这些()都是封装和评估匿名函数所必需的。
result <- (if (x==10 | y< 5) 'foo' else if (x==11 & y== 5) 'bar' )
。2)仅当x
和y
是标量时才有效;对于向量,就像原始问题一样,嵌套ifelse
语句将是必需的。
在您指的是这些情况下,我正在使用switch()
。它看起来像一个控制语句,但实际上,它是一个函数。对表达式进行求值,并基于该值返回列表中的相应项目。
开关以两种不同的方式工作,具体取决于第一个参数是字符串还是数字。
以下是一个简单的字符串示例,该示例解决了将旧类别折叠为新类别的问题。
对于字符串形式,在命名值之后有一个未命名的参数作为默认值。
newCat <- switch(EXPR = category,
cat1 = catX,
cat2 = catX,
cat3 = catY,
cat4 = catY,
cat5 = catZ,
cat6 = catZ,
"not available")
混合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]
如果该列可以从mut而不是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
您可以将该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
从data.table v1.13.0开始,您可以使用函数fcase()
(快速使用)执行CASE
类似SQL的操作(也类似于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') ]
dput()
b)发布示例数据片段,是否要在R,dplyr,data.table,tidyverse ...中找到解决方案?