DataFrame是Row类型的Dataset集合。
spark.range(2).toDF().collect()
spark类型:可以通过如下使用Scala类型
import org.apache.spark.sql._
val b = ByteType
DataFrame创建示例
val df = spark.read.format("json")
.loan("/data/t.json")
DF的转换操作
可以通过获取一组行并将它们转换操作为一个DF来即时创建DF
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.{StructField, StructType, StringType, LongType}
val myManualSchema = new StructType(Array(
new StructField("some", StringType, true),
new StructField("col", StringType, true),
new StructField("names", Longype, false)))
val myRows = Seq(Row("Hello", null ,1L))
val myRDD = spark.sparkContext.parallelize(myRows)
val myDf = spark.createDataFrame(myRDD, myManualSchema)
myDf.show()
创建的df可以通过如下方式查看模式:
df.printSchema()
df.schema
列
有很多方法来构建,如:
import org.apache.spark.sql.functions.{col,column}
col("someColumnName")
column("someColumnName")
$"myColumn"
'myColumn
//如果想显示的引用某一列
df.col("count")
列的表达式:
expr("someCol") 等同于 col("someCol")
(((col("someCol") + 5) * 200) - 6 ) < col("otherCol") 等同于
expr("(((someCol + 5) * 200 ) - 6 ) < otherCol")
可以使用columns属性查询DF的所有列:
df.columns
更改列类型
df.withColumn("count2",col("count").cast("long"))
添加列
withColum函数,此函数有两个参数,分别为列名和赋值表达式
//添加一个仅包含1的列
df.withColum("numberOne", lit(1)).show(2)
DEST_COUNTRY_NAME | count | numberOne |
---|---|---|
United States | 15 | 1 |
United States | 2 | 1 |
//当出发国家与目的国家相同时,设置为一个bool标志
df.withColumn("withinCounter",expr("ORIGIN_COUNTRY_NAME == DEST_COUNTRY_NAME"))
.show(2)
也可以使用这个函数给字段重命名
df.withColumn("newName",expr("DEST_COUNTRY_NAME")).columns
重命名列函数:withColumnRenamed(原列名, 新列名)
df.withColumnRenamed("DEST_COUNTRY_NAME","DEST").columns
删除列
df.drop("DEST_COUNTRY_NAME").columns
行
可以通过以下方式查看df的一行
df.first()
可以通过手动创建Row对象来创建DF的列,但是需要保证与DF的列顺序一致
import org.apache.spark.sql.Row
val myRow = Row("hello",null, 1, false)
//访问行的数据
myRow(0) //任意类型
myRow(0).asInstanceOf[String] //字符串
myRow.getString(0) //字符串
myRow.getInt(2) //整型
过滤行使用filter或where,两者作用相同
//一般使用where操作
df.filter(col("count") < 2).show(2)
df.where("count < 2 ").show(2)
df.where(col("count") < 2).where(col("DEST_COUNTRY_NAME") =!= "Croatia")
获取去重后的行
//对select中的两列进行去重
df.select("ROIGIN_COUNTRY_NAME", "DEST_COUNTRY_NAME").distinct().count()
连接和追加行
使用union操作
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($"ROIGIN_COUNTRY_NAME" =!= "United States")
.show()
tip: =!= 不仅能比较字符串,还能比较表达式
行排序
sort和orderBy方法是等效的:
df.sort("count").show()
df.orderBy("count", "DEST_COUNTRY_NAME").show(2)
//指定升序还是降序
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)
重划分和合并
根据一些经常过滤的列队数据进行分区,控制跨集群数据的物理布局,重新分区会导致数据的全面洗牌。
如果经常根据某一列执行过滤操作,那么根据该列进行重新分区是很有必要的。
df.repartition(col("DEST_COUNTRY_NAME"))
//也可以指定分区的数量
df.repartition(5, col("dest_country_name"))
select函数和selectExpr函数
//select查看列的方式
df.select(df.col("a"),
col("b"),
column("c"),
'd,
$"e",
expr("f")).show()
df.select("a","b").show()
//使用expr重命名,将ods_name重命名为new_name后,在重命名回去
df.select(expr("old_name as new_name").alias("old_name"))
df.selectExpr("ols_name as new_name", "aa").show(2)
我们可以使用selectExpr构建复杂表达式来创建DF
//在原有列的基础上加一列boolean的判断列withinCountry
df.selectExpr("*", // 包含所有原始表中的列
"DEST_SOUNTRY_NAME = ORIGIN_SOUNTRY_NAME as withinCountry")
.show(2)
可以使用系统定义好的聚合函数
df.selectExpr("avg(count)", "count(distinct(DEST_COUNTRY_NAME))").show(2)
转换操作spark类型(字面量)
有时候需要显示的传递给spark一个值,可能是一个常量或者是接下来需要比较的值,这种情况需要用到literal(字面量)
df.select(expr("*"), lit(1).as("one")).show(2)
结果如下:
DEST_COUNTRY_NAME | count | one |
---|---|---|
United States | 15 | 1 |
United States | 2 | 1 |
随机抽样与随机分割
随机抽样使用sample方法,通过withReplacement参数指定是否放回
df.sample(withReplacement = false, 0.5 , seed = 5).count()
随机分割可以按照比例分成几份:
val fataFrames : Array[DataFrame] = df.randomSplit(Array(0.25,0.75), seed)