我最近一直在处理Spark数据集,在这种情况下,我必须为每一行生成行号并将其存储在名为“ Ids”的列中。此行号从1开始,2、3 ...,并根据数据集中的行数递增。 (在我的情况下,有10000-20000条记录)
考虑一下,我有一个具有值的数据集'empDataset':
name , dept , project
---------------------
Tina, Finance , abc
Leena, Finance , abc
Joe, Marketing , xyz
现在对于上述数据集,我想添加一个列“ Ids”,其值从1,2,3 ..等递增。
预期输出是
name , dept , project ,Ids
--------------------------
Tina, Finance , abc , 1
Leena, Finance , abc , 2
Joe, Marketing , xyz , 3
我还想将此输出存储在另一个数据集中,并进一步用于不同的转换。
需要帮助来解决这个问题。!
我的代码段:
LongAccumulator accValue = spark.sparkContext().longAccumulator();
long rowNumber = 1;
spark.udf().register("randomNumberGenerator", new UDF1<String, Long>() {
@Override
public Long call(String namCol) throws Exception {
accum.add(rowNumber);
System.out.println("inside" + accum.value());
return accum.value();
}
}, DataTypes.LongType);
Dataset<Row> empDatasetWithIds= empDataset.withColumn("Ids",callUDF("randomNumberGenerator",
col(name)));
Dataset<Row> filterDept = empDatasetWithIds.filter(...here filtering with dept...)
我得到的输出是empDatasetWithIds(输出不正确):
name , dept , project ,Ids
--------------------------
Tina, Finance , abc , 1
Leena, Finance , abc , 2
Joe, Marketing , xyz , 1
在本地模式下运行时,上面的代码可以正常工作,但在群集模式下,这些值不会增加。
我还浏览了以下链接: https://community.hortonworks.com/questions/36888/spark-java-accumulator-not-incrementing.html Spark Java Accumulator not incrementing
火花累积器需要采取行动来触发作业。在我的场景中,我将进一步对数据集执行过滤器转换,如何解决此问题。需要帮助。
答案 0 :(得分:1)
累加器是用于跨执行器累积数据并将其发送回驱动程序的变量。如果您从执行程序中读取其值,则该行为未定义(AFAIK)。我想您可能会得到到目前为止本地分区已累积的内容。实际上,spark的目标是进行并行计算。因此,当使用累加器时,每个分区的数据都在单独的累加器中累加,然后合并并发送回驱动程序(map reduce范式)。因此,您不能使用累加器在执行程序之间共享信息。那不是什么意思
但是,您可以做的是,如果需要连续的索引,则可以使用RDD API中的zipWithIndex
,如果需要连续的索引,则可以使用SparkSQL API中的monoticallyIncreasingId
。前者触发一个小的火花作业,而后者几乎是免费的(无火花作业)。
选项1(增加但不一定是连续的索引)
yourDataframe.withColumn("id", functions.monotonicallyIncreasingId());
选项2(连续和递增的索引)
StructType schema = yourDataframe.schema();
schema.add(new StructField("id", DataTypes.LongType, false,null));
JavaRDD<Row> rdd = yourDataframe.toJavaRDD().zipWithIndex()
.map(x -> {
Collection<Object> row = JavaConverters.asJavaCollection(x._1.toSeq());
Long index = x._2;
row.add(index);
return RowFactory.create(row);
});
Dataset<Row> indexedData = spark.createDataFrame(rdd, schema);
答案 1 :(得分:0)
如果顺序升序不是问题,则可以按照以下说明简单地进行操作:
import org.apache.spark.sql.functions.monotonically_increasing_id
import spark.implicits._
val ds = sc.parallelize(Seq(
("a", -1.0), ("b", -2.0), ("c", -3.0)),5).toDS // Just a dummy DS
val newds = ds.withColumn("uniqueIdColumn", monotonically_increasing_id())
newds.show(false)
尝试一下,以适应自己的情况。
顺便说一句:错误使用蓄电池。
答案 2 :(得分:-1)
为此功能,您可以使用row_number
import org.apache.spark.sql.expressions.Window
import static org.apache.spark.sql.functions.col;
import static org.apache.spark.sql.functions.row_number;
Dataset<Row> empDatasetWithIds = empDataset.withColumn("Ids",
row_number().over(Window.orderBy(col("name"), col("dept"), col("project)))
)
参考: https://stackoverflow.com/a/31077759
如注释中所指出,使用不带分区的Window效率很低。并且应避免在生产代码中处理大数据。
您对累加器的处理不起作用(如Why does worker node not see updates to accumulator on another worker nodes?中所述),因为spark在不同的执行程序(在不同计算机上运行的不同jvm进程)中运行此代码,并且每个累加器都有自己的副本。