Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/81.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何加速r中的for循环_R - Fatal编程技术网

如何加速r中的for循环

如何加速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)

我只能为下面的问题陈述想出一个迭代版本。它可以工作,但速度很慢。这是一个扁平化数据的例子

对于我的数据帧中的每一行,我都会计算它——我在“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 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
设置。要了解发生了什么,请一步一步地看:

  • 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

希望这会有所帮助。

这里有一个替代实现,只需使用一个
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,我在我的答案中添加了一个解释。