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()

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

被折叠的 条评论
为什么被折叠?



