在同一个数据框中对因子进行分组/重编码

7
假设我有一个像这样的数据框:
df <- data.frame(a=letters[1:26],1:26)

我想将a、b和c重构为"a",你能告诉我如何实现吗?请注意不要删除HTML标记。
5个回答

14

一种选择是使用包car中的recode()函数:

require(car)
df <- data.frame(a=letters[1:26],1:26)
df2 <- within(df, a <- recode(a, 'c("a","b","c")="a"'))
> head(df2)
  a X1.26
1 a     1
2 a     2
3 a     3
4 d     4
5 e     5
6 f     6

举个例子,其中a并不简单,我们需要将几个级别重新编码成一个。

set.seed(123)
df3 <- data.frame(a = sample(letters[1:5], 100, replace = TRUE),
                  b = 1:100)
with(df3, head(a))
with(df3, table(a))

最后几行显示:

> with(df3, head(a))
[1] b d c e e a
Levels: a b c d e
> with(df3, table(a))
a
 a  b  c  d  e 
19 20 21 22 18

现在我们将使用 recode() 将等级 ae 合并成等级 Z

df4 <- within(df3, a <- recode(a, 'c("a","e")="Z"'))
with(df4, head(a))
with(df4, table(a))

它给出了:

> with(df4, head(a))
[1] b d c Z Z Z
Levels: b c d Z
> with(df4, table(a))
a
 b  c  d  Z 
20 21 22 37

不用明确指定要合并的级别就可以做到这一点:

## Select the levels you want (here 'a' and 'e')
lev.want <- with(df3, levels(a)[c(1,5)])
## now paste together
lev.want <- paste(lev.want, collapse = "','")
## then bolt on the extra bit
codes <- paste("c('", lev.want, "')='Z'", sep = "")
## then use within recode()
df5 <- within(df3, a <- recode(a, codes))
with(df5, table(a))

这使得我们获得与上面的df4相同的结果:

> with(df5, table(a))
a
 b  c  d  Z 
20 21 22 37 

需要将其设置为有序因子才能使用吗? - Brandon Bertelsen
@Brandon:不是的,在上面的例子中,它并没有按照“with(df, is.ordered(a))”这个视角排序。我会添加另一个例子,展示一个比你原来的a更加复杂的情况。 - Gavin Simpson
@Brandon:好的,太棒了。因此,我在我的答案中添加的额外示例展示了recode()在无序因子中的工作方式。 - Gavin Simpson
有没有一种方法可以在不输入标签的情况下引用级别? - Brandon Bertelsen
抱歉,我有点迟钝。我已经编辑了我的第二个示例,展示了我认为你想要的内容。 - Gavin Simpson
显示剩余2条评论

13

有人尝试过使用这个简单的方法吗?它不需要特殊的软件包,只需要理解R如何处理因子。

假设您想重命名因子中的级别,请获取它们的索引。

data <- data.frame(a=letters[1:26],1:26)
lalpha <- levels(data$a)

在这个例子中,我们想知道水平为'e'和'w'的索引。

lalpha <- levels(data$a)
ind <- c(which(lalpha == 'e'), which(lalpha == 'w'))
现在我们可以使用这个索引来替换因子'a'的级别。
levels(data$a)[ind] <- 'X'

如果你现在查看数据框因子a,你会发现原先ew的位置上有一个X。

我让你自己尝试结果。


3
你可以像这样做:

你可以这样做:

df$a[df$a %in% c("a","b","c")] <- "a"

更新:更加复杂的因素。
Data <- data.frame(a=sample(c("Less than $50,000","$50,000-$99,999",
  "$100,000-$249,999", "$250,000-$500,000"),20,TRUE),n=1:20)
rows <- Data$a %in% c("$50,000-$99,999", "$100,000-$249,999")
Data$a[rows] <- "$250,000-$500,000"

这个对我提供的例子有效,但是一旦我尝试使用更复杂的因子名称,它就会出错。 - Brandon Bertelsen
@Brandon,你能提供一些“更复杂的因子名称”的例子吗? - Joshua Ulrich
"少于$50,000","$50,000-$99,999", "$100,000-$249,999", "$250,000-$500,000" - Brandon Bertelsen
而为了清理,你还应该执行 levels(df$a) <- levels(df$a)[!levels(df$a)%in%c("b","c")] - Marek

2
有两种方法。 如果您不想删除未使用的级别,即“b”和“c”,Joshua的解决方案可能是最好的。
如果您想要删除未使用的级别,则:
df$a<-factor(ifelse(df$a%in%c("a","b","c"),"a",as.character(df$a)))

或者

levels(df$a)<-ifelse(levels(df$a)%in%c("a","b","c"),"a",levels(df$a))

0
这是所选答案的简化版本:
我发现最简单的方法是通过查看因子水平并将数字写下来进行覆盖,以处理此问题。
df <- data.frame(a=letters[1:26],1:26)
levels(df)

> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" 
 "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"

levels(df$a)[c(1,2)] <- "c"
summary(df$a)

> c d e f g h i j k l m n o p q r s t u v w x y z 
  3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接