2、Spark DataFrame理解和使用之概念理解-Schemas\Columns\Rows\Spark Types

这些天突然发觉基础的东西真的还蛮重要的。尤其当理解到位了,能随心所用的时候,真的能优化代码质量,提高工作效率。

我以前都不喜欢整理和记录这些很细节琐碎的事情,总是想从宏观的角度去把握和理解事情。现在也开始关注些细节。整体和细节,两手抓才能行得稳,走得长远吧。


概念理解

Spark中的DataFrame和Datasets是具有定义好的行、列的(分布式的)数据表。(可以具象理解成EXCEL表格)

Spark中DataFrame和Datasets是不可变的、lazily懒惰执行的,只有当执行一个action操作时,才会执行并返回结果。

表、视图 和DataFrame基本上是一样的。只不过在表和视图上,我们用SQL代码写操作命令,在DataFrame上,我们用DataFrame的操作代码。

在结构化API中,DataFrame是非类型化(untyped)的,Datasets是类型化(typed)的。说DataFrame非类型化,指的是spark只在运行(runtime)的时候检查数据的类型是否与指定的schema一致,而Datasets在编译(compile)的时候就检查数据类型是否符合规范。

DataFrame是Row类型的简单版的Datasets。Row类型是Spark为计算而优化的内存格式的内部表示形式,有助于高效计算。JVM类型会导致很高的垃圾回收、对象实例化成本,因此Spark不使用JVM类型,而是在自己内部的sprak数据类型上运行。

Schemas

schema定义了列名和数据类型。Schema是Spark的StructType类型,由一些域StructFields组成,域中明确了列名、列类型、以及一个布尔类型的参数来表示该列是否可以有缺失值或null值,最后也可以可选择的明确该列关联的元数据。(机器学习库中元数据是一种存储列信息的方式,平常很少用到。)

有两种方式定义:手动定义、或从数据源中读入。

使用手动定义Schema还是从数据中读入取决于现实使用场景。多数情况下读入就可以了,但有时候在解析CSV/JSON文本获取Schema时稍微有点慢,另外,也可能会有准确性问题,比如类型推断时,可能把Long类型识别成Int类型。当使用Spark做线上生产环境的ETL时,建议使用人工手动定义的方法。

方式一:手动定义schema

--in scala 

//in scala
import org.apache.spark.sql.types._
import org.apache.spark.sql.types.Metadata
//手动定义Schema
val schema = StructType(Seq(
    StructField("columnA", IntegerType, nullable = false,Meatadata.fromJson("{\"hello\":\"world\"}")),
    StructField("columnB", StringType, nullable = true),
    StructField("columnC", DateType, nullable = true)
    ))

//使用手动定义的Schema作为创建的DF的Schema
val dfJson = spark.read.format("json").schema(myManualSchema).load("/data/test.json")
//打印Schema
dfJson.printSchema() 

--in python

# in Python
from pyspark.sql.types import StructField, StructType, StringType, LongType
myManualSchema = StructType([
	StructField("sku_id", StringType(), True),
	StructField("itm_sku_nm", StringType(), True),
	StructField("attr_name", StringType(), True),
	StructField("attr_value", StringType(), True)
	])

cate_df=spark.read.format("csv").schema(myManualSchema).load("hdfs://user/ad/dev/sku_attr_data")
cate_df.cache()
cate_df.createOrReplaceTempView("sku_title")
spark.sql('''select * from sku_title limit 100''').show()

方式二:从数据源中读入

//从文件header中推断schema
val dfCsvSchema = spark.read.format("csv").option("header",true).load("/data/test.csv").schema
//从文件读入数据到DF,输出其推断的Schema
val dfJsonSchema = spark.read.format("json").load("/data/test.json").schema

常见使用方式示例:

//读入数据,推断Schema,创建视图
val df = spark.read.format("csv")
  .option("header", "true")
  .option("inferSchema", "true")
  .load("/data/day/2010-12-01.csv")
df.printSchema()
df.createOrReplaceTempView("dfTable")

Columns

Columns列代表了一个数据类型,比如整数型、String型,或者Array、map类型,或者null值。Spark会跟踪所有列的类型信息,并且提供列变换的方法。你可以选择、删除、对列进行计算等操作,这些操作被表示成表达式expressions。对Spark,列其实是基于表达式对每条记录(record)的值进行计算的逻辑构建。

要想一列有值,得先有row;要有ro w,必须要有一个DataFrame。不能脱离DataFrame的环境对列进行操作,必须在一个DataFrame中使用spark变换来修改列的内容。需要注意的是列在与catalog中的列名进行比较之前是不解析的(not resolved),列和表的解析是在analyzer阶段。这就是说,你的代码可能发现不了列名不存在的bug。(如果使用IDEA并安装了插件,是可以发现列名引用错误的,而且compile时也会爆出来。)

  • 引用列的方法-col  or colmun 函数 或语法糖 $ or '
import org.apache.spark.sql.functions.{col,column}
//列引用的方式
df.col("count")
df.column("count")
df.$"count"
df.'count
  • 获取DataFrame的列名
//获取列名
val colnames = spark.read.format("json").load("/data/test.json").columns

 

Rows

DataFrame的每一条记录都是Row类型。Spark用列表达式操作Row对象来生成计算数据。创建Row类型数据的方式有:从SQL手工创建、从RDD创建、读取数据源。

//使用range函数创建Row类型的数据集
spark.range(4).toDF().collect()

//从DF中查看数据返回的是一个Row对象类型
df.first()
  • 手动创建Rows对象

Row对象是没有Schema的,只有DF有,所以手动创建Row对象时,要注意数据的位置与DF中Schema定义的顺序一致。

import org.apache.spark.sql.Row
val myRow = Row("Hello",null,9,false)
  • 获取Row对象中的数据

根据位置获取想要的数据就可以。

myRow(0)  //Any 类型
myRow(0).asInstanceOf[String] //转成String类型
myRow.getString(0) //String类型
myRow.getInt(9) //Int类型

 

Spark Types

Spark有自己内部的类型表示。当我们用代码语言(scala、Python、Java、R)来指定数据类型时,它实际上会通过查表,将我们使用语言的数据类型转成spark内部的数据类型。不同语言(python\scala)的数据类型 与 spark数据类型的对应表在后面给出。

首先,来看下数据类型的实例化

//实例化数据类型
import org.apache.spark.sql.types._
val b = ByteType

scala语言的数据类型映射参考:

Data typeValue type in ScalaAPI to access or create a data type
ByteTypeByteByteType
ShortTypeShortShortType
IntegerTypeIntIntegerType
LongTypeLongLongType
FloatTypeFloatFloatType
DoubleTypeDoubleDoubleType
DecimalTypejava.math.BigDecimalDecimalType
StringTypeStringStringType
BinaryTypeArray[Byte]BinaryType
BooleanTypeBooleanBooleanType
TimestampTypejava.sql.TimestampTimestampType
DateTypejava.sql.DateDateType
ArrayTypescala.collection.SeqArrayType(elementType, [containsNull]). Note: The default value of containsNull is true.
MapTypescala.collection.MapMapType(keyType, valueType, [valueContainsNull]). Note: The default value of valueContainsNull is true.
StructTypeorg.apache.spark.sql.RowStructType(fields). Note: fields is an Array of StructFields. Also, fields with the same name are not allowed.
StructFieldThe value type in Scala of the data type of this field (for example, Int for a StructField with the data type IntegerType)StructField(name, dataType, [nullable]). Note: The default value of nullable is true.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值