快速地给定范围内的R个独特组合,并使用更少的系统资源

时间:2019-04-30 23:19:04

标签: r tidyverse

这是来自这里的后续问题: https://stackoverflow.com/a/55912086/3988575

我有一个像这样的数据集:

ID=as.character(c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20))
IQ=c(120.5,128.1,126.5,122.5,127.1,129.7,124.2,123.7,121.7,122.3,120.9,122.4,125.7,126.4,128.2,129.1,121.2,128.4,127.6,125.1)
Section=c("A","A","B","B","A","B","B","A","B","A","B","B","A","A","B","B","A","B","B","A")
zz=data.frame(ID,IQ,Section)
zz_new=do.call("rbind", replicate(zz, n=30, simplify = FALSE))

我想做的就是根据智商的范围来匹配人们(这是前面的问题)。

现在,我想创建多个级别的范围。例如,一个范围可以是10个IQ类:120-121,121-122,122-123 .... 129-130。另一个示例是单个IQ类:120-130。以上所有可能的组合都可以通过以下方式获得:

IQ_Class=c(120,121,122,123,124,125,126,127,128,129,130)
n = length(IQ_Class)-2
all_combin=expand.grid(replicate(n, 0:1, simplify = FALSE))
all_combin$First=1
all_combin$Last=1
all_combin_new=all_combin[c("First",names(all_combin)[1:(length(names(all_combin))-2)],"Last")] #Reorder columns
all_combin_new = t((apply(all_combin_new,1,function(x)(x*IQ_Class)))) #Multiply by IQ classes
all_combin_new = apply(all_combin_new, 1, function(x) { x[x!=0] })

请注意,最终对象all_combin_new提供了所有类列表的列表(总共512个类)。

现在,我要做的是获取一个类(all_combin_new中的一个元素),并通过其部分在该特定IQ类中创建ID的所有组合。保存此数据集,并从all_combin_new获取下一个类,然后重复该操作。

从上一个答案中,我可以通过更改上一个问题中的以下内容来修改代码以按部分考虑组合:

zz1=list("list",length(all_combin_new))
for (i in 1:length(all_combin_new)){ #changed this line to run for all combinations in all_combin_new
  zz2=all_combin_new[[i]]
  zz11=zz_new%>%
    mutate(ID=as.character(ID),vec=as.character(cut(IQ,zz2,right=F)))%>%
    group_by(vec,Section)%>% #Changed this line
    summarize(if(n()>1)list(data.frame(t(combn(ID,2)),stringsAsFactors = F))
              else list(data.frame(X1=ID,X2=ID,stringsAsFactors = F)))%>%
    unnest()%>%
    bind_cols(read.csv(text=gsub("[^0-9,]","",.$vec),h=F))
  zz1[[i]]=as.data.frame(zz11)
}

我的实际数据集具有20个断面的大约10K(与zz_new相比)观测结果(与all_combin_new列表的长度= 512相比,IQ的范围为2 ^ 18 = 262144)。这会导致两个主要问题:

a)时间:速度极慢。有没有办法提高速度?

b)创建的对象的大小:在我的测试中,即使不考虑大量的组合,列表也会变得太大而导致代码失败。我在这里可以使用哪些替代方法?请注意,在这里获得的列表列表中,我还需要进行进一步的计算。

任何帮助将不胜感激。预先感谢。

P.S。如果任何部分不清楚或代码的任何部分有一些意外错误,请通知我。

1 个答案:

答案 0 :(得分:1)

编辑:现在带有循环,可以遍历所有IQ组合并包括Section作为联接的键。

我在链接的问题中使用了样本数据。无需创建列表和循环,而是可以立即执行所有操作。

请注意,有一个笛卡尔积,因此它仍然可能会遇到内存问题。如果遇到问题,您可以随时尝试使用data.table,因为您可以进行非等额联接。

library(tidyverse)

zz <- tibble(ID=1:12
                 ,IQ=c(120.5,123,125,122.5,122.1,121.7,123.2,123.7,120.7,122.3,120.1,122)
                 ,Section=c("A","A","B","B","A","B","B","A","B","A","B","B")
)

IQ_Class <- c(120,122,124,126)

IQ_Classes <- data.frame(First = 1
             ,expand.grid(replicate(length(IQ_Class)-2, 0:1, simplify = FALSE))
             ,Last = 1)

IQ_Classes <- IQ_Classes * IQ_Class[col(IQ_Classes)]                    

IQ_Classes_List <- apply(IQ_Classes, 1, function(x) { x[x!=0] })

all_combos <- lapply(IQ_Classes_List
                     , function(IQs) 
                       {
                       z_cut <- zz%>%
                         mutate(cut_range = cut(IQ, IQ_Class, right = F, labels = F))

                       inner_join(z_cut
                                  , z_cut %>%
                                    select(V2 = ID, cut_range, Section)
                                  , by = c('cut_range', 'Section'))%>%
                         filter(V2 > ID) %>%
                         mutate(Previous_IQ_class = IQs[cut_range],
                                Next_Class = IQs[cut_range+1])
                       }
                     )%>%
  bind_rows(.id = 'IQ_List')