sparklyr支持在Spark Cluster中通过大规模运行任意R代码spark_apply()。
spark_apply()将R函数应用于Spark对象(通常是Spark DataFrame)。Spark对象是分区的,因此可以跨群集分布。你可以使用spark_apply默认分区,也可以使用group_by参数定义自己的分区。你的R函数必须返回另一个Spark DataFrame。spark_apply将在每个分区上运行您的R函数并输出单个Spark DataFrame
将R函数应用于SPARK对象
让我们举一个简单的例子。我们将使用函数I()创建的数字列表应用识别sdf_len函数。
library(sparklyr)
sc <- spark_connect(master = "local")
sdf_len(sc, 5, repartition = 1) %>%
spark_apply(function(e) I(e))
# Source: spark [?? x 1]
id
*
1 1
2 2
3 3
4 4
5 5
你的R功能应设计为在R 数据帧上运行。传递的R函数spark_apply需要一个DataFrame,并返回一个可以转换为DataFrame的对象。我们可以使用该class函数来验证数据类。
sdf_len(sc, 10, repartition = 1) %>%
spark_apply(function(e) class(e))
# Source: spark [?? x 1]
result
*
1 data.frame
Spark将按散列或范围对数据进行分区,以便可以在群集中分布。在下面的示例中,我们创建了两个分区并计算每个分区中的行数。然后我们打印每个分区中的第一条记录。
trees_tbl <- sdf_copy_to(sc, trees, repartition = 2)
> trees_tbl %>%
+ spark_apply(function(e) nrow(e), names = "n")
# Source: spark [?? x 1]
n
*
1 15
2 16
>
>
> trees_tbl %>%
+ spark_apply(function(e) head(e, 1))
# Source: spark [?? x 3]
Girth Height Volume
*
1 8.30 70.0 10.3
2 12.9 74.0 22.2
我们可以将任意函数应用于Spark DataFrame中的分区。例如,我们可以缩放或抖动列。请注意,spark_apply将R函数应用于所有分区并返回单个DataFrame。
> trees_tbl %>%
+ spark_apply(function(e) scale(e))
# Source: spark [?? x 3]
Girth Height Volume
*
1 -2.05 -0.607 -1.69
2 -1.79 -1.43 -1.69
3 -1.62 -1.76 -1.71
4 -0.134 -0.276 -0.339
5 0.0407 1.21 0.191
6 0.128 1.54 0.390
7 0.302 -1.27 -0.515
8 0.302 0.221 0.0589
9 0.389 1.05 1.03
10 0.477 0.221 0.434
# ... with more rows
本质上就是传入一个DataFrame 进入spark_apply
> trees_tbl %>%
+ spark_apply(function(e) lapply(e, jitter))
# Source: spark [?? x 3]
Girth Height Volume
*
1 8.30 70.1 10.3
2 8.58 64.9 10.3
3 8.79 63.2 10.2
4 10.5 72.1 16.4
5 10.7 80.8 18.8
6 10.8 83.0 19.7
7 11.0 66.1 15.6
8 11.0 74.9 18.2
9 11.1 80.0 22.6
10 11.2 75.1 19.9
# ... with more rows
默认情况下spark_apply(),从输入Spark数据框中派生列名。使用names参数重命名或添加新列。
> trees_tbl %>%
+ spark_apply(
+ function(e) data.frame(2.54 * e$Girth, e),
+ names = c("Girth(cm)", colnames(trees)))
# Source: spark [?? x 4]
`Girth(cm)` Girth Height Volume
*
1 21.1 8.30 70.0 10.3
2 21.8 8.60 65.0 10.3
3 22.4 8.80 63.0 10.2
4 26.7 10.5 72.0 16.4
5 27.2 10.7 81.0 18.8
6 27.4 10.8 83.0 19.7
7 27.9 11.0 66.0 15.6
8 27.9 11.0 75.0 18.2
9 28.2 11.1 80.0 22.6
10 28.4 11.2 75.0 19.9
# ... with more rows
group by
在某些情况下,你可能希望将R函数应用于数据中的特定组。例如,假设您要针对特定子组计算回归模型。要解决此问题,您可以指定group_by参数。此示例计算iris按物种划分的行数,然后为每个物种拟合一个简单的线性模型。
iris_tbl <- sdf_copy_to(sc, iris)
> iris_tbl %>%
+ spark_apply(nrow, group_by = "Species")
# Source: spark [?? x 2]
Species result
*
1 versicolor 50
2 virginica 50
3 setosa 50
>
>
>
> iris_tbl %>%
+ spark_apply(
+ function(e) summary(lm(Petal_Length ~ Petal_Width, e))$r.squared,
+ names = "r.squared",
+ group_by = "Species")
# Source: spark [?? x 2]
Species r.squared
*
1 versicolor 0.619
2 virginica 0.104
3 setosa 0.110
调用R包
有了spark_apply()你可以在spark中使用任何R包。例如,你可以使用broom包从线性回归输出创建整洁的数据框。
> spark_apply(
+ iris_tbl,
+ function(e) broom::tidy(lm(Petal_Length ~ Petal_Width, e)),
+ names = c("term", "estimate", "std.error", "statistic", "p.value"),
+ group_by = "Species")
# Source: spark [?? x 6]
Species term estimate std.error statistic p.value
*
1 versicolor (Intercept) 1.78 0.284 6.28 9.48e⁻ ⁸
2 versicolor Petal_Width 1.87 0.212 8.83 1.27e⁻¹¹
3 virginica (Intercept) 4.24 0.561 7.56 1.04e⁻ ⁹
4 virginica Petal_Width 0.647 0.275 2.36 2.25e⁻ ²
5 setosa (Intercept) 1.33 0.0600 22.1 7.68e⁻ ²⁷
6 setosa Petal_Width 0.546 0.224 2.44 1.86e⁻ ²
要在Spark中使用R包,必须在工作节点上安装包。第一次调用spark_apply本地中的所有内容时,.libPaths()将通过该SparkConf.addFile()函数将其复制到每个Spark工作节点。软件包只会被复制一次,只要连接保持打开状态,软件包就会一直存在。R库的大小为几千兆字节并不罕见,因此在将R软件包复制到Spark集群时,请准备一次性税。您可以通过设置禁用包分发packages = FALSE。注意:程序包不会以本地模式(master="local")复制,因为程序包已存在于系统
异常处理
对群集中的R问题进行故障排除比在本地模式下更难。例如,以下R代码导致分布式执行失败,建议您检查日志以获取详细信息。
spark_apply(iris_tbl, function(e) stop("Make this fail"))
在本地模式下,sparklyr将为你检索日志。日志指出了ERROR sparklyr: RScript (4190) Make this fail您可能期望的真正故障。