stack overflow原文地址
弱雞小白在使用SparkR處理大規模的R dataframe時想使用map的方式進行數據操作。數據都是結構化的,并且每個分區都是相同的結構。本想的將這些數據作為parquet這樣就可以避免collect的Action操作。現在很擔心能不能再程序輸出的output list后進行write.df的操作,能否使用worker tasks編寫替代指定parquet進行操作?
小白的程序如下:
#! /usr/bin/Rscript
library(SparkR, lib.loc="/opt/spark-1.5.1-bin-without-hadoop/R/lib")
source("jdbc-utils.R")
options(stringsAsFactors = FALSE)
# I dislike having these here but when I move them into main(), it breaks - the sqlContext drops.
assign("sc", sparkR.init(master = "spark://poc-master-1:7077", sparkHome = "/opt/spark-1.5.1-bin-without-hadoop/", appName = "Peter Spark test", list(spark.executor.memory="4G")), envir = .GlobalEnv)
assign("sqlContext", sparkRSQL.init(sc), envir =.GlobalEnv)
#### MAP function ####
run.model <- function(v) {
x <- v$xs[1]
y <- v$ys[1]
startTime <- format(Sys.time(), "%F %T")
xs <- c(1:x)
endTime <- format(Sys.time(), "%F %T")
hostname <- system("hostname", intern = TRUE)
xys <- data.frame(xs,y,startTime,endTime,hostname,stringsAsFactors = FALSE)
return(xys)
}
# HERE BE THE SCRIPT BIT
main <- function() {
# Make unique identifiers for each run
xs <- c(1:365)
ys <- c(1:1)
xys <- data.frame(xs,ys,stringsAsFactors = FALSE)
# Convert to Spark dataframe for mapping
sqlContext <- get("sqlContext", envir = .GlobalEnv)
xys.sdf <- createDataFrame(sqlContext, xys)
# Let Spark do what Spark does
output.list <- SparkR:::map(xys.sdf, run.model)
# Reduce gives us a single R dataframe, which may not be what we want.
output.redux <- SparkR:::reduce(output.list, rbind)
# Or you can have it as a list of data frames. output.col <- collect(output.list)
return(NULL)
}
小白心里是這樣想的,先生成一個名字叫xys的dataframe,兩列數據,一列是1:365,另一列是1。通過createDataFrame將其轉換成為RDD,然后進行map和reduce的操作。同時編寫了一個demo小函數,用來進行map。
程序結果.png
小白同學的心中是充滿疑惑的:
- 并沒有想象中的需要避免絕對的collect使用,而去將結果組合作為Parquet進行存儲;
- 同時,也并不確信
:::map
的函數形式真正實現了并行,難道需要一直申明parallelise
對于小白的疑惑,大腿同學是這樣解釋的:
假設你的數據差不多是下面這個樣子的:
rdd <- SparkR:::parallelize(sc, 1:5)
dfs <- SparkR:::map(rdd, function(x) mtcars[(x * 5):((x + 1) * 5), ])
首先給你瞅一眼mtcars的數據:
mtcars.png
瞅一眼程序結果:
程序結果.png
同時大腿也給出了自己的思路:
因為要對所有數據的列進行操作,完全可以把它轉換成為row-wise的逐行操作類型;
rows <- SparkR:::flatMap(dfs, function(x) {
data <- as.list(x)
args <- list(FUN = list, SIMPLIFY = FALSE, USE.NAMES = FALSE)
do.call(mapply, append(args, data))})
sdf <- createDataFrame(sqlContext, rows)
head(sdf)
結果.png
大腿這里用了append秒rbind一萬條街;用flatmap實現了map實現的捉襟見肘的多分區集合,小白深感佩服。
看到小白一臉蔥白的樣子,大神接著說:
接下來就可以使用簡單的write.df
/
saveDF
了
小白啊,你的問題主要是一開始使用了一個內部方法map,他被從最初版本移除的一個重要原因是如果直接使用是不健全的,而且也不清楚將來會不會被支持,誰知道呢。
于是小白關注了大腿同學。