【问题标题】:Create a new column based on other column names and values根据其他列名和值创建新列
【发布时间】:2014-10-12 16:12:07
【问题描述】:

我正在尝试根据该数据框中的其他几个列名及其值创建一个新列。

数据看起来像这样

user_id  Gender Age  Cate_Ch_Bot  Cate_Ch_Phy  Cate_Ch_Chem  Cate_Ch_Comp  Cate_Ch_Zoo
0001      F     13      0              1           0              1              0
0002      M     17      1              1           0              0              0
0003      F     13      0              0           0              0              0
0004      F     12      0              0           1              0              0
0005      F     14      0              1           0              0              1
0006      M     16      0              0           0              0              0

我需要创建一个类别列,其中将包含所有值为 1 的类别。如果用户没有类别,则它为空白或 NA。

所以想要的输出是:

user_id  Gender Age Cate_Ch_Bot Cate_Ch_Phy Cate_Ch_Chem Cate_Ch_Comp Cate_Ch_Zoo   Ch_Category
0001      F     13      0            1            0           1           0         Phy:Comp
0002      M     17      1            1            0           0           0         Bot:Phy
0003      F     13      0            0            0           0           0         NA
0004      F     12      0            0            1           0           0         Chem
0005      F     14      0            1            0           0           1         Phy:Zoo
0006      M     16      0            0            0           0           0         NA

我正在尝试遍历列名,但不确定如何正确执行。

test$category = ""
for (j in 1:dim(test)[1]){
  for (i in colnames(test[4:14])){
    name = colnames(test[i])   
    if (test[j,name] == 1){
      test$category[j] = paste(test$category[j], colnames(test[i]),sep=":")
    }
  }
}

我将非常感谢这方面的任何帮助。

【问题讨论】:

    标签: r


    【解决方案1】:

    这样的事情怎么样:

    Df <- data.frame(
      user_id=1:6,
      Gender=rep(c("M","F"),3),
      Age=sample(13:17,6,replace=TRUE),
      Cate_Ch_Bot=c(0,1,rep(0,4)),
      Cate_Ch_Phy=c(1,1,0,0,1,0),
      Cate_Ch_Chem=c(0,0,0,1,0,0),
      Cate_Ch_Comp=c(1,0,0,0,0,0),
      Cate_Ch_Zoo=c(0,0,0,0,1,0),
      stringsAsFactors=FALSE)
    ##
    Labs <- gsub("Cate_Ch_","",names(Df)[-c(1:3)])
    ##
    getCols <- function(x)
    {
      Reduce(function(x,y){paste0(x,":",y)},Labs[which(x==1)])
    }
    ##
    Df$new <- apply(Df[,-c(1:3)],1,function(X){
      if( is.null(getCols(X)) ){
        ""
      } else {
        getCols(X)
      }
    })
    ##
    > Df2
      user_id Gender Age Cate_Ch_Bot Cate_Ch_Phy Cate_Ch_Chem Cate_Ch_Comp Cate_Ch_Zoo      new
    1       1      M  13           0           1            0            1           0 Phy:Comp
    2       2      F  14           1           1            0            0           0  Bot:Phy
    3       3      M  16           0           0            0            0           0         
    4       4      F  14           0           0            1            0           0     Chem
    5       5      M  14           0           1            0            0           1  Phy:Zoo
    6       6      F  16           0           0            0            0           0         
    

    已编辑: 我不得不在apply 函数内用if..else 语句包装getCols,因为它实际上返回了list 而不是vector,NULL 元素用于Df 的行,其中没有一列的值为1。以前,它在表面上看起来像一个data.frame,但仔细观察会发现:

    > class(Df)
    [1] "data.frame"
    > str(Df)
    'data.frame':   6 obs. of  9 variables:
     $ user_id     : int  1 2 3 4 5 6
     $ Gender      : chr  "M" "F" "M" "F" ...
     $ Age         : int  13 14 16 14 14 16
     $ Cate_Ch_Bot : num  0 1 0 0 0 0
     $ Cate_Ch_Phy : num  1 1 0 0 1 0
     $ Cate_Ch_Chem: num  0 0 0 1 0 0
     $ Cate_Ch_Comp: num  1 0 0 0 0 0
     $ Cate_Ch_Zoo : num  0 0 0 0 1 0
     $ new         :List of 6
      ..$ : chr "Phy:Comp"
      ..$ : chr "Bot:Phy"
      ..$ : NULL
      ..$ : chr "Chem"
      ..$ : chr "Phy:Zoo"
      ..$ : NULL
    

    这是不可取的。至于解决方案中发生了什么的解释,

    1. Labs &lt;- gsub("Cate_Ch_","",names(Df)[-c(1:3)]) 只是一个方便的步骤,这样就有一个现成标签的向量可以参考。 gsub 正在获取目标列的名称并将"Cate_Ch_" 替换为空字符串"",以便剩余的文本可以用作标签。
    2. getCols 函数被构造为对单个向量 x 进行操作 - 在这种情况下,Df 的单行。它使用Reduce 以累积方式应用子操作(粘贴两个字符串,由: 分隔),其中该子操作以匿名函数function(x,y){ ... } 的形式给出。我们给function(x,y) 的输入是整个Labs 向量的一个子集——该子集仅开始于x==1 所在行中的那些元素。使用which(x==1) 只会为我们提供等于一的行索引。所以对于Df 的第2 行,which(x==1) 给出向量c(1,2)(因为Cate_Ch_Bot 和Cate_Ch_Phy 在第2 行中的值为1)。评估Labs 内部的这个索引向量会为您提供1 和2 的元素Labs - c("Bot","Phy")。当它传递给我们的Reduce(function(x,y) ... 调用时,它将所有元素粘贴在一起,由: 分隔,并返回单个字符值"Bot:Phy"。如果Reduce 的输入是c("A","B","C","D"),它将返回"A:B:C:D",依此类推。
    3. 在定义了对单行执行所需操作的函数后,apply 用于对多行执行操作。正如我上面提到的,我必须对我最初对apply 的调用进行轻微更改,以确保它返回vector 而不是list。

    在所有的打字之后...... @Richard Scriven 指出了一个更好的答案:

    > apply(Df[-(1:3)] == 1, 1, function(x) {
      paste(gsub(".*_", "", names(which(x))), collapse = ":")
    })
    [1] "Phy:Comp" "Bot:Phy"  ""         "Chem"     "Phy:Zoo"  "" 
    

    【讨论】:

    • 非常感谢@nrussell 的帮助。该代码适用于我的数据集。但是,由于我是 r 新手,所以我无法完全理解代码,如果您也能简单地解释一下代码,我将不胜感激。
    • 当然,请给我一分钟,我将介绍发生了什么。还要注意我对解决方案所做的修改。
    • 您也可以使用apply(df[-(1:3)] == 1, 1, function(x) paste(gsub(".*_", "", names(which(x))), collapse = ":")) 而不需要if 声明。如有必要,更改为 NA
    • 非常感谢@nrussell 的所有解释。非常感谢您的帮助。
    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    • 2020-09-24
    • 2021-10-03
    • 2020-08-18
    • 2022-07-05
    相关资源
    最近更新 更多