2016-09-01 14 views
11

私は、いくつかの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 ... 
+6

あなたの最初の質問とstackoverflowへようこそおめでとうございます。 – Tensibai

+0

私はあなたがここではあまりにも賢いようにしようとしていると思います。私は閉鎖を使用する理由は見当たりません。なぜこの「拡大リスト」が必要なのですか?どうやら、リストの大きさを知っているので、単にvector(mode = "list"、length = length(tables))を使って事前に割り当てる必要があります。 – Roland

+0

こんにちはローランド、私はあなたの提案に2つの異論があります。まず、問題を明確にするために、すべてのページ(gループ)からすべてのテーブル(iループ)のすべての(最終行)を収集し、すべてのページのテーブル数を知られていない。これは、iループ(コードを使用)で作成されたリストをc()(gループの最後)で解決できることがわかります。私がexpanded.list()を好む第2の異論は、 "c()way"(最初のインデックスはg-indexから継承され、2番目はi-indexから継承された) expanding.list()を避ける – dgdi

答えて

3

私はこの問題は、あなたが「read_html」を呼び出すことにより、マスターにXML/HTMLドキュメントオブジェクトを作成し、処理しているということだと思いますそれらを労働者に与える。私はいくつかの実験を試みましたが、それはうまくいかないように見えます。なぜなら、これらのオブジェクトは直列化できず、ワーカーに送られてから正しくデシリアライズできないからです。私はオブジェクトが壊れていると思うので、 "html_table"関数を使って操作しようとすると、作業者はセグメンテーションを起こすことになります。

作業者が "read_html"を呼び出すことができるようにファイル名を反復処理するようにコードを修正し、XMLドキュメントオブジェクトのシリアライズを避けることをお勧めします。私は、問題がforeachのかdoSNOWではなかったことを確認するために、直接、雪の機能を使用

library(xml2) 
library(snow) 
cl <- makeSOCKcluster(3) 
clusterEvalQ(cl, library(xml2)) 

# Create XML documents on the master 
docs <- lapply(1:10, 
     function(i) read_xml(paste0("<foo>", i, "</foo>"))) 

# Call xml_path on XML documents created on master 
r1 <- lapply(docs, xml_path)   # correct results 
r2 <- clusterApply(cl, docs, xml_path) # incorrect results 

# This seems to work... 
docs2 <- clusterApply(cl, 1:10, 
     function(i) read_xml(paste0("<foo>", i, "</foo>"))) 

# But this causes a segfault on the master 
print(docs2) 

:ここ


は私が実験したテストコードの一部です。

+0

こんにちはスティーブ、あなたの時間のために多くのおかげで、あなたの答えは本当に大きな進歩であり、私は今清掃を開始することができます。 (クラスタにエクスポートされたrvestとともに) 'clusterApply(cl、1:10、function(i)html_table(read_xml(paste0(" "、i" ")、fill = T))'を使用してリストのアクセス可能なリストdata.framesの返されます(素晴らしい!)。私もあなたのdoc2、おそらくprint xmlの問題でエラーが発生しましたか?しかし、私は間違っている場合、私はあなたの戦略では不可能である間、私は(doRedisの場合)に、他のマシンがジョブに参加することを許可するためにマスターで読むことによってページのコンテンツを "カプセル化"していた質問 – dgdi

+0

@dgdi私は、masterからworkerへのread_html関数の移動は、仕事のスケジュール方法を根本的に変えないと思っていましたが、おそらく私は何か不足しています。 –

関連する問題