我只能想到下面问题陈述的迭代版本。它有效,但速度很慢。这是扁平化数据的一个例子。
对于我的数据框中的每一行,我都计算了这个 - 我有一些值存储在'agevalues'中。每个年龄值都有一个等效列,例如,如果值为50,则等效列名称为age_50。我检查'age1'到'age3'中的任何列是否包含'agevalues'中的值。如果是,则如果存在值50,则将此行的age_250设置为1.
请参阅下面的解决方案
age1=c(20,30,30)
age2=c(10,20,45)
age3=c(50,60,70)
df = data.frame(age1,age2,age3)
#finding unique values of age1...age3 columns
agevalues = NULL
for(i in which(names(df) == "age1"):which(names(df) == "age3"))
{
agevalues = c(agevalues, unique(df[,i]))
}
uniqueagevalues = unique(agevalues)
#creating a column for each of these age buckets
count = 0;
for(i in 1:length(uniqueagevalues))
{
newcol = paste("age_",as.character(uniqueagevalues[i]),sep="");
print(newcol)
df[newcol] = 0
count = count + 1;
}
#putting 1 if present, else 0
count = 0;
for(i in 1:nrow(df))
{
for(j in 1:length(uniqueagevalues))
{
if(length(which(df[i,which(names(df) == "age1"):which(names(df) == "age3")] == uniqueagevalues[j])))
{
coltoaddone = paste("age_",as.character(uniqueagevalues[j]),sep="");
print(coltoaddone)
df[i,coltoaddone] = 1;
}
count = count + 1;
}
}
输入
> df
age1 age2 age3
1 20 10 50
2 30 20 60
3 30 45 70
输出
> df
age1 age2 age3 age_20 age_30 age_10 age_45 age_50 age_60 age_70
1 20 10 50 1 0 1 0 1 0 0
2 30 20 60 1 1 0 0 0 1 0
3 30 45 70 0 1 0 1 0 0 1
答案 0 :(得分:3)
这是一个替代实现,只使用一个sapply
循环和一些前后的矢量化:
# get the unique age values:
agevalues <- unique(unname(unlist(df)))
# check which agevalues are present in which row:
m <- sapply(agevalues, function(x) as.integer(rowSums(df == x) > 0L))
# add the result to the original data and set column names:
df <- setNames(cbind(df, m), c(names(df), paste0("age_", agevalues)))
df
# age1 age2 age3 age_20 age_30 age_10 age_45 age_50 age_60 age_70
#1 20 10 50 1 0 1 0 1 0 0
#2 30 20 60 1 1 0 0 0 1 0
#3 30 45 70 0 1 0 1 0 0 1
age1=c(20,30,30)
age2=c(10,20,45)
age3=c(50,60,70)
df = data.frame(age1,age2,age3)
编辑注释:针对每行多个匹配的情况进行调整,仅返回1(不是匹配数)
评论后编辑:
转换为矩阵由sapply
完成,因为它使用默认的simplify = TRUE
设置。要了解会发生什么,请一步一步地查看:
sapply(agevalues, ... )
是一个循环,它为每个循环提供一个agevalues元素,即它从第一个元素开始,在这种情况下为20。接下来会发生什么:
df == 20 # (because x == 20 in the first loop)
# age1 age2 age3
#[1,] TRUE FALSE FALSE # 1 TRUE in this row
#[2,] FALSE TRUE FALSE # 1 TRUE in this row
#[3,] FALSE FALSE FALSE # 0 TRUE in this row
在此阶段,您已经有一个矩阵,指示条件为TRUE的位置。然后,将其包装在rowSums
中,会发生什么:
rowSums(df == 20)
#[1] 1 1 0
它告诉你每行有多少匹配。请注意,如果一行中有2个或更多匹配,rowSums
将为该行返回值> 1。因为您只想要返回0或1个条目,所以您可以检查rowSums
元素是0(不匹配)还是> 0(任意数量的匹配大于或等于1):
rowSums(df == agevalues[1]) > 0L
#[1] TRUE TRUE FALSE
如您所见,这将返回带有TRUE / FALSE条目的逻辑向量。由于您希望在最终输出中使用0/1,因此可以使用以下命令将逻辑转换为整数:
as.integer(rowSums(df == agevalues[1]) > 0L)
# [1] 1 1 0
这些是您在sapply输出中看到的值。而且,由于你是为agevalues中的每个元素做的,sapply能够将列表中的结果简化为这样的矩阵:
sapply(agevalues, function(x) as.integer(rowSums(df == x) > 0L))
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#[1,] 1 0 1 0 1 0 0
#[2,] 1 1 0 0 0 1 0
#[3,] 0 1 0 1 0 0 1
请注意,如果您在simplify = FALSE
中指定了sapply
,则会获得一个回复列表:
sapply(agevalues, function(x) as.integer(rowSums(df == x) > 0L), simplify = FALSE)
[[1]]
[1] 1 1 0
[[2]]
[1] 0 1 1
[[3]]
[1] 1 0 0
[[4]]
[1] 0 0 1
[[5]]
[1] 1 0 0
[[6]]
[1] 0 1 0
[[7]]
[1] 0 0 1
希望有所帮助。
答案 1 :(得分:3)
您可以尝试 qdapTools
中的mtabulate
library(qdapTools)
df1 <- mtabulate(as.data.frame(t(df)))
names(df1) <- paste('age', names(df1), sep="_")
cbind(df, df1)
# age1 age2 age3 age_10 age_20 age_30 age_45 age_50 age_60 age_70
#1 20 10 50 1 1 0 0 1 0 0
#2 30 20 60 0 1 1 0 0 1 0
#3 30 45 70 0 0 1 1 0 0 1
df <- structure(list(age1 = c(20L, 30L, 30L), age2 = c(10L, 20L, 45L
), age3 = c(50L, 60L, 70L)), .Names = c("age1", "age2", "age3"
), class = "data.frame", row.names = c("1", "2", "3"))
答案 2 :(得分:2)
尝试:
labels = paste("age",unique(unlist(df)), sep='_')
lst = lapply(data.frame(t(df)), function(u) as.integer(labels %in% paste("age",u,sep='_')))
setNames(cbind(df,do.call(rbind, lst)),c(names(df),labels))
# age1 age2 age3 age_20 age_30 age_10 age_45 age_50 age_60 age_70
#X1 20 10 50 1 0 1 0 1 0 0
#X2 30 20 60 1 1 0 0 0 1 0
#X3 30 45 70 0 1 0 1 0 0 1