• R语言
  • 请教高手高效R语句(程序)写法。

在二楼的基础上, 我追加了data.table方法, 最终拿到的数据是20万个元素的list花费17.24秒完成所有计算, 应该能够满足你的需求. 程序运行效率与数据规模见下:
   user  system elapsed 
  16.33    0.67   17.24 
> length(dm)
[1] 200000
> sum(sapply(dm, length))
[1] 5090840
具体代码见下:
library(data.table)
system.time({
  # 生成dm数据
  length <- sample(1:50, 200000, replace=T)
  dm <- vector("list", 200000)
  for (i in 1:200000){
    dm[[i]] <- sample(1:100, length[i], replace=T)
  }
  # 将dm从list转换为data.table
  library(data.table)
  where <- rep(1:length(dm), times=length)
  what <- unlist(dm)
  dm_dt <- data.table(where=where, what=what)
  # rm(length, where, what); gc()
  # 将data.table从长表转化为宽表
  dm_dt <- dcast.data.table(dm_dt, where~what)
  # 循环运算
  while(nrow(dm_dt)!=0){
    # 那列最多? 该列的列名是什么?
    dc <- colnames(dm_dt)[which.max(dm_dt[, lapply(.SD, sum), .SDcols=2:ncol(dm_dt)])+1]
    # 按dc列删除数据行(原来list中的每个元素)
    dm_dt <- dm_dt[get(dc)==0,]
    # 按dc列删除数据列(没有用处的频次统计列)
    dm_dt <- dm_dt[, dc:=NULL, with=F]
  }
})
[未知用户]
哥,非常感谢您的帮助。可是我在运行unlist(dm)时,提示"cannot allocate vector of size 595.8Mb",我的内存是4G,也运行了memory.limit(3000)命令,问题依旧。有无良策?

[未知用户]
哥,非常感谢您的帮助。可是我在运行do.call(c,dm)时,提示”cannot allocate vector of size 595.8Mb”,我的内存是4G,也运行了memory.limit(3000)命令,问题依旧。有无良策?
稍微改下,顺便加点注释,优化下方法。
说一下,你的数据量级,列表内元素种数(V1~Vn,n是多大)。
(以下算法若是20万条数据,你的元素种类数超过700,转化后ndm内存占用就会超过500M。)
若是元素种数过多,可以改写成分块运算方法。
#统计数据类型数(低内存占用方案)
all<-unique(dm[[1]])
for(i in 2:length(dm)){
  all<-unique(c(all,dm[[i]]))
}
#数据转换函数
trand <- function(dmi){
  all%in%dmi
}
#数据转换
ndm<-do.call(rbind,lapply(dm,trand))
colnames(ndm)<-all
tndm<-ndm
while(length(tndm)!=0){
  #那列最多?
  if(is.null(nrow(tndm))){
    print(names(tndm[tndm][1]))
    break
  }else{  
    dc<-which.max(matrix(1,1,nrow(tndm))%*%tndm)
  }
  print(colnames(tndm)[dc])
  #按该列删除数据列
  tndm<-tndm[tndm[,dc]==FALSE,-dc]
}
这类表,的复杂程度指标有:列表长度、列表总数据量、元素种类数。
你需要给我们至少这三样指标才能较好的分析该用什么算法。
举个例子:
以下数据的三样指标分别为:
列表长度:6
列表总数据量:23
元素种类数:9
[[1]]
[1] “V1″ “V2″ “V3″ “V4″ “V5″ “V6″ “V7″ “V8″

[[2]]
[1] “V1″ “V3″

[[3]]
[1] “V3″ “V5″ “V6″

[[4]]
[1] “V1″ “V2″ “V4″ “V7″ “V8″ “V9″

[[5]]
[1] “V3″ “V7″

[[6]]
[1] “V8″ “V9″
[未知用户]
多谢兄台指教。我已通过某种方法,将各元素中的V去掉了,现在变成
[[1]]
[1] “1″ “2″ “3″ “4″ “5″ “6″ “7″ “8″

[[2]]
[1] “1″ “3″

[[3]]
[1] “3″ “5″ “6″

[[4]]
[1] “1″ “2″ “4″ “7″ “8″ “9″

[[5]]
[1] “3″ “7″

[[6]]
[1] “8″ “9″

至于三个指标,()内为实际数量级:
列表长度:6 (几十万)
列表总数据量:23(百万以上)
元素种类数:9(1万左右)

还请老兄给点大数据量或优化的解法。多谢了!
[未知用户]

元素种类在10000个, 长表时内存占用极少:
> tables()
     NAME       NROW NCOL MB COLS       KEY
[1,] dm_dt 2,199,189    2 17 where,what    
Total: 17MB
转换为宽表就是个超大的稀疏矩阵, 内存就装不下了...
Using 'what' as value column. Use 'value.var' to override
Aggregate function missing, defaulting to 'length'
Error: cannot allocate vector of size 14.9 Gb
所谓分块运算方法是在硬盘与内存中来回倒腾么? 话说长表这么小, 总感觉直接循环覆盖掉也不是不可以...
上班时间,我弄这个,可见我有多无聊。 :D

我来瞎掰掰。。。。。。。

元素数>>列表总数据量/列表长度
转完整宽表是不适合的,所以才问他这三个指标。
我想说的是,在空间和时间之间我们是有一定选择权的:

分块转宽表,是这样一种方法:
元素10000个,我计算时200个元素构建一张宽表,(形成50张小表)。此时,进行压缩,若数据是稀疏的,即(元素数>>列表总数据量/列表长度),每张小表,存在大量的全false行,删除这些false行数据。每张小宽表的数据量会急剧减小。
极端情况下。
若是每个元素一张表,10000张表,每张表一列,删除每张表false值,就是长表(数据量级与长表相同)。
若是所有元素一张表,1张表,表10000列数,就是宽表(数据量级与款表相同)

并不一定要极端的选择长表或宽表
这简单的分块方法是长表与宽表计算的折中方案,而且适用于并行计算。
单核同配置计算来说。
表数越多,时间越长,内存占用越低。
表数越少,时间越短,内存占用越高。

直接循环计算比较快。
直接循环计算unlist(dm)和表,成一张表比较好。
他提到unlist(dm)内存不足(无法转化为一张表)。可以将dm分成数张表(仅仅分表,并不是转化为宽表),分别计算各部分元素数求和,之后将各部分加在一起。


按照他给的数量级。使用3楼方法,我模拟了下长表数据量100M左右,不需要分表。
列表长度:百万 (几十万)
列表总数据量:千万(百万以上)
元素种类数:2万(1万左右)
以下是直接循环长表的方法
length <- sample(1:20, 1000000, replace=T)
dm <- vector("list", 1000000)
for (i in 1:1000000){
  dm[[i]] <- sample(1:20000, length[i], replace=T)
}
where <- rep(1:length(dm), times=length)
what <- unlist(dm)
where1<-where
what1<-what
while(length(what1)!=0){
  #找出最大的元素
  max<-which.max(table(what1))
  print(names(max))
  flag<-!where1%in%unique(where1[what1==names(max)[1]]) 
  where1<-where1[flag]
  what1<-what1[flag]
}

已经跑了快1个半小时了,还没跑完,20000个已经筛到300多个元素了,会越筛越快,瞎估计要算3~5小时左右:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22704 Rstudio 20 0 1734m 1.2g 11m R 99.8 31.7 80:57.84 rsession
[未知用户]

r server好评~~ 坐等明天早上实验结果.

关于分表的方法, 看了你的描述, 觉得基本上可行, 但是运行时间是不是优于直接循环不太好说, 原因是线程之间无法避免进行沟通, 每个线程不是独立计算下去的.

我理了一下多线程的逻辑:
1. 多线程: n*k张表分配到n个线程, 每张表200个元素的话, 就是求200*n*k个列的频次最高元素.
2. 单线程: 寻找频次最高元素相关的行标签, 顺手可以删了这张表的"频次最高元素列".
3. 多线程: n*k张表分配到n个线程, 每张表删除步骤2中找到的行标签.
4. 1-3循环.

有空我再来填坑用多线程算算看.

加班还来搅和这个东西可见我更加蛋疼 :cool:
回复 11 楼ethansun
[未知用户]

有位仁兄推荐了如下方法
m=names(which.max(table(unlist(dm))))
v=c(v,m)
dml=dml[-grep(m,dml)]

不知两位兄台意下如何?
[未知用户]
确实,楼主提供的方法,既简单又好用!用ethansun提供的模拟数据来进行模拟的话,时间为11秒左右
(windows8, 12GB, i5-4200M)
keep.ele <- vector()
system.time({
  while(length(dm)!=0){
    dm.cha <- unlist(dm)
    most.ele <- names(which.max(table(dm.cha)))[1]
    keep.ele <- c(keep.ele, most.ele)
    dm <- dm[-grep(most.ele, dm)]
  }
})
[未知用户]
有点错误,
上个程序的user time为26s

如果将
dm <- dm[-grep(most.ele, dm)]
换成
ele.idx <- lapply(dm, function(i) most.ele %in% i)
dm <- dm[!unlist(ele.idx)]

user time就会是18.63秒
:devil: