【问题标题】:Replace value per row with value in first column用第一列中的值替换每行的值
【发布时间】:2016-08-03 23:39:43
【问题描述】:

我的问题很简单。我有一个数据框,每一行都有不同的数字,超过 100 列。第一列始终是非零数字。我想要做的是将每行(不包括第一列)中的每个非零数字替换为行中的第一个数字(第一列的值)

我会认为 ifelse 和 for 循环遍历行,但必须有一个更简单的矢量化方式来做到这一点......

【问题讨论】:

  • 贴一个适合测试和演示的例子。

标签: r if-statement dataframe


【解决方案1】:

另一种方法是使用sapply,它比循环更有效。假设您的数据在数据框中df

df[,-1] <- sapply(df[,-1], function(x) {ind <- which(x!=0); x[ind] = df[ind,1]; return(x)})

在这里,我们将function 应用于df 除了第一列之外的所有列。在function 中,x 依次是这些列中的每一列:

  1. 首先使用which 查找列中为零的行索引。
  2. x中的这些行设置为df第一列的行中的对应值。
  3. 返回列

请注意,函数中的操作都是在列上“矢量化”的。也就是说,没有循环列的行。 sapply 的结果是处理后的列的矩阵,它将替换 df 的所有不是第一列的列。

请参阅 this 以获得对 *apply 系列函数的出色评论。

希望这会有所帮助。

【讨论】:

  • 优秀。谢谢你。只是出于好奇,我们不能使用 apply 对每一行而不是每一列执行相同的操作吗?
  • apply 用于在数组的某个维度上应用函数。请参阅 this SO answer 以获得对 *apply 函数系列的良好评论。
  • 似乎这并没有达到我想要的效果,但这只是将 which==0 更改为 which!=0 的情况。请记住,我想将所有 nonzeros 更改为每行的第一个数字。从我的 iPad 发布,所以还没尝试过
【解决方案2】:

由于你的数据不是很大,我建议你使用一个简单的循环

for (i in 1:nrow(mydata))
{
 for (j in 2:ncol(mydata)
  {

    mydata[i,j]<- ifelse(mydata[i,j]==0 ,0 ,mydata[i,1])
  }
 }

【讨论】:

  • 感谢您的回答。但是数据集实际上非常大,我正在寻找一种更加矢量化/ r 的方式来做到这一点。同样在您的解决方案中,第一列数据也不会被替换吗?我需要第一列保持完整。
  • 如果我没记错的话,ifelse末尾应该是mydata[i,1]而不是mydata[1,j]
  • 抱歉错误。这主要是因为此时多任务处理 :) 希望通过新的变化,您的第二个问题得到解决。我同意这不是解决此问题的最有效方法。我很想看看其他人的答案,看看他们如何解决这个问题。
【解决方案3】:

假设您的数据框是dat,我有一个全矢量化解决方案供您使用:

mat <- as.matrix(dat[, -1])
pos <- which(mat != 0)
mat[pos] <- rep(dat[[1]], times = ncol(mat))[pos]
new_dat <- "colnames<-"(cbind.data.frame(dat[1], mat), colnames(dat))

示例

set.seed(0)
dat <- "colnames<-"(cbind.data.frame(1:5, matrix(sample(0:1, 25, TRUE), 5)),
                    c("val", letters[1:5]))
#  val a b c d e
#1   1 1 0 0 1 1
#2   2 0 1 0 0 1
#3   3 0 1 0 1 0
#4   4 1 1 1 1 1
#5   5 1 1 0 0 0

我上面的代码给出:

#  val a b c d e
#1   1 1 0 0 1 1
#2   2 0 2 0 0 2
#3   3 0 3 0 3 0
#4   4 4 4 4 4 4
#5   5 5 5 0 0 0

您想要一个基准?

set.seed(0)
n <- 2000  ## use a 2000 * 2000 matrix
dat <- "colnames<-"(cbind.data.frame(1:n, matrix(sample(0:1, n * n, TRUE), n)),
                    c("val", paste0("x",1:n)))

## have to test my solution first, as aichao's solution overwrites `dat`

## my solution
system.time({mat <- as.matrix(dat[, -1])
            pos <- which(mat != 0)
            mat[pos] <- rep(dat[[1]], times = ncol(mat))[pos]
            "colnames<-"(cbind.data.frame(dat[1], mat), colnames(dat))})
#   user  system elapsed 
#  0.352   0.056   0.410 

## solution by aichao
system.time(dat[,-1] <- sapply(dat[,-1], function(x) {ind <- which(x!=0); x[ind] = dat[ind,1]; x}))
#   user  system elapsed 
#  7.804   0.108   7.919 

我的解决方案快 20 倍!

【讨论】:

  • 尚未尝试重现和理解您的代码,但结果不是我想要的。我希望为每行中的第一个数字赋予非零值,您的解决方案将零更改为第一个数字
  • 我接受了一个易于理解的解决方案,@aichao 非常友好地为他的代码的工作原理提供了详尽的解释。对我作为一个初学者来说,这比拥有绝对的最佳表现更重要,这不是一场比赛,而是一次学习更多关于 r 的练习。
猜你喜欢
  • 2013-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 1970-01-01
  • 2020-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多