清理因子级别(折叠多个级别/标签)
Posted
技术标签:
【中文标题】清理因子级别(折叠多个级别/标签)【英文标题】:Cleaning up factor levels (collapsing multiple levels/labels) 【发布时间】:2013-10-24 23:05:59 【问题描述】:清理包含需要折叠的多个级别的因素的最有效(即有效/适当)方法是什么?即如何将两个或多个因子水平合二为一。
这是一个示例,其中“是”和“Y”两个级别应折叠为“是”,“否”和“N”应折叠为“否”:
## Given:
x <- c("Y", "Y", "Yes", "N", "No", "H") # The 'H' should be treated as NA
## expectedOutput
[1] Yes Yes Yes No No <NA>
Levels: Yes No # <~~ NOTICE ONLY **TWO** LEVELS
一种选择当然是使用sub
和朋友事先清理字符串。
另一种方法,是允许重复标签,然后删除它们
## Duplicate levels ==> "Warning: deprecated"
x.f <- factor(x, levels=c("Y", "Yes", "No", "N"), labels=c("Yes", "Yes", "No", "No"))
## the above line can be wrapped in either of the next two lines
factor(x.f)
droplevels(x.f)
但是,有没有更有效的方法?
虽然我知道 levels
和 labels
参数应该是向量,但我尝试了列表、命名列表和命名向量,看看会发生什么
不用说,以下这些都没有让我更接近我的目标。
factor(x, levels=list(c("Yes", "Y"), c("No", "N")), labels=c("Yes", "No"))
factor(x, levels=c("Yes", "No"), labels=list(c("Yes", "Y"), c("No", "N")))
factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Yes="Y", Yes="Yes", No="No", No="N"))
factor(x, levels=c("Yes", "No"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
【问题讨论】:
尚未对此进行测试,但 R 3.5.0 (2018-04-23) 发行说明说“因子(x,级别,标签)现在允许重复标签(而不是重复级别!) . 因此,您可以将 x 的不同值直接映射到同一级别。” 【参考方案1】:也许使用命名向量作为键:
> factor(unname(c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA)[x]))
[1] Yes Yes Yes No No <NA>
Levels: No Yes
这看起来与您上次的尝试非常相似......但这个有效:-)
【讨论】:
谢谢阿难。这是个好主意。对于我的应用程序,我可能可以取消unname
...这可能会成功
多年后重访...这将删除未显示的级别,这可能是不可取的,例如,x="N"
只有“否”级别会显示在结果中。
@Frank,这不是通过在factor
步骤中添加明确的levels
来轻松解决的吗?
啊,很酷的东西 :) 是的,添加显式级别是可行的,尽管您必须再次输入列表,将列表保存在某处,或者做一些吹奏或像c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA) %>% factor(unname(.[x]), levels = unique(.))
eh 一样运行。
@frank 更酷的东西,其额外的好处是它可以按预期顺序排列级别:Yes
,No
。【参考方案2】:
我不知道您的真实用例,但 strtrim
在这里有用吗...
factor( strtrim( x , 1 ) , levels = c("Y" , "N" ) , labels = c("Yes" , "No" ) )
#[1] Yes Yes Yes No No <NA>
#Levels: Yes No
【讨论】:
【参考方案3】:更新 2:请参阅 Uwe 的回答,其中展示了新的“tidyverse”方式,该方式正在迅速成为标准。
更新 1:现在确实允许重复标签(但不是级别!)(根据我上面的评论);见蒂姆的回答。
原始答案,但仍然有用且感兴趣:
有一个鲜为人知的选项可以将命名列表传递给levels
函数,正是为了这个目的。列表的名称应该是所需的级别名称,元素应该是应该重命名的当前名称。有些人(包括 OP,请参阅 Ricardo 对 Tim 的回答的评论)更喜欢这个以方便阅读。
x <- c("Y", "Y", "Yes", "N", "No", "H", NA)
x <- factor(x)
levels(x) <- list("Yes"=c("Y", "Yes"), "No"=c("N", "No"))
x
## [1] Yes Yes Yes No No <NA> <NA>
## Levels: Yes No
如levels
文档中所述;也可以看看那里的例子。
值:对于“因子”方法,a 长度至少为数字的字符串向量 'x' 的级别,或指定如何重命名的命名列表 级别。
这也可以在一行中完成,就像 Marek 在这里所做的那样:https://***.com/a/10432263/210673; levels<-
魔法在这里解释https://***.com/a/10491881/210673。
> `levels<-`(factor(x), list(Yes=c("Y", "Yes"), No=c("N", "No")))
[1] Yes Yes Yes No No <NA>
Levels: Yes No
【讨论】:
+1 更健壮,我想比我的尝试更安全。 谢谢 Aaron,我喜欢这种方法,因为它至少避免了与droplevles(factor(x, ...))
相关的警告,但我仍然对任何更直接的方法感到好奇。例如:如果可以在factor(.)
调用中直接使用levels=<a named list>
)
同意在factor
内不能这样做很奇怪;我不知道更直接的方法,除了使用像阿南达的解决方案或者可能是匹配的东西。
这也适用于ordered
,折叠的层级按提供的顺序排序,例如a = ordered(c(1, 2, 3)); levels(a) = list("3" = 3, "1,2" = c(1, 2))
产生排序Levels: 3 < 1,2
。【参考方案4】:
类似于@Aaron 的方法,但稍微简单一点:
x <- c("Y", "Y", "Yes", "N", "No", "H")
x <- factor(x)
# levels(x)
# [1] "H" "N" "No" "Y" "Yes"
# NB: the offending levels are 1, 2, & 4
levels(x)[c(1,2,4)] <- c(NA, "No", "Yes")
x
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
【讨论】:
【参考方案5】:另一种方法是制作一个包含映射的表:
# stacking the list from Aaron's answer
fmap = stack(list(Yes = c("Y", "Yes"), No = c("N", "No")))
fmap$ind[ match(x, fmap$values) ]
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
# or...
library(data.table)
setDT(fmap)[x, on=.(values), ind ]
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
我更喜欢这种方式,因为它会留下一个易于检查的对象来总结地图;并且 data.table 代码看起来就像该语法中的任何其他联接。
当然,如果你不想要像fmap
这样的对象来总结变化,它可以是“单行”:
library(data.table)
setDT(stack(list(Yes = c("Y", "Yes"), No = c("N", "No"))))[x, on=.(values), ind ]
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
【讨论】:
另一个例子:franknarf1.github.io/r-tutorial/_book/tables.html#dt-recode【参考方案6】:由于问题的标题是清理因子级别(折叠多个级别/标签),为了完整起见,这里也应该提到 forcats
包。 forcats
于 2016 年 8 月出现在 CRAN。
有几个方便的函数可用于清理因子水平:
x <- c("Y", "Y", "Yes", "N", "No", "H")
library(forcats)
将因子级别折叠到手动定义的组中
fct_collapse(x, Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
#[1] Yes Yes Yes No No <NA>
#Levels: No Yes
手动更改因子水平
fct_recode(x, Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
#[1] Yes Yes Yes No No <NA>
#Levels: No Yes
自动重新标记因子水平,必要时折叠
fun <- function(z)
z[z == "Y"] <- "Yes"
z[z == "N"] <- "No"
z[!(z %in% c("Yes", "No"))] <- NA
z
fct_relabel(factor(x), fun)
#[1] Yes Yes Yes No No <NA>
#Levels: No Yes
请注意,fct_relabel()
适用于因子级别,因此它需要一个 因子 作为第一个参数。另外两个函数fct_collapse()
和fct_recode()
也接受一个字符向量,这是一个未记录的特性。
按首次出现重新排序因子水平
OP 给出的预期输出是
[1] Yes Yes Yes No No <NA>
Levels: Yes No
这里的级别按照它们出现在x
中的顺序排列,这与默认值不同(?factor
:一个因子的级别默认排序)。
为了符合预期的输出,这可以通过使用fct_inorder()
在折叠级别来实现:
fct_collapse(fct_inorder(x), Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
fct_recode(fct_inorder(x), Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
现在两者都以相同的顺序返回预期的输出。
【讨论】:
【参考方案7】:您可以使用以下函数来组合/折叠多个因素:
combofactor <- function(pattern_vector,
replacement_vector,
data)
levels <- levels(data)
for (i in 1:length(pattern_vector))
levels[which(pattern_vector[i] == levels)] <-
replacement_vector[i]
levels(data) <- levels
data
例子:
初始化 x
x <- factor(c(rep("Y",20),rep("N",20),rep("y",20),
rep("yes",20),rep("Yes",20),rep("No",20)))
检查结构
str(x)
# Factor w/ 6 levels "N","No","y","Y",..: 4 4 4 4 4 4 4 4 4 4 ...
使用函数:
x_new <- combofactor(c("Y","N","y","yes"),c("Yes","No","Yes","Yes"),x)
重新检查结构:
str(x_new)
# Factor w/ 2 levels "No","Yes": 2 2 2 2 2 2 2 2 2 2 ...
【讨论】:
【参考方案8】:首先让我们注意,在这种特定情况下,我们可以使用部分匹配:
x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c("Yes","No")
x <- factor(y[pmatch(x,y,duplicates.ok = TRUE)])
# [1] Yes Yes Yes No No <NA>
# Levels: No Yes
在更一般的情况下,我会选择dplyr::recode
:
library(dplyr)
x <- c("Y", "Y", "Yes", "N", "No", "H")
y <- c(Y="Yes",N="No")
x <- recode(x,!!!y)
x <- factor(x,y)
# [1] Yes Yes Yes No No <NA>
# Levels: Yes No
如果起点是一个因素,则略有改变:
x <- factor(c("Y", "Y", "Yes", "N", "No", "H"))
y <- c(Y="Yes",N="No")
x <- recode_factor(x,!!!y)
x <- factor(x,y)
# [1] Yes Yes Yes No No <NA>
# Levels: Yes No
【讨论】:
【参考方案9】:我添加这个答案是为了证明在数据框中的特定因素上工作的公认答案,因为这对我来说最初并不明显(尽管它可能应该是)。
levels(df$var1)
# "0" "1" "Z"
summary(df$var1)
# 0 1 Z
# 7012 2507 8
levels(df$var1) <- list("0"=c("Z", "0"), "1"=c("1"))
levels(df$var1)
# "0" "1"
summary(df$var1)
# 0 1
# 7020 2507
【讨论】:
【参考方案10】:自 R 3.5.0 (2018-04-23) 起,您可以通过一条清晰而简单的线路做到这一点:
x = c("Y", "Y", "Yes", "N", "No", "H") # The 'H' should be treated as NA
tmp = factor(x, levels= c("Y", "Yes", "N", "No"), labels= c("Yes", "Yes", "No", "No"))
tmp
# [1] Yes Yes Yes No No <NA>
# Levels: Yes No
1 行,将多个值映射到同一级别,为缺失级别设置 NA" – h/t @Aaron
【讨论】:
有用的更新,但命名列表对需要阅读代码的任何人都更友好以上是关于清理因子级别(折叠多个级别/标签)的主要内容,如果未能解决你的问题,请参考以下文章