在数据框中重命名几列:为什么使用 %in% 运算符的 ifelse 函数不能保留顺序?

4
这是关于这个问题的参考。
我想重命名大型数据框中的一部分列。 我希望下面的代码将把列X4X5X6X7重命名为gradekgrade1grade2grade3
set.seed(1)
in.df <- data.frame( matrix( rnorm(60), ncol=10) )
names(in.df) <- ifelse( names(in.df) %in% c('X4', 'X5', 'X6', 'X7'),
                         paste('grade', c('k',1:3), sep=''),
                         names(in.df) )

然而,
> names(in.df)
 [1] "X1"     "X2"     "X3"     "grade3" "gradek" "grade1" "grade2" "X8"    
 [9] "X9"     "X10"   

尽管
> paste('grade', c('k',1:3), sep='')
[1] "gradek" "grade1" "grade2" "grade3"

展示了顺序并未被保留。该帖子建议使用match代替%in%,但在这种情况下并不起作用。(也许在R的其他版本中是正确的。在我安装的版本(2.15.3)中,match的帮助页面表明%in%是通过match定义的,所以改变它是没有帮助的。)
任何帮助都将不胜感激!
接受的答案 这个答案解决了我的重命名问题。 这个答案解释了奇怪的行为是由于循环使用造成的。

我认为这是因为你的“是”条件与“否”条件的长度不同。请注意文档中有关循环利用的讨论。 - joran
我认为你是正确的。嗯,我会稍微试一下这个。 - Nathan VanHoudnos
为什么你不能像这样使用 names(in.df)[names(in.df) %in% c("X4", "X5", "X6", "X7")] <- paste('grade', c('k',1:3), sep='') - A5C1D2H2I1M1N2O1R2T1
@AnandaMahto 我正要把那个作为答案发布...我认为你应该让它正式化。 - joran
@AnandaMahto 那会起作用。我之前尝试过 names(in.df[,c("X4", "X5", "X6", "X7")]) <- paste('grade', c('k',1:3), sep=''),但不行。我没有想到需要对 names 的调用进行子集化,而不是数据框本身。 - Nathan VanHoudnos
2个回答

5

%in%应该可行,但也许使用match更好。

考虑以下内容。“A”和“B”代表您的names(in.df)。我们希望按照那个顺序使用paste('grade', c('k',1:3), sep='')的结果替换“matchme”中的值。

比较不同的输出:

A <- B <- c("X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10")
matchme <- c('X4', 'X7', 'X6', 'X5')
A[A %in% matchme] <- paste('grade', c('k',1:3), sep='')
A
#  [1] "X1"     "X2"     "X3"     "gradek" "grade1" "grade2" "grade3" "X8"    
#  [9] "X9"     "X10"  
B[match(matchme, B)] <- paste('grade', c('k',1:3), sep='')
B
#  [1] "X1"     "X2"     "X3"     "gradek" "grade3" "grade2" "grade1" "X8"    
#  [9] "X9"     "X10"   

我不理解你的例子。%in% 运算符产生了正确的顺序,而 match 函数则没有。 - Nathan VanHoudnos
@NathanVanHoudnos 这是因为 matchme 中的顺序问题。如果你按照“正确”的顺序排序,它会得到同样的结果。 - joran
@NathanVanHoudnos,请阅读我的序言以及我为什么建议使用“match”。我的观点是,我可以使用“match”指定顺序(将X4替换为gradek,x7替换为grade1等),但是我不能使用“%in%”进行这样的操作。 - A5C1D2H2I1M1N2O1R2T1
我本可以花时间想出一个更好的例子,但现在已经是午夜了,我要结束今天的工作了 :) - A5C1D2H2I1M1N2O1R2T1
1
啊,我没看到matchme改变了顺序!现在我明白了。谢谢你的帮助。然后去睡觉吧。 :) - Nathan VanHoudnos

4

Ananda的答案提供了一个很好的方法,告诉你如何做到你想要的。我将回答为什么你得到了你得到的结果而不是你期望的那个问题。

名字似乎顺序错乱的原因与 ifelse 如何工作和参数循环有关。让我们看一下ifelse的三个参数:

> list(names(in.df) %in% c('X4', 'X5', 'X6', 'X7'),
+      paste('grade', c('k',1:3), sep=''),
+      names(in.df))
[[1]]
 [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE

[[2]]
[1] "gradek" "grade1" "grade2" "grade3"

[[3]]
 [1] "X1"  "X2"  "X3"  "X4"  "X5"  "X6"  "X7"  "X8"  "X9"  "X10"

ifelse函数根据第一个参数是TRUE还是FALSE来决定选择哪个相应的元素。但是第二个参数的长度不如第一个长,因此它被循环使用以达到相同的长度。将它们放入数据框中,以便更容易地并排查看它们,并手动扩展第二组名称,得到:

> data.frame(test = names(in.df) %in% c('X4', 'X5', 'X6', 'X7'),
+            `TRUE` = rep(paste('grade', c('k',1:3), sep=''),length=10),
+            `FALSE` = names(in.df))
    test  TRUE. FALSE.
1  FALSE gradek     X1
2  FALSE grade1     X2
3  FALSE grade2     X3
4   TRUE grade3     X4
5   TRUE gradek     X5
6   TRUE grade1     X6
7   TRUE grade2     X7
8  FALSE grade3     X8
9  FALSE gradek     X9
10 FALSE grade1    X10

因为参数回收的缘故,新名称的第4、5、6和7个元素被使用,这些对应于第4、1、2和3个元素。

没关系!感谢您回答我问题的另一部分。+1。 - Nathan VanHoudnos
+1 - 很好的解释,从listdata.frame再到解释循环利用。 - Ricardo Saporta

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