"foreach"并行循环返回<NA> s

6 foreach r dataframe

我试图并行处理几个列表项.

我的目标是:根据每个列的值运行一些标记函数.然后返回包含节点名称,列名称和已处理标签的数据框

使用普通for循环,工作流程正常.但是,当我尝试在foreach循环中执行相同的操作时,返回的结果是(请注意:以下只是原始数据集的抽象)

我不确定到底是什么搞砸了..如果你可以帮我把那件事搞定那会很棒:-)

set.seed(12345)
options(stringsAsFactors = F)


# I. Random data generation (Original data is in data frame format)
random.data = list()
random.data[["one"]] = as.data.frame(matrix(data = runif(n = 15), ncol = 3))
random.data[["two"]] = as.data.frame(matrix(data = runif(n = 15), ncol = 3))
random.data[["three"]] = as.data.frame(matrix(data = runif(n = 15), ncol = 3))



# II. Some function applied to each column to label/classify the values
valslabel = function(DataCOlumn) {
  if(mean(DataCOlumn) < 0.5) return("low")
  return("high")
}



# III. Generating the desired output in a regular for loop : 

desiredOutput = list()

for(frame.i in seq_along(random.data)) {

  frame = random.data[[frame.i]]
  frame.name = names(random.data)[frame.i]
  frame.results = data.frame(frame.name = character(0), 
                  mappedField = character(0), label = character(0) )

  for(col.i in 1:ncol(frame)) {
    frame.results[col.i, "frame.name"] = frame.name
    frame.results[col.i, "mappedField"] = colnames(frame)[col.i]
    frame.results[col.i, "label"] = valslabel(frame[,col.i])  
  }

  desiredOutput[[frame.name]] = frame.results
}


print(desiredOutput)

# $one
# frame.name mappedField label
# 1        one          V1  high
# 2        one          V2  high
# 3        one          V3   low
# 
# $two
# frame.name mappedField label
# 1        two          V1   low
# 2        two          V2  high
# 3        two          V3   low
# 
# $three
# frame.name mappedField label
# 1      three          V1   low
# 2      three          V2  high
# 3      three          V3  high




# IV. Using the "foreach" parallel execution

library(foreach)
library(doParallel)

cl = makeCluster(6)
registerDoParallel(cl)

output = foreach(frame.i = seq_along(random.data), .verbose = T) %dopar% {

  frame = random.data[[frame.i]]
  frame.name = names(random.data)[frame.i]
  frame.results = data.frame(frame.name = character(0), mappedField = character(0), label = character(0) )

  for(col.i in 1:ncol(frame)) {
    frame.results[col.i, "frame.name"] = frame.name
    frame.results[col.i, "mappedField"] = colnames(frame)[col.i]
    frame.results[col.i, "label"] = valslabel(frame[,col.i])  
  }

  return(frame.results)
}


print(output)

# [[1]]
# frame.name mappedField label
# 1       <NA>        <NA>  <NA>
# 2       <NA>        <NA>  <NA>
# 3       <NA>        <NA>  <NA>
#   
# [[2]]
# frame.name mappedField label
# 1       <NA>        <NA>  <NA>
# 2       <NA>        <NA>  <NA>
# 3       <NA>        <NA>  <NA>
#   
# [[3]]
# frame.name mappedField label
# 1       <NA>        <NA>  <NA>
# 2       <NA>        <NA>  <NA>
# 3       <NA>        <NA>  <NA>
Run Code Online (Sandbox Code Playgroud)

谢谢!

kon*_*vas 3

该问题与您初始化数据框的方式有关,并且在foreach环境中该选项stringsAsFactors未设置为FALSE。每个循环中发生的事情foreach是这样的

options(stringsAsFactors = FALSE)
d <- data.frame(x =character(0))
d[1, "x"] <- "a"
#Warning message:
#In `[<-.factor`(`*tmp*`, iseq, value = "a") :
#  invalid factor level, NA generated
d
#     x
#1 <NA>
Run Code Online (Sandbox Code Playgroud)

请注意,这只会给出警告,而不是错误,因此循环不会停止。如果您设置stringsAsFactorsFALSE第一个,则没有问题(就像您在不并行运行东西时所做的那样)

options(stringsAsFactors = FALSE)
d <- data.frame(x =character(0))
d[1, "x"] <- "a"
d
#  x
#1 a
Run Code Online (Sandbox Code Playgroud)

在您的全局环境中,您已经设置了options(stringsAsFactors = FALSE)循环%do%工作。但是,此选项不会在每个并行作业的本地环境中传递,因此%dopar%循环会遇到上述问题。

例如,查看以下输出

options(stringsAsFactors = FALSE)
.Options$stringsAsFactors
#[1] FALSE
foreach(i = 1:3) %dopar% .Options$stringsAsFactors
#[[1]]
#[1] TRUE
#
#[[2]]
#[1] TRUE
#
#[[3]]
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)

stringsAsFactors = FALSE所以解决方案是在循环内设置选项foreach

顺便说一句,如果可能的话,使用整个列向量而不是逐行创建数据框要好得多。在你的例子中你可以替换

frame.results = data.frame(frame.name = character(0), mappedField = character(0), label = character(0))
for(col.i in 1:ncol(frame)) {
    frame.results[col.i, "frame.name"] = frame.name
    frame.results[col.i, "mappedField"] = colnames(frame)[col.i]
    frame.results[col.i, "label"] = valslabel(frame[,col.i])  
}
Run Code Online (Sandbox Code Playgroud)

frame.results <- data.frame( 
    frame.name = frame.name, 
    mappedField = colnames(frame), 
    label = valslabel1(colMeans(frame)))
Run Code Online (Sandbox Code Playgroud)

其中该valslabel函数已被矢量化版本替换

valslabel1 <- function(x) {
    ifelse(x < 0.5, "low", "high")
}
Run Code Online (Sandbox Code Playgroud)

  • @Deena @cryo111顺便说一句,我注意到,如果您使用参数“cores”注册并行后端,即“registerDoParallel(cores = 6)”,则选项_is_传递到每个作业的本地环境...不确定这是为什么以及是否特定于平台 (2认同)
  • @konvas 这很有趣。我在我的 Linux Mint 17.1 机器上检查了它,就像你说的那样,“registerDoParallel(cores = 6)”使所有进程的“stringsAsFactors”选项设置为“FALSE”。 (2认同)
  • @konvas刚刚检查了“registerDoParallel”源代码。非 Windows 操作系统的差异似乎在于,使用 `missing(cl)==TRUE` 时,会调用 `setDoPar(doParallelMC, cores, mcinfo)`,而使用 `missing(cl)==FALSE` 时,会调用 `setDoPar( doParallelSNOW, cl, Snowinfo)` 被执行。所以看起来好像 `doParallelMC` 导出了选项,而 `doParallelSNOW` 则没有。 (2认同)