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

Posted

技术标签:

【中文标题】根据其他列名和值创建新列【英文标题】: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=":")
    
  

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

【问题讨论】:

【参考方案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 而不是vectorNULL 元素用于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

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

    Labs &lt;- gsub("Cate_Ch_","",names(Df)[-c(1:3)]) 只是一个方便的步骤,这样就有一个现成标签的向量可以参考。 gsub 正在获取目标列的名称并将"Cate_Ch_" 替换为空字符串"",以便剩余的文本可以用作标签。 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_BotCate_Ch_Phy 在第2 行中的值为1)。评估Labs 内部的这个索引向量会为您提供12 的元素Labs - c("Bot","Phy")。当它传递给我们的Reduce(function(x,y) ... 调用时,它将所有元素粘贴在一起,由: 分隔,并返回单个字符值"Bot:Phy"。如果Reduce 的输入是c("A","B","C","D"),它将返回"A:B:C:D",依此类推。 在定义了对单行执行所需操作的函数后,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 的所有解释。非常感谢您的帮助。

以上是关于根据其他列名和值创建新列的主要内容,如果未能解决你的问题,请参考以下文章

oracle如何查找变量的列名和值? [关闭]

获取DataRow的列名和值

根据列值返回 SQL Server 列名和对应值

Spring Data JPA - 将列名和值作为参数传递

在 Qtsql PyQT4 中添加列名和值作为变量

SQL Server:导入 Excel 并在表中放置列名和值