如何从因子级别创建新的数据框列(并解决变异错误)

How to make new dataframe columns from factor levels (& troubleshoot mutate error)

我在 SO 和其他地方的搜索提出了有趣的解决方案,以解决具有相似搜索词但不是我的问题的问题。以为我找到了解决办法,但这个错误让我很困惑。我正在尝试更好地学习 tidyverse 方法,但我很欣赏任何解决方案策略。

目标: 在数据框中创建新的向量列,其中每个新向量都是根据现有数据框向量的因子级别命名的。 代码解决方案应该是动态的,以便它可以应用于具有任意数量级别的因素。

测试数据

df <- data.frame(x=c(1:5), y=letters[1:5])

按预期生成

> str(df)
'data.frame':   5 obs. of  2 variables:
 $ x: int  1 2 3 4 5
 $ y: Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5
> df
  x y
1 1 a
2 2 b
3 3 c
4 4 d
5 5 e

完成后应该看起来像

> df
  x y  a  b  c  d  e
1 1 a NA NA NA NA NA
2 2 b NA NA NA NA NA
3 3 c NA NA NA NA NA
4 4 d NA NA NA NA NA
5 5 e NA NA NA NA NA

整洁的循环方法

library(tidyverse)

for (i in 1:length(levels(df$y))) {
  df <- mutate(df, levels(df$y)[i] = NA)
}

但这给了我以下错误:

> for (i in 1:length(levels(df$y))) {
+   df <- mutate(df, levels(df$y)[i] = NA)
Error: unexpected '=' in:
"for (i in 1:length(levels(df$y))) {
  df <- mutate(df, levels(df$y)[i] ="
> }
Error: unexpected '}' in "}"

故障排除,我删除了循环并简化了 mutate 以查看它是否总体上有效,它会带或不带引号 (注意,我重新运行测试数据以重新开始).

levels(df$y)[1]
> "a"

df <- mutate(df, a = NA)
df <- mutate(df, "a" = NA) # works the same as the previous line
> df
  x y  a
1 1 a NA
2 2 b NA
3 3 c NA
4 4 d NA
5 5 e NA

将级别函数替换回来,但没有循环 returns 变异错误 (注意,我重新运行测试数据以重新开始):

> df <- mutate(df, levels(df$y)[1] = NA)
Error: unexpected '=' in "df <- mutate(df, levels(df$y)[1] ="

我继续遇到同样的错误,因为我尝试使用 .data=df 指定数据集或将 as.character()、paste() 或 paste0() 包裹在水平函数周围——我在网上找到了其他各种解决方案。如果我使用 %>% 管道重构代码,R 也不只是挑剔。

对于我的关卡代码替换(以及潜在的新手错误),等号出乎意料怎么办? 非常感谢任何帮助!

根据收到的评论为其他人发布解决方案,这样我就可以将这个问题标记为已解决。 请投票给@arg0naut91 和@Gregor 以获得他们的解决方案和指导帮助。

测试数据

df <- data.frame(x=c(1:5), y=letters[1:5])

方案一:基础R

@arg0naut91 提供了一个优雅的基础 R 解决方案:

df[, levels(df$y)] <- NA
df
  x y  a  b  c  d  e
1 1 a NA NA NA NA NA
2 2 b NA NA NA NA NA
3 3 c NA NA NA NA NA
4 4 d NA NA NA NA NA
5 5 e NA NA NA NA NA

解决方案 2:使用 quo() 和 :=

@Gregor 的指导和有用的链接展示了一些函数,以及几乎所有的 tidyverse,并不像我们预期的那样计算对象。

第一次使用单个新列进行测试:

df <- data.frame(x=c(1:5), y=letters[1:5]) # refresh test data

varlevel <- levels(df$y)[1] # where level 1=a
df <- mutate(df, !!varlevel := NA)
rm(varlevel) # cleanup
df
  x y  a
1 1 a NA
2 2 b NA
3 3 c NA
4 4 d NA
5 5 e NA

然后将其放入for循环中,将每个因子水平捕获为一个新列:

df <- data.frame(x=c(1:5), y=letters[1:5]) # refresh test data

for (i in 1:length(levels(df$y))) {
+   varlevel <- levels(df$y)[i]
+   df <- mutate(df, !!varlevel := NA)
+   rm(varlevel) # cleanup
+   }
df
  x y  a  b  c  d  e
1 1 a NA NA NA NA NA
2 2 b NA NA NA NA NA
3 3 c NA NA NA NA NA
4 4 d NA NA NA NA NA
5 5 e NA NA NA NA NA