根据其他列名和值创建新列
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
而不是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
这是不可取的。至于解决方案中发生了什么的解释,
Labs <- 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_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"
,依此类推。
在定义了对单行执行所需操作的函数后,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 的所有解释。非常感谢您的帮助。以上是关于根据其他列名和值创建新列的主要内容,如果未能解决你的问题,请参考以下文章