如何加速r中的for循环
我只能为下面的问题陈述想出一个迭代版本。它可以工作,但速度很慢。这是一个扁平化数据的例子 对于我的数据帧中的每一行,我都会计算它——我在“agevalues”中存储了一些值。这些年龄值中的每一个都有一个等效列,因此,如果该值为50,则等效列名为age_50。我检查从“age1”到“age3”的任何列是否包含“agevalues”中的值。如果是,如中所示,如果存在值50,则我将此行的age_250设置为1 请参阅下面我的解决方案如何加速r中的for循环,r,R,我只能为下面的问题陈述想出一个迭代版本。它可以工作,但速度很慢。这是一个扁平化数据的例子 对于我的数据帧中的每一行,我都会计算它——我在“agevalues”中存储了一些值。这些年龄值中的每一个都有一个等效列,因此,如果该值为50,则等效列名为age_50。我检查从“age1”到“age3”的任何列是否包含“agevalues”中的值。如果是,如中所示,如果存在值50,则我将此行的age_250设置为1 请参阅下面我的解决方案 age1=c(20,30,30) age2=c(10,20,45)
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 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
下面是一个替代实现,它只使用一个
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
编辑说明:针对每行多个匹配的情况进行了调整,只返回1(而不是匹配的数量)
评论后编辑: 到矩阵的转换由
sapply
完成,因为它使用默认的simplify=TRUE
设置。要了解发生了什么,请一步一步地看:
是一个循环,为每个循环提供一个agevalues元素,即它从第一个元素开始,在本例中为20sapply(agevalues,…)
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
在这个阶段,您已经有了一个矩阵,指示条件为真的位置。然后,将其包装在rowSums
中,结果是:
rowSums(df == 20)
#[1] 1 1 0
它告诉您每行有多少个匹配项。请注意,如果一行中有2个或更多匹配项,rowSums
将为该行返回一个大于1的值。因为您只希望返回0或1个条目,所以可以在行和中检查元素是0(无匹配项)还是>0(任何大于或等于1的匹配项数):
如您所见,这将返回一个包含真/假条目的逻辑向量。由于您希望最终输出为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
请注意,如果您在sapply
中指定了simplify=FALSE
,您将得到一个列表作为回报:
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
希望这会有所帮助。这里有一个替代实现,只需使用一个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
编辑说明:针对每行多个匹配的情况进行了调整,只返回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
在这个阶段,您已经有了一个矩阵,指示条件为真的位置。然后,将其包装在rowSums
中,结果是:
rowSums(df == 20)
#[1] 1 1 0
它告诉您每行有多少个匹配项。请注意,如果一行中有2个或更多匹配项,rowSums
将为该行返回一个大于1的值。因为您只希望返回0或1个条目,所以可以在行和中检查元素是0(无匹配项)还是>0(任何大于或等于1的匹配项数):
如您所见,这将返回一个包含真/假条目的逻辑向量。由于您希望最终输出为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
请注意,如果您在sapply
中指定了simplify=FALSE
,您将得到一个列表作为回报:
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
希望能有所帮助。试试:
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
尝试:
您可以从qdapTools
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
库(qdapTools)
df1您可以从qdapTools
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
库(qdapTools)
df1请提供一些示例数据来运行您的代码。@Docendiscimus,好的,给我几分钟时间。生成样本data@docendodiscimus,已经编辑了上面的问题。或者只需使用(stack(df),ftable(ind,values))
创建一个表,并将其绑定在一起,如果您需要cbind(df,as.matrix(with(stack(df),ftable(ind,values))
请提供一些示例数据来运行您的代码。@docendodiscimus,好的,给我几分钟。生成样本data@docendodiscimus,已编辑了上述问题。或者只需使用(堆栈(df),ftable(ind,values))创建一个表,
,并将其绑定在一起(如果需要cbind(df,as.matrix)(使用(堆栈(df),ftable(ind,values)))
我没有使用原始帖子进行基准测试,但这应该已经是一个不错的性能改进。谢谢,@ColonelBeauvel。你的答案也是一个很好的建议。我也在转切,但rowSums在那里很聪明!最好的方法。@Docendiscimus,谢谢。我理解了第一行和第三行。一段时间以来,我一直在努力理解第二行,但没有完全理解。行和(df==x)>0L)有什么作用?它是如何产生一个0-1矩阵的?@IAMTubby,我在我的回答中添加了一个解释。我没有用原始帖子进行基准测试,但这应该已经是一个不错的性能改进。谢谢,@ColonelBeauvel。你的答案也是一个很好的建议。我也在转切,但rowSums在那里很聪明!最好的方法。@Docendiscimus,谢谢。我理解了第一行和第三行。一段时间以来,我一直在努力理解第二行,但没有完全理解。行和(df==x)>0L)有什么作用?它是如何产生一个0-1矩阵的?@IAMTubby,我在我的答案中添加了一个解释。