如何在数据框中的列之间有效地交换元素?

时间:2017-07-25 17:28:32

标签: r dataframe

我问了一个类似的问题before,但我刚才意识到前面的例子在因子水平是等间距的意义上有点过于特殊。在这里,我想以更通用的方式重新构造问题,旧线程中的解决方案将无法正常工作。

假设我们在R中有以下数据框:

set.seed(1)
(tmp <- data.frame(x = 1:10, R1 = sample(c('A','D','F','G','I'), 10, replace = TRUE), R2 = sample(c('D','F','G','I','Z'), 10, replace = TRUE), stringsAsFactors=FALSE))

    x R1 R2
1   1  D  F
2   2  D  D
3   3  F  I
4   4  I  F
5   5  D  I
6   6  I  G
7   7  I  I
8   8  G  Z
9   9  G  F
10 10  A  I

请注意,两列R1R2不共享完全相同的元素。我想要执行以下操作:如果列R1和列R2的elemet索引(元素之间的连续顺序)之间的差异是奇数,则两个因子的级别需要在它们之间切换,可以通过以下代码执行:

for(ii in 1:dim(tmp)[1]) {
   kk <- which(levels(as.factor(tmp$R2)) %in% tmp[ii,'R2'], arr.ind = TRUE) - which(levels(as.factor(tmp$R1)) %in% tmp[ii,'R1'], arr.ind = TRUE)
   if(kk%%2!=0) { # swap the elements between the two columns
      qq <- tmp[ii,]$R1
      tmp[ii,]$R1 <- tmp[ii,]$R2
      tmp[ii,]$R2 <- qq
  }
}

由于两列R1R2不共享相同的元素,因此我有意创建了数据框tmp,而R1R2不是因此,我可以用上面的kludge代码淹没两列之间的元素。以下是交换后的输出:

    x R1 R2
1   1  D  F
2   2  D  D
3   3  I  F
4   4  I  F
5   5  D  I
6   6  G  I
7   7  I  I
8   8  Z  G
9   9  F  G
10 10  I  A

我的解决方案对于大型数据帧来说太尴尬和缓慢。任何优雅的方式来执行此操作?

1 个答案:

答案 0 :(得分:1)

# convert to character
dat[, c("R1", "R2")] <- lapply(dat[, c("R1", "R2")], as.character)

接下来,我们将您的行更改条件进行矢量化。所有TRUE元素都是需要评估和交换的行。

# get logical inidcator for elements to change
changeInd <- !!((match(dat$R2, levels(as.factor(dat$R2))) -
                match(dat$R1, levels(as.factor(dat$R1)))) %% 2)

# perform swapping for given rows
dat[changeInd, c("R1", "R2")] <- dat[changeInd, c("R2", "R1")]

在这里,我们使用match来选择需要更改的行。在此之后,使用[执行简单的变量交换。

返回

dat
    x R1 R2
1   1  D  F
2   2  D  D
3   3  F  I
4   4  F  I
5   5  D  I
6   6  G  I
7   7  I  I
8   8  G  Z
9   9  F  G
10 10  A  I

注意所需输出中可能存在拼写错误。从那以后

identical((sapply(seq_len(nrow(dat)),
           function(x) which(levels(as.factor(dat$R2)) %in% dat[x,'R2'], arr.ind = TRUE) -
                       which(levels(as.factor(dat$R1)) %in% dat[x,'R1'], arr.ind = TRUE)) %% 2) != 0,
          changeInd)
[1] TRUE

数据

dat <-
structure(list(x = 1:10, R1 = structure(c(1L, 1L, 4L, 4L, 1L, 
3L, 4L, 5L, 2L, 4L), .Label = c("D", "F", "G", "I", "Z"), class = "factor"), 
    R2 = structure(c(3L, 2L, 3L, 3L, 5L, 5L, 5L, 4L, 4L, 1L), .Label = c("A", 
    "D", "F", "G", "I"), class = "factor")), .Names = c("x", 
"R1", "R2"), class = "data.frame", row.names = c("1", "2", "3", 
"4", "5", "6", "7", "8", "9", "10"))