Apache spark Spark-如何使用数据帧Scala更新值
我有两个具有以下结构的文件 文件1 文件2 如果file1.gnk_id=file2.gnk_machid,我想用文件2中的matchid值更新文件1中的gnk_id值 为此,我在Spark中创建了两个数据帧。我想知道我们是否可以更新Spark中的值?如果没有,是否有任何解决方法可以提供更新的最终文件 更新 我做了这样的事 案例类GnkMatchIdgnk:String,gnk_matchid:String 案例类MatchGroupgnkid:String,matchid:String,ts:String val gnkmatchidRDD=sc.textFile000000000001.map_u2;.split','.mapx=>x0,x1 val gnkmatchidDF=gnkmatchidd.map x=>GnkMatchIdx.\u 1,x.\u 2.toDF val matchGroupMr=sc.textFilepart-00000.map_uu.split','.mapx=>x0,x1,x2.map f=>MatchGroupf._1,f._2,f._3.toString.toDF val matchgrp_joinDF=matchGroupMr.joingnkmatchidDF,matchGroupMrgnkid==gnkmatchidDFgnk_matchid,左 matchgrp_joinDF.mapx=>ifx.getAs[String]gnk_matchid.length!=0{MatchGroupx.getAs[String]gnk_matchid,x.getAs[String]matchid,x.getAs[String]ts}其他{MatchGroupx.getAs[String]gnkid,x.getAs[String]matchid,x.getAs[String]ts}.toDF.showApache spark Spark-如何使用数据帧Scala更新值,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我有两个具有以下结构的文件 文件1 文件2 如果file1.gnk_id=file2.gnk_machid,我想用文件2中的matchid值更新文件1中的gnk_id值 为此,我在Spark中创建了两个数据帧。我想知道我们是否可以更新Spark中的值?如果没有,是否有任何解决方法可以提供更新的最终文件 更新 我做了这样的事 案例类GnkMatchIdgnk:String,gnk_matchid:String 案例类MatchGroupgnkid:String,matchid:String,ts:
但在最后一步,它的NULLpointerEXception失败了,这取决于您使用的数据源是否支持它 和你在一起。 使用该文件,您只需过滤掉行,进行更改并将其添加回。 希望能有所帮助 2018/04/11
更新链接。这取决于您使用的数据源是否支持该链接 和你在一起。 使用该文件,您只需过滤掉行,进行更改并将其添加回。 希望能有所帮助 2018/04/11
更新链接。这可能是您正在寻找的加入。假设在文件1和文件2中有数据帧,可以尝试以下操作: val结果=文件1 .joinfile2,file1matchId==file2matchid 选择 colgnk_matchid.asgnk_id, 科尔马奇德, 时间戳
这可能是你正在寻找的一个加入。假设在文件1和文件2中有数据帧,可以尝试以下操作: val结果=文件1 .joinfile2,file1matchId==file2matchid 选择 colgnk_matchid.asgnk_id, 科尔马奇德, 时间戳 DataFrame基于RDD,所以不能更新其中的值 但您可以使用column通过添加新列来更新值 在您的情况下,您可以通过使用UDF执行join和withColumn操作:
// df1: your File1
// +------+-------+---+
// |gnk_id|matchId| ts|
// +------+-------+---+
// | 1| 10|100|
// | 2| 20|200|
// +------+-------+---+
// df2: your File2
// +-----------+-------+
// |gnk_matchid|matchid|
// +-----------+-------+
// | 1| 1000|
// | 3| 3000|
// +-----------+-------+
// UDF: choose values from matchid or gnk_id for the new column
val myUDF = udf[Integer,Integer,Integer]((df2_matchid: Integer, df1_gnk_id: Integer) => {
if (df2_matchid == null) df1_gnk_id
else df2_matchid
})
df1.join(df2, $"gnk_id"===$"gnk_matchid", "left_outer")
.select($"df1.*", $"df2.matchid" as "matchid2")
.withColumn("gnk_id", myUDF($"matchid2", $"gnk_id"))
.drop($"matchid2")
.show()
以下是输出:
DataFrame基于RDD,所以不能更新其中的值
但您可以使用column通过添加新列来更新值
在您的情况下,您可以通过使用UDF执行join和withColumn操作:
// df1: your File1
// +------+-------+---+
// |gnk_id|matchId| ts|
// +------+-------+---+
// | 1| 10|100|
// | 2| 20|200|
// +------+-------+---+
// df2: your File2
// +-----------+-------+
// |gnk_matchid|matchid|
// +-----------+-------+
// | 1| 1000|
// | 3| 3000|
// +-----------+-------+
// UDF: choose values from matchid or gnk_id for the new column
val myUDF = udf[Integer,Integer,Integer]((df2_matchid: Integer, df1_gnk_id: Integer) => {
if (df2_matchid == null) df1_gnk_id
else df2_matchid
})
df1.join(df2, $"gnk_id"===$"gnk_matchid", "left_outer")
.select($"df1.*", $"df2.matchid" as "matchid2")
.withColumn("gnk_id", myUDF($"matchid2", $"gnk_id"))
.drop($"matchid2")
.show()
以下是输出:
最简单的实现方法是,下面的代码读取每个批次的维度数据文件夹,但请记住新维度数据值。在我的例子中,国家名称必须是一个新文件 下面是流+批连接的解决方案
package com.databroccoli.streaming.dimensionupateinstreaming
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, ForeachWriter, Row, SparkSession}
import org.apache.spark.sql.functions.{broadcast, expr}
import org.apache.spark.sql.types.{StringType, StructField, StructType, TimestampType}
object RefreshDimensionInStreaming {
def main(args: Array[String]) = {
@transient lazy val logger: Logger = Logger.getLogger(getClass.getName)
Logger.getLogger("akka").setLevel(Level.WARN)
Logger.getLogger("org").setLevel(Level.ERROR)
Logger.getLogger("com.amazonaws").setLevel(Level.ERROR)
Logger.getLogger("com.amazon.ws").setLevel(Level.ERROR)
Logger.getLogger("io.netty").setLevel(Level.ERROR)
val spark = SparkSession
.builder()
.master("local")
.getOrCreate()
val schemaUntyped1 = StructType(
Array(
StructField("id", StringType),
StructField("customrid", StringType),
StructField("customername", StringType),
StructField("countrycode", StringType),
StructField("timestamp_column_fin_1", TimestampType)
))
val schemaUntyped2 = StructType(
Array(
StructField("id", StringType),
StructField("countrycode", StringType),
StructField("countryname", StringType),
StructField("timestamp_column_fin_2", TimestampType)
))
val factDf1 = spark.readStream
.schema(schemaUntyped1)
.option("header", "true")
.csv("src/main/resources/broadcasttest/fact")
var countryDf: Option[DataFrame] = None: Option[DataFrame]
def updateDimensionDf() = {
val dimDf2 = spark.read
.schema(schemaUntyped2)
.option("header", "true")
.csv("src/main/resources/broadcasttest/dimension")
if (countryDf != None) {
countryDf.get.unpersist()
}
countryDf = Some(
dimDf2
.withColumnRenamed("id", "id_2")
.withColumnRenamed("countrycode", "countrycode_2"))
countryDf.get.show()
}
factDf1.writeStream
.outputMode("append")
.foreachBatch { (batchDF: DataFrame, batchId: Long) =>
batchDF.show(10)
updateDimensionDf()
batchDF
.join(
countryDf.get,
expr(
"""
countrycode_2 = countrycode
"""
),
"leftOuter"
)
.show
}
.start()
.awaitTermination()
}
}
最简单的实现方法是,下面的代码读取每个批次的维度数据文件夹,但请记住新维度数据值。在我的例子中,国家名称必须是一个新文件 下面是流+批连接的解决方案
package com.databroccoli.streaming.dimensionupateinstreaming
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, ForeachWriter, Row, SparkSession}
import org.apache.spark.sql.functions.{broadcast, expr}
import org.apache.spark.sql.types.{StringType, StructField, StructType, TimestampType}
object RefreshDimensionInStreaming {
def main(args: Array[String]) = {
@transient lazy val logger: Logger = Logger.getLogger(getClass.getName)
Logger.getLogger("akka").setLevel(Level.WARN)
Logger.getLogger("org").setLevel(Level.ERROR)
Logger.getLogger("com.amazonaws").setLevel(Level.ERROR)
Logger.getLogger("com.amazon.ws").setLevel(Level.ERROR)
Logger.getLogger("io.netty").setLevel(Level.ERROR)
val spark = SparkSession
.builder()
.master("local")
.getOrCreate()
val schemaUntyped1 = StructType(
Array(
StructField("id", StringType),
StructField("customrid", StringType),
StructField("customername", StringType),
StructField("countrycode", StringType),
StructField("timestamp_column_fin_1", TimestampType)
))
val schemaUntyped2 = StructType(
Array(
StructField("id", StringType),
StructField("countrycode", StringType),
StructField("countryname", StringType),
StructField("timestamp_column_fin_2", TimestampType)
))
val factDf1 = spark.readStream
.schema(schemaUntyped1)
.option("header", "true")
.csv("src/main/resources/broadcasttest/fact")
var countryDf: Option[DataFrame] = None: Option[DataFrame]
def updateDimensionDf() = {
val dimDf2 = spark.read
.schema(schemaUntyped2)
.option("header", "true")
.csv("src/main/resources/broadcasttest/dimension")
if (countryDf != None) {
countryDf.get.unpersist()
}
countryDf = Some(
dimDf2
.withColumnRenamed("id", "id_2")
.withColumnRenamed("countrycode", "countrycode_2"))
countryDf.get.show()
}
factDf1.writeStream
.outputMode("append")
.foreachBatch { (batchDF: DataFrame, batchId: Long) =>
batchDF.show(10)
updateDimensionDf()
batchDF
.join(
countryDf.get,
expr(
"""
countrycode_2 = countrycode
"""
),
"leftOuter"
)
.show
}
.start()
.awaitTermination()
}
}
您的第一个链接已失效,我还怀疑您是否可以在配置单元表上使用Spark SQL进行更新,因为有一个开放的票证支持配置单元事务表的更新:来自本书。配置单元解决方案只是将文件连接起来,而不会更改或更改记录。可以使用ORC格式和配置单元中的事务表以及insert、update、delete更新配置单元中的数据,它会定期自动为您进行连接。目前,这仅适用于orc格式的表。存储为orc的表也可以使用Hbase和Phoenix,因为顶部配置单元上的SQL层最初不是为更新而设计的,因为它纯粹是以仓库为中心的,最近的一个可以进行更新,以事务方式删除etc。确实可以使用配置单元以ORC格式对配置单元事务表进行更新。但是,Spark SQL不支持这一点:目前无法在配置单元事务表上使用Spark SQL进行更新,正如我提到的票证中所记录的,您的第一个链接已失效,而且我怀疑您是否可以在配置单元表上使用Spark SQL进行更新,因为存在支持配置单元事务表更新的开放票证:来自本书。配置单元解决方案只是将文件连接起来,而不会更改或更改记录。可以使用ORC格式和配置单元中的事务表以及insert、update、delete更新配置单元中的数据,它会定期自动为您进行连接。目前,这仅适用于orc格式的表。存储为orc格式的表也可以使用Hbase和Phoenix,因为顶部配置单元上的SQL层最初不是为更新而设计的,因为它纯粹是以仓库为中心的,最近的一个可以以事务方式进行更新、删除等。这确实是
可以使用配置单元以ORC格式对配置单元事务表进行更新。但是,Spark SQL不支持这一点:目前不可能在配置单元事务表上使用Spark SQL进行更新,正如我提到的票证中记录的,这里回答了类似的问题这里回答了类似的问题
package com.databroccoli.streaming.dimensionupateinstreaming
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, ForeachWriter, Row, SparkSession}
import org.apache.spark.sql.functions.{broadcast, expr}
import org.apache.spark.sql.types.{StringType, StructField, StructType, TimestampType}
object RefreshDimensionInStreaming {
def main(args: Array[String]) = {
@transient lazy val logger: Logger = Logger.getLogger(getClass.getName)
Logger.getLogger("akka").setLevel(Level.WARN)
Logger.getLogger("org").setLevel(Level.ERROR)
Logger.getLogger("com.amazonaws").setLevel(Level.ERROR)
Logger.getLogger("com.amazon.ws").setLevel(Level.ERROR)
Logger.getLogger("io.netty").setLevel(Level.ERROR)
val spark = SparkSession
.builder()
.master("local")
.getOrCreate()
val schemaUntyped1 = StructType(
Array(
StructField("id", StringType),
StructField("customrid", StringType),
StructField("customername", StringType),
StructField("countrycode", StringType),
StructField("timestamp_column_fin_1", TimestampType)
))
val schemaUntyped2 = StructType(
Array(
StructField("id", StringType),
StructField("countrycode", StringType),
StructField("countryname", StringType),
StructField("timestamp_column_fin_2", TimestampType)
))
val factDf1 = spark.readStream
.schema(schemaUntyped1)
.option("header", "true")
.csv("src/main/resources/broadcasttest/fact")
var countryDf: Option[DataFrame] = None: Option[DataFrame]
def updateDimensionDf() = {
val dimDf2 = spark.read
.schema(schemaUntyped2)
.option("header", "true")
.csv("src/main/resources/broadcasttest/dimension")
if (countryDf != None) {
countryDf.get.unpersist()
}
countryDf = Some(
dimDf2
.withColumnRenamed("id", "id_2")
.withColumnRenamed("countrycode", "countrycode_2"))
countryDf.get.show()
}
factDf1.writeStream
.outputMode("append")
.foreachBatch { (batchDF: DataFrame, batchId: Long) =>
batchDF.show(10)
updateDimensionDf()
batchDF
.join(
countryDf.get,
expr(
"""
countrycode_2 = countrycode
"""
),
"leftOuter"
)
.show
}
.start()
.awaitTermination()
}
}