私は、いくつかのhtml文書に保存されたデータの抽出を並列化し、data.framesに格納しようとしています(何百万もの文書、したがって並列化の有用性)。foreachでリストを使うR
最初の手順では、キューを登録するマシンで、htmlファイルのサブセットを選択し、read_html関数を適用します(rvestパッケージから、XMLから同様の機能を試しました)パッケージが、私はメモリリークの問題が発生していた)多くのHTMLページの内容を格納する一意のリストを取得します。
次に、このリストのイテレータを使用して、foreachにフィードされる小さなチャンクを取得します。
foreach内では、data.frame(html_table関数と基本的なデータ操作を使用して)を構築し、要素が整理されたdata.framesであるリストを返します。
ubuntu 16.04でdoSNOWバックエンドとdoRedisの両方を使用しようとしました。
最初の場合は空のリストが返され、2番目の場合はメモリマッピングのエラーがスローされます。質問の一番下にトレースバックがあります。
私が理解しているように、私がコアに送っているリストは、私が期待したように行動していないということです。私は、リストオブジェクトはポインタのセットかもしれないが、私はそれを確認することができませんでした周りに集まっている。おそらく、これは問題になる可能性がありますか? 複数のhtmlページのデータを「カプセル化」するための「リスト方法」の代替手段はありますか?
以下に、問題を再現したコードがあります。 私は完全に新しいスタックオーバーフロー、並列プログラミングの新機能、Rプログラミングの新しいものです。改善のアドバイスは大歓迎です。 ありがとうございます。 Redisのバックエンドを使用した場合にスローさ
library(rvest)
library(foreach)
#wikipedia pages of olympic medalist between 1992 and 2016 are
# downloaded for reproducibility
for(i in seq(1992, 2016, by=4)){
html = paste("https://en.wikipedia.org/wiki/List_of_", i, "_Summer_Olympics_medal_winners", sep="")
con = url(html)
htmlCode = readLines(con)
writeLines(htmlCode, con=paste(i, "medalists", sep="_"))
close(con)
}
#declaring the redis backend (doSNOW code is also included below)
#note that I am using the package from
#devtools::install_github("bwlewis/doRedis") due to a "nodelay error"
#(more info on that here: https://github.com/bwlewis/doRedis/issues/24)
# if it is not your case please drop the nodelay and timeout options
#Registering cores ---Ubuntu---
cores=2
library('doRedis')
options('redis:num'=TRUE)
registerDoRedis("jobs", nodelay=FALSE)
startLocalWorkers(n=cores, "jobs", timeout=2, nodelay=FALSE)
foreachOpt <- list(preschedule=FALSE)
#Registering cores ---Win---
#cores=2
#library("doSNOW")
#registerDoSNOW(makeCluster(cores, type = "SOCK"))
#defining the iterator
iterator <- function(x, ...) {
i <- 1
it <- idiv(length(x), ...)
if(exists("chunks")){
nextEl <- function() {
n <- nextElem(it)
ix <- seq(i, length=n)
i <<- i + n
x[ix]
}
}else{
nextEl <- function() {
n <- nextElem(it)
ix <- seq(i, i+n-1)
i <<- i + n
x[ix]
}
}
obj <- list(nextElem=nextEl)
class(obj) <- c(
'ivector', 'abstractiter','iter')
obj
}
#reading files
names_files<-list.files()
html_list<-lapply(names_files, read_html)
#creating iterator
ChunkSize_html_list<-2
iter<-iterator(html_list, chunkSize=ChunkSize_html_list)
#defining expanding list (thanks StackOverflow and many thanks to
#JanKanis's answer : http://stackoverflow.com/questions/2436688/append-an-object-to-a-list-in-r-in-amortized-constant-time-o1 )
expanding_list <- function(capacity = 10) {
buffer <- vector('list', capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
capacity <<- capacity * 2
}
methods$add <- function(val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
}
methods$as.list <- function() {
b <- buffer[0:length]
return(b)
}
methods
}
#parallelized part
clean_data<-foreach(ite=iter, .packages=c("itertools", "rvest"), .combine=c,
.options.multicore=foreachOpt, .options.redis=list(chunkSize=1)) %dopar% {
temp_tot <- expanding_list()
for(g in 1:length(ite)){
#extraction of data from tables
tables <- html_table(ite[[g]], fill=T, header = T)
for(i in 1:length(tables)){
#just some basic data manipulation
temp<-lapply(tables, function(d){d[nrow(d),]})
temp_tot$add(temp)
rm(temp)
gc(verbose = F)
}
}
#returning the list of cleaned data.frames to the foreach
temp_tot$as.list()
}
エラー:
*** caught segfault ***
address 0x60, cause 'memory not mapped'
Traceback:
1: .Call("xml2_doc_namespaces", PACKAGE = "xml2", doc)
2: doc_namespaces(doc)
3: xml_ns.xml_document(x)
4: xml_ns(x)
5: xpath_search(x$node, x$doc, xpath = xpath, nsMap = ns, num_results = Inf)
6: xml_find_all.xml_node(x, ".//table")
7: xml2::xml_find_all(x, ".//table")
8: html_table.xml_document(ite[[g]], fill = T, header = T)
9: html_table(ite[[g]], fill = T, header = T)
10: eval(expr, envir, enclos)
11: eval(.doRedisGlobals$expr, envir = .doRedisGlobals$exportenv)
12: doTryCatch(return(expr), name, parentenv, handler)
13: tryCatchOne(expr, names, parentenv, handlers[[1L]])
14: tryCatchList(expr, classes, parentenv, handlers)
15: tryCatch({ lapply(names(args), function(n) assign(n, args[[n]], pos = .doRedisGlobals$exportenv)) if (exists(".Random.seed", envir = .doRedisGlobals$exportenv)) { assign(".Random.seed", .doRedisGlobals$exportenv$.Random.seed, envir = globalenv()) } tryCatch({ if (exists("set.seed.worker", envir = .doRedisGlobals$exportenv)) do.call("set.seed.worker", list(0), envir = .doRedisGlobals$exportenv) }, error = function(e) cat(as.character(e), "\n")) eval(.doRedisGlobals$expr, envir = .doRedisGlobals$exportenv)}, error = function(e) e)
16: FUN(X[[i]], ...)
17: lapply(work[[1]]$argsList, .evalWrapper)
18: redisWorker(queue = "jobs", host = "localhost", port = 6379, iter = Inf, linger = 30, log = stdout(), timeout = 2, nodelay = FALSE)
aborting ...
あなたの最初の質問とstackoverflowへようこそおめでとうございます。 – Tensibai
私はあなたがここではあまりにも賢いようにしようとしていると思います。私は閉鎖を使用する理由は見当たりません。なぜこの「拡大リスト」が必要なのですか?どうやら、リストの大きさを知っているので、単にvector(mode = "list"、length = length(tables))を使って事前に割り当てる必要があります。 – Roland
こんにちはローランド、私はあなたの提案に2つの異論があります。まず、問題を明確にするために、すべてのページ(gループ)からすべてのテーブル(iループ)のすべての(最終行)を収集し、すべてのページのテーブル数を知られていない。これは、iループ(コードを使用)で作成されたリストをc()(gループの最後)で解決できることがわかります。私がexpanded.list()を好む第2の異論は、 "c()way"(最初のインデックスはg-indexから継承され、2番目はi-indexから継承された) expanding.list()を避ける – dgdi