Apache spark 在Spark数据集中创建包含运行总计的列
假设我们有一个Spark数据集,它有两列,比如索引和值,按第一列(索引)排序 我们希望有一个第三列的数据集,第二列(Value)中的值的总和是连续的 有什么建议可以让我们一次通过数据就有效地做到这一点吗?或者是否有任何罐装CDF类型的功能可以用于此 如果需要,数据集可以转换为数据帧或RDD来完成任务,但它必须保持分布式数据结构。也就是说,它不能简单地收集并转换为数组或序列,并且不能使用可变变量(Apache spark 在Spark数据集中创建包含运行总计的列,apache-spark,spark-dataframe,rdd,apache-spark-dataset,Apache Spark,Spark Dataframe,Rdd,Apache Spark Dataset,假设我们有一个Spark数据集,它有两列,比如索引和值,按第一列(索引)排序 我们希望有一个第三列的数据集,第二列(Value)中的值的总和是连续的 有什么建议可以让我们一次通过数据就有效地做到这一点吗?或者是否有任何罐装CDF类型的功能可以用于此 如果需要,数据集可以转换为数据帧或RDD来完成任务,但它必须保持分布式数据结构。也就是说,它不能简单地收集并转换为数组或序列,并且不能使用可变变量(val,仅限var) 但它必须保持分布式数据结构 不幸的是,你所说的你想做的事在Spark中是不可能的
val
,仅限var
)
但它必须保持分布式数据结构
不幸的是,你所说的你想做的事在Spark中是不可能的。如果您愿意将数据集重新分区到单个分区(实际上是将其整合到单个主机上),则可以轻松地编写一个函数来执行所需操作,并将递增的值保留为字段
由于Spark函数在执行时不在网络上共享状态,因此无法创建共享状态,从而使数据集完全分布
如果您愿意放宽要求,允许数据在一台主机上通过一次传递进行整合和读取,那么您可以通过将数据重新分区到单个分区并应用函数来实现。这不会将数据拉入驱动程序(将其保存在HDFS/集群中),但仍会在单个执行器上串行计算输出。例如:
package com.github.nevernaptitsa
import java.io.Serializable
import java.util
import org.apache.spark.sql.{Encoders, SparkSession}
object SparkTest {
class RunningSum extends Function[Int, Tuple2[Int, Int]] with Serializable {
private var runningSum = 0
override def apply(v1: Int): Tuple2[Int, Int] = {
runningSum+=v1
return (v1, runningSum)
}
}
def main(args: Array[String]): Unit ={
val session = SparkSession.builder()
.appName("runningSumTest")
.master("local[*]")
.getOrCreate()
import session.implicits._
session.createDataset(Seq(1,2,3,4,5))
.repartition(1)
.map(new RunningSum)
.show(5)
session.createDataset(Seq(1,2,3,4,5))
.map(new RunningSum)
.show(5)
}
}
这里的两个语句显示不同的输出,第一个语句提供正确的输出(串行,因为调用了重新分区(1)
),第二个语句提供错误的输出,因为结果是并行计算的
第一次陈述的结果:
+---+---+
| _1| _2|
+---+---+
| 1| 1|
| 2| 3|
| 3| 6|
| 4| 10|
| 5| 15|
+---+---+
+---+---+
| _1| _2|
+---+---+
| 1| 1|
| 2| 2|
| 3| 3|
| 4| 4|
| 5| 9|
+---+---+
第二项声明的结果:
+---+---+
| _1| _2|
+---+---+
| 1| 1|
| 2| 3|
| 3| 6|
| 4| 10|
| 5| 15|
+---+---+
+---+---+
| _1| _2|
+---+---+
| 1| 1|
| 2| 2|
| 3| 3|
| 4| 4|
| 5| 9|
+---+---+
一位同事根据该方法提出了以下建议。 (据我所知,其他数据结构不提供对其分区索引的此类引用。)
谢谢你的详细答复,@Ed。我也这么怀疑。也许我应该为这个用例研究一种近似方法。没问题@BahmanEngheta!如果你对我的回答满意,你介意把它标为接受吗?
val data = sc.parallelize((1 to 5)) // sc is the SparkContext
val partialSums = data.mapPartitionsWithIndex{ (i, values) =>
Iterator((i, values.sum))
}.collect().toMap // will in general have size other than data.count
val cumSums = data.mapPartitionsWithIndex{ (i, values) =>
val prevSums = (0 until i).map(partialSums).sum
values.scanLeft(prevSums)(_+_).drop(1)
}