Spark 结构化API——基础操作

这篇博客介绍了Spark结构化API的基本概念,包括DataFrame、Schema及其数据类型。重点讲解了DataFrame的创建、变换,如select、filter、distinct、重分区等操作,还涉及到了列的添加、重命名及对保留字符的处理。此外,讨论了行排序、随机抽样以及数据收集到Driver的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spark 结构化API——基础操作

基本概念

DataFrame:

分布式,表格形式,行和列的集合;每一列的行数必须相同;列有特定的类型,列类型对所有行都一致。

Schema:

定义DataFrame所有列的名字和类型。

以下是如何查询一个结构化数据的schema(spark类型推断):

// in Scala
spark.read.format("json").load("/data/flight-data/json/2015-summary.json").schema

返回schema信息:

org.apache.spark.sql.types.StructType = ...
StructType(StructField(DEST_COUNTRY_NAME,StringType,true),
StructField(ORIGIN_COUNTRY_NAME,StringType,true),
StructField(count,LongType,true))

schema是由StructField组成的StructType,以下是如何定义一个Schema:

// in Scala
import org.apache.spark.sql.types.{StructField, StructType, StringType, LongType}
import org.apache.spark.sql.types.Metadata
val myManualSchema = StructType(Array(
StructField("DEST_COUNTRY_NAME", StringType, true),
StructField("ORIGIN_COUNTRY_NAME", StringType, true),
StructField("count", LongType, false,
Metadata.fromJson("{\"hello\":\"world\"}"))
))

如何使用定义的schema加载数据(不适用scala的类型推断):

val df = spark.read.format("json").schema(myManualSchema)
.load("/data/flight-data/json/2015-summary.json")

Spark数据类型:

ByteType, ShortType, IntegerType, LongType, FloatType, DoubleType, DecimalType, StringType, BinaryType, BooleanType, TimestampType, DataType, ArrayType

column:

用下面的方法引用列,列是否存在只在运行程序时才解析:

// in Scala
import org.apache.spark.sql.functions.{col, column}
col("someColumnName")
column("someColumnName")
// 引用某个表的列,常用于join
df.col("count")

row:

DataFrame的每一行是一个record,每一个record是Row。DataFrame可以看成是type Row的实例的集合。
创建Row:

// in Scala
import org.apache.spark.sql.Row
val myRow = Row("Hello", null, 1, false)

查看Row每一个元素的Spark类型,注意:Row本身没有schema,只能通过带入具体的值(符合DataFrame的schema)创建Row :

// in Scala
myRow(0) // type Any
myRow(0).asInstanceOf[String] // String
myRow.getString(0) // String
myRow.getInt(2) // Int

这里Row(R大写)表示一个type Row的实例。

DataFrame的变换

创建DataFrame

// in Scala
val df = spark.read.format("json")
.load("/data/flight-data/json/2015-summary.json")
df.createOrReplaceTempView("dfTable")

也可以传入一些行,从RDD转化生成DataFrame:

// in Scala
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.{StructField, StructType, StringType, LongType}
// 创建schema
val myManualSchema = new StructType(Array(
new StructField("some", StringType, true),
new StructField("col", StringType, true),
new StructField("names", LongType, false)))
// 创建Row
val myRows = Seq(Row("Hello", null, 1L))
// 转化成RDD
val myRDD = spark.sparkContext.parallelize(myRows)
// 创建DataFrame
val myDf = spark.createDataFrame(myRDD, myManualSchema)
myDf.show()

select and selectExpr

df.select("DEST_COUNTRY_NAME", "ORIGIN_COUNTRY_NAME").show(2)
// in Scala
import org.apache.spark.sql.functions.{expr, col, column}
df.select(
df.col("DEST_COUNTRY_NAME"),
col("DEST_COUNTRY_NAME"),
column("DEST_COUNTRY_NAME"),
'DEST_COUNTRY_NAME,
$"DEST_COUNTRY_NAME",
expr("DEST_COUNTRY_NAME"))
.show(2)
// in Scala
df.selectExpr(
"*", // include all original columns
"(DEST_COUNTRY_NAME = ORIGIN_COUNTRY_NAME) as withinCountry")
.show(2)

literal

使用literal字面量将一个值传给Spark,这个值会被转化成Spark的数据类型

// in Scala
import org.apache.spark.sql.functions.lit
df.select(expr("*"), lit(1).as("One")).show(2)

Adding column

// in Scala
df.withColumn("numberOne", lit(1)).show(2)

// in Scala
df.withColumn("withinCountry", expr("ORIGIN_COUNTRY_NAME == DEST_COUNTRY_NAME"))
.show(2)

Renaming Columns

// in Scala
df.withColumnRenamed("DEST_COUNTRY_NAME", "dest").columns

Reserved Characters and Keywords

对于空格、短划线等保留字符,如果在表达式中使用需要做处理。
下面的情况不需要withColumn的第一个参数是一个字符串,不需要对保留字符做额外的处理。

// in Scala
import org.apache.spark.sql.functions.expr
val dfWithLongColName = df.withColumn(
"This Long Column-Name",
expr("ORIGIN_COUNTRY_NAME"))

直接将字符串作为列名来引用也不需要额外处理保留字符。

// in Scala
dfWithLongColName.select(col("This Long Column-Name")).columns

这种情况下,selectExpr的各个参数都是一个表达式,对于列名这些变量这需要用` `括起来。

// in Scala
dfWithLongColName.selectExpr(
"`This Long Column-Name`",
"`This Long Column-Name` as `new col`")
.show(2)

大小写敏感

-- in SQL
set spark.sql.caseSensitive true

filter

df.filter(col("count") < 2).show(2)
df.where("count < 2").show(2)

// in Scala
df.where(col("count") < 2).where(col("ORIGIN_COUNTRY_NAME") =!= "Croatia")
.show(2)

distinct

// in Scala
df.select("ORIGIN_COUNTRY_NAME", "DEST_COUNTRY_NAME").distinct().count()

随机抽样

val seed = 5
val withReplacement = false//是否有放回抽样
val fraction = 0.5
df.sample(withReplacement, fraction, seed).count()

Random Splits

// in Scala
val dataFrames = df.randomSplit(Array(0.25, 0.75), seed)
dataFrames(0).count() > dataFrames(1).count() // False

Concatenating and Appending Rows (Union)

union只会讲两个DataFrame的数据拼接在一起,用户自行确保两各DataFrame具有相同的schema。

// in Scala
import org.apache.spark.sql.Row
val schema = df.schema
val newRows = Seq(
Row("New Country", "Other Country", 5L),
Row("New Country 2", "Other Country 3", 1L)
)
val parallelizedRows = spark.sparkContext.parallelize(newRows)
val newDF = spark.createDataFrame(parallelizedRows, schema)
df.union(newDF)
.where("count = 1")
.where($"ORIGIN_COUNTRY_NAME" =!= "United States")
.show() // get all of them and we'll see our new rows at the end

行排序

默认安装升序排列

// in Scala
df.sort("count").show(5)
df.orderBy("count", "DEST_COUNTRY_NAME").show(5)
df.orderBy(col("count"), col("DEST_COUNTRY_NAME")).show(5)

指定排列顺序

// in Scala
import org.apache.spark.sql.functions.{desc, asc}
df.orderBy(expr("count desc")).show(2)
df.orderBy(desc("count"), asc("DEST_COUNTRY_NAME")).show(2)

//asc_nulls_first, desc_nulls_first, asc_nulls_last, or desc_nulls_last 可以控制空值的位置

分区内排序

// in Scala
spark.read.format("json").load("/data/flight-data/json/*-summary.json")
.sortWithinPartitions("count")

limit

// in Scala
df.limit(5).show()

重分区和合并分区

从分区会导致一次shuffle过程。

// in Scala 获取分区信息
df.rdd.getNumPartitions // 1

// in Scala 重分区
df.repartition(5)

// in Scala 按列重分区,当有可能对列进行过滤等操作时
df.repartition(col("DEST_COUNTRY_NAME"))

// in Scala 按列重分区并指定分区个数
df.repartition(5, col("DEST_COUNTRY_NAME"))

coalsece过程合并分区,不一定会引发完整的shuffle过程。

// in Scala
df.repartition(5, col("DEST_COUNTRY_NAME")).coalesce(2)

Collecting Rows to the Driver

将分散在集群中的数据全部返回到Driver,数据量大时可能导致Driver崩溃。

// in Scala
val collectDF = df.limit(10)
collectDF.take(5) // take works with an Integer count
collectDF.show() // this prints it out nicely
collectDF.show(5, false)
collectDF.collect()
collectDF.toLocalIterator()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值