根据其他列名称和值创建新列

时间:2014-10-12 16:12:07

标签: r

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

数据看起来像这样

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=":")
    }
  }
}

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

1 个答案:

答案 0 :(得分:3)

这样的事情怎么样:

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         

<强>编辑: 我必须在getCols函数中包含if..else apply语句,因为它实际上返回list而不是vectorNULL 1}} 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 <- 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)只是给我们行的索引等于1。因此,对于Df的第2行,which(x==1)给出了向量c(1,2)(因为Cate_Ch_BotCate_Ch_Phy在第2行中的值为1) 。评估Labs内的这个索引向量,可以得到1 - 2的元素Labsc("Bot","Phy")。将此传递给我们的Reduce(function(x,y) ...调用后,它会将所有元素粘贴在一起,以:分隔,并返回单个字符值"Bot:Phy"。如果Reduce的输入为c("A","B","C","D"),则会返回"A:B:C:D",依此类推。
  3. 在定义了一个函数以在单行上执行所需操作之后,apply用于执行多行操作。如上所述,我必须对原来的apply来电稍作修改,以确保它返回vector而不是list
  4. 然后打字...... @Richard Scriven指出了一个更好的答案:

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