org.apache.spark.sql.functions
1.算子
算子在使用方法上被分为两种:
1.列操作算子,即需要通过调用方法的形式使用,如
.withColumn()
2.聚合算子:即直接使用即可,如
max()
1.col:字段名
使用方法:
col("id")
+---+
| id|
+---+
| 1|
| 1|
| 2|
| 3|
+---+
源码实现:
def col(colName:String):Column = Column(colName)
可见,col 接受一个字符串类型的 colName,并调用 Column 方法接收 colName
进入 Column(),发现目标在 org.apache.spark.sql 下
private[sql] object Column{
def apply(colName:String):Column = new Column(colName)
def apply(expr: Expression): Column = new Column(expr)
}
可见,Column 有两个重载的 apply 方法,我们的 col 传入的是字符串类型的 colName,那么明显是第一个 apply,第一个 apply 方法接收一个字符串类型的 colName,并且构建了一个对象 Column,并把参数传入
进入该 Column,发现在同一包下,实现如下:
class Column(val expr:Expression) extends Logging{
def this(name:String) = this(name match{
case "*" => UnresolveStar(None)
case _ if name.endsWith(".*") =>
val parts = UnresolvedAttribute.parseAttributeName(name.substring(0,name.length - 2))
UnresolvedStar(Some(parts))
case _ => UnresolvedAttrebute.quotedString(name)
})
}
可见,方法 this 指向前一个 apply,主要逻辑为通过模式匹配进行判断
1.如果传入的 name 是 * 时,执行UnresoledStar(None)
2.使用模式守卫的方式,如果末尾的 2 位是 “.*” 的话,就用类似切片的方式将这两位给去掉(name.substring(0,name.length - 2))
3.如果以上均不满足,则执行 UnresolvedAttribute.quotedString(name),这段是为了对输入的字符串进行判断,要求对字符串进行单引号封口、“.” 的方法结束,源码如下
def parseAttributeName(name:String):Seq[String]={
def e = new AnalysisException(s"syntax error in attribute name: $name")
val nameParts = scala.collection.mutable.ArrayBuffer.empty[String]
val tmp = scala.collection.mutable.ArrayBuffer.empty[Char]
var inBacktick = false
var i = 0
while(i < name.length){
val char = name(i)
if(inBacktick){
if(char == '`'){
inBcaktick = false
if(i + 1 < name.length && name(i + 1) != '.') throw e
} else{
tmp += char
}
} else {
if (char == ’`'){
if(tmp.nonEmpty) throw e
inBacktick = true
}else if(char == '.'){
if(name(i - 1) == '.' || i == name.length - 1) throw e
nameParts += tmp.mkString
tmp.char()
}else{
tmp += char
}
}
i += 1
}
if(inBacktick) throw e
nameParts += tmp.mkString
nameParts.toSeq
}
可以将col单纯理解为一个强制带有一对单引号的字符串
2.column:字段名
使用方法:
column("id")
与 col 的实现和作用完全一致,存在的意义是有些开发者喜欢简短的 col,有些开发者喜欢明确的 column
3.lit:填充
使用方法:
.withColumn(col("id"),lit(1))
+---+
| id|
+---+
| 1|
| 1|
| 1|
| 1|
+---+
源码如下:
def lit(literal:Any):Column = typedList(listeral)
可见,lit 接收一个任意类型的参数 literal,并将其赋给 typedList 方法,源码如下
def typedLit[T:TypeTag](literal:T):Column = literal match{
case c:Column => c
case s:Symbol => new ColumnName(s.name)
case _ => Column(Literal.create(literal))
}
这段代码对方法指定了一个泛型 T,T 的类型是 TypeTag,表示包含所有类型;除此之外接收一个参数,也是任意类型的,然后进行模式匹配:
1.如果传入的类型是 Column,则直接返回值
2.如果传入的类型是 Symbol(说白了就是字符串)
final class Symbol private (val name:String) extends Serializable{
override def toString():String ="'" + name
private def readResolve():Any=Symbol.apply(name)
override def hashCode=name.hashCode()
override def equals(other:Any) = this eq other.asInstanceOf(AnyRef)
}
那么就构建一个新对象,即创建一个新列
3.以上均不满足则转变为字面值
4.asc:正序排列
使用方法:
.orderBy(asc("id"))
+---+-----------+
| id|create_time|
+---+-----------+
| 1| 2023-05-10|
| 1| 2023-05-06|
| 2| 2023-05-25|
| 3| 2023-05-06|
+---+-----------+
为什么不能使用 select?
因为受 asc 的实现限制,直接使用 select 会缺少形参传入,进而抛出报错
源码如下:
def asc(columnName:String):Column = Column(columnName).asc
可见,function 库下面的 asc 仅仅是将自己的字符串形参传给 Column,然后使用 Column 自带的 asc 进行操作
def asc:Column = withExpr { SortOrder(expr,Ascending)}
这段代码对 SortOrder 传入了 Column 的形参即列名,也传入了 Ascending,Ascending 表示正序排列
object SortOrder {
def apply(
child: Expression,
direction: SortDirection,
sameOrderExpressions: Seq[Expression] = Seq.empty): SortOrder = {
new SortOrder(child, direction, direction.defaultNullOrdering, sameOrderExpressions)
}
...
}
case object Ascending extends SortDirection{
override def sql:String = "ASC"
override def defaultNullOrdering:NullOrdering = NullsFirst
}
可见,当传入值之后,SortOrder 实例化了一个新的SortOrder,并传入了一系列的值,最终的排序由 new 的 SortOrder 来完成
5.asc_nulls_frist:正序排列,空值放置最前
使用方法:
.orderBy(asc_nulls_frist("id"))
+----+-----------+
| id|create_time|
+----+-----------+
|null| 2023-05-06|
| 1| 2023-05-06|
| 1| 2023-05-10|
| 2| 2023-05-25|
| 3| 2023-05-06|
+----+-----------+
为什么不能使用 select?
因为受 asc 的实现限制,直接使用 select 会缺少形参传入,进而抛出报错
字面意思,不解释源码
def asc_nulls_first(columnName: String): Column = Column(columnName).asc_nulls_first
6.asc_nulls_laster:正序排列,空值放置最后
使用方法:
.orderBy(asc_nulls_laster("id"))
+----+-----------+
| id|create_time|
+----+-----------+
| 1| 2023-05-10|
| 1| 2023-05-06|
| 2| 2023-05-25|
| 3| 2023-05-06|
|null| 2023-05-06|
+----+-----------+
字面意思,不解释源码
def asc_nulls_last(columnName: String): Column = Column(columnName).asc_nulls_last
7.desc:倒序排列
使用方法:
.orderBy(desc("id"))
+----+-----------+
| id|create_time|
+----+-----------+
| 3| 2023-05-06|
| 2| 2023-05-25|
| 1| 2023-05-10|
| 1| 2023-05-06|
|null| 2023-05-06|
+----+-----------+
与正序排列类似,不解释
def desc(columnName: String): Column = Column(columnName).desc
8.desc_nulls_frist:倒序排列,空值放置最前
使用方法:
.orderBy(desc_nulls_frist("id"))
+----+-----------+
| id|create_time|
+----+-----------+
|null| 2023-05-06|
| 3| 2023-05-06|
| 2| 2023-05-25|
| 1| 2023-05-10|
| 1| 2023-05-06|
+----+-----------+
字面意思,不解释源码
def desc_nulls_first(columnName:String):Column = Column(columnName).desc_nulls_first
9.desc_nulls_last:倒序排列,空值放置最后
使用方法:
.orderBy(desc_nulls_last("id"))
+----+-----------+
| id|create_time|
+----+-----------+
| 3| 2023-05-06|
| 2| 2023-05-25|
| 1| 2023-05-10|
| 1| 2023-05-06|
|null| 2023-05-06|
+----+-----------+
字面意思,不解释源码
def desc_nulls_last(columnName:String):Column = Column(columnName).desc_nulls_last
10.approx_count_distinct:近似计算去重后的行数
使用方法:
//假设存在以下数据
//-------------
//| id |
//|-----------|
//| 1 |
//|-----------|
//| 1 |
//|-----------|
//| 2 |
//|-----------|
//| 3 |
//-------------
.select(approx_count_distinct("id"))
//结果
//------------------------------------
//| approx_count_distinct(id) |
//|----------------------------------|
//| 3 |
//|----------------------------------|
对某列进行操作,去除重复的数据后,大概统计剩下的行数,近似计算结果与真正结果必然有所差距,可支持表达式
def approx_count_distinct(e:Column):Column = withAggregateFunction{
HyperLogLogPlusPlus(e.expr)
}
还有其他三个重载方法
四个方法的方式:
1.使用 Column 指定列
.approx_count_distinct(column("a"))
.approx_count_distinct(col("a"))
2.使用字符串
.approx_count_distinct("a")
3.使用 Column指定列,并指定一定范围的偏差值,偏差值的类型是 Double
.approx_count_distinct(col("a"),0.1)
4.使用字符串,并指定一定范围的偏差值,偏差值的类型是 Double
.approx_count_distinct("a",0.1)
11.avg:统计平均数
使用方法:
.select(avg("id"))
+-------+
|avg(id)|
+-------+
| 1.75|
+-------+
源码如下:
def avg(e:Column):Column = withAggregateFunction { Average(e.expr) }
代码接收一个列类型的参数,并将参数传给 Average,Average 在 evaluateExpression 处实现模式匹配,用于最终的平均值取值
override lazy val evaluateExpression = child.dataType match{
case _:DecimalType =>
DecimalPrecision.decimalAndDecimal(
Divide(sum,count.cast(DecimalType.LongDecimal),failOnError = false)).cast(resultType)
case _ =>
Divide(sum.cast(resultType),count.cast(resultType),failOnError = false)
}
avg拥有一个重载的方法,传入的不再是 Column,而是字符串
def avg(columnName:String):Column = avg(Column(columnName))
12.collect_list:将一个分组中的数值转换成一个list
使用方法:
.select(collect_list("id"))
+----------------+
|collect_list(id)|
+----------------+
| [1, 1, 2, 3]|
+----------------+
源码:
def collect_list(e: Column):Column=withAggregateFunction { CollectList(e.expr)}
collect_list 接受一个 Column 类型的参数,并返回一个 Column ,具体实现是将形参中的列名作为参数传给 CollectList
这个算子有一个重载的方法
def collect_list(columnName:String):Column = collect_list(Column(columnName))
12.collect_set:将一个分组中的数值转换成一个set(去重)
使用方法:
.select(collect_set("id"))
+---------------+
|collect_set(id)|
+---------------+
| [1, 2, 3]|
+---------------+
源码
def collect_set(e:Column):Column = withAggreateFunction { CollectSet(e.expr)}
这个算子有一个重载的方法
def collect_set(columnName:String):Column = collect_set(Column(columnName))
可见,collect_set 接受一个 Column 类型或者 String 类型的形参,在 Column 中,方法会先将列名传入 CollectSet 方法,这是主要实现的方法,然后被 withAggreateFunction 包裹,这是一个实现聚合的方法
13.corr:返回两个列的相关性
使用方法:
.select(corr("id","pp"))
+--------------------+
| corr(id, pp)|
+--------------------+
|-0.42640143271122083|
+--------------------+
相关性:指两个列的值变为点放置在二维坐标图上,判断两个列的向量走势是否一致
def corr(column1:Column,column2:Column):Column = withAggreagteFunction{
Corr(column1.expr,column2.expr)
}
可见,这个方法接受两个参数,均为 Column 类型
这个方法有一个重载方法
def corr(columnName1:String,columnName2:String):Column={
corr(Column(columnName1),Column(column(columnNmae2)))
}
14.count:统计行数(去空)
使用方法:
select(count("id"))
+---------+
|count(id)|
+---------+
| 4|
+---------+
源码:
def count(e:Column):Column = withAggregateFunction {
e.expr match {
case s:Star => Count(Literal(1))
case _ => Count(e.expr)
}
}
count 接受一个 Column 类型的参数,并返回一个 Columnn(新列),withAggregateFunction 表示这个函数是聚合函数,然后对参数 e 进行模式匹配,当参数 e 的类型是 Star 时,执行 Count(Literal(1)),可见实际上 count 实现的是底层的 Count
object Count{
def apply(child:Expression):Count = Count(child :: Nil)
}
这里接受到 count 的操作,并将参数直接转换到 Count 中,这个 Count 是 object Count 的一个案例对象
case class Count(children:Seq[Expression]) extends DeclarativeAggregate
这个案例对象中有一个方法,这个方法是直接实现统计的作用
override lazy val updateExpressions = {
val nullableChildren = children.filter(_.nullable)
if(nullableChildren.isEmpty){
Seq(
count + 1L
)
}else{
Seq(
If(nullableChildren.map(IsNull).reduce(Or),count,count + 1L)
)
}
}
代码使用懒加载,最大程度地优化内存,同时将传入的序列拆分,然后对被拆分的元素进行累计 + 1L,这样就能实现对行数的统计了
这个方法有一个重载的方法:
def count(columnName:String):TypedColumn[Any,Long]=
count(Column(columnName)).as(ExpressionEncoder[Long]())
15.countDistinct:统计行数(去重)(去空)
使用方法;
.select(countDistinct("id"))
+----------------------+
|count(DISTINCT id, pp)|
+----------------------+
| 5|
+----------------------+
源码
def countDistinct(expr: Column,exprs: Column*):Column={
Column(UnresolveFunction("count",(expr +: exprs).map(_.expr),isDistinct = true))
}
countDistinct 接受一个 expr 和任意数量的 exprs,它们都是 Column 类型,当传入一个列时,函数只会对当前列进行去重的统计
但是当传入任意数量的列时,countDistinct 将会对所有列都进行去重操作,然后再进行统计
例如:
存在
id time pp
1 2023-05-10 5
1 2023-05-06 3
2 2023-05-25 1
1 2023-05-06 2
2023-05-06 6
4 2023-05-06
5 2023-05-06 7
当进行了
.select(countDistinct("id"))
后,id 和 pp 里有重复的值全部去掉,最后留下的是
id time pp
1 2023-05-10 5
1 2023-05-06 3
5 2023-05-06 7
统计出来一共有3行
其中
2 2023-05-25 1
1 2023-05-06 2
2023-05-06 6
4 2023-05-06
均被排除掉了
可以得知,countDisstinct 的去重并不是去除单列的重,而是两列在组合上的相同
这个方法有一个重载方法
def countDistinct(columnName:String,columnNames:String*):Column={
countDistinct(Column(columnName),columnNames.map(Column.apply) : _*)
}
表示除了使用 col 方法外,还可以使用字符串的方式指定列
17.covar_pop:求两个列的总协方差
协方差:一个衡量两个变量之间关系的统计量,衡量了这两个变量的变化趋势是否一致以及变化的强度大小
公式如下:covar_pop(X, Y) = Σ((X - μX) * (Y - μY)) / N
使用方法:
.select(covar_pop(col("id"),col("pp")))
源码:
def covar_pop(column1:Column,column2:Column):Column = withAggregateFunction{
CovPopulation(column1.expr,column2.expr)
}
covar_pop 接受两个参数,均为 Column 类型的列,并返回一个新列 Column 类型,withAggregateFunction表示这是一个聚合函数,最后将参数列表的值的表达式传入 CovPopulation 方法中
CovPopulation源码:
case class CovPopulation(
left:Expression,
right:Expression,
nullOnDivideByZero:Boolean = !SQLConf.get.legacyStatisticalAggregate)
extends Covariance(left, right, nullOnDivideByZero){
def this(left:Expression,right:Expression)=
this(left,right,!SQLConf.get.legacyStatisticalAggregate)
override val evaluateExpression:Expression={
If(n === 0.0),Literal.create(null,DoubleType),ck /n)
}
override def prettyName:String="covar_pop"
}
CovPopulation 接受三个参数:left、right,这两个的类型均为表达式,这也就是为什么 covar_pop 会将 column的表达式传过来的原因,还有一个参数 nullOnDivideByZero,类型为布尔,他有一个默认值,在 legacyStatisticalAggregate 中默认为false
协方差的主要实现在 evaluateExpression 中,如果 n 不为 0.0,则执行 ck / n
这个方法拥有一个重载的方法
def covar_pop(columnName1:String,columnName2:String):Column={
covar_pop(Column(columnName1),Column(columnName2))
}
18.covar_samp:求两个列的样本协方差
样本协方差:当数据量相当大的时候,求出总协方差所耗费的时间将会相当之长
这时候就有了样本协方差,样本协方差就是对数据的一部分进行求协方差,而不是对整个数据求
它的实现与 covar_pop 类似,只不过一个面向整个数据, 一个面向部分数据
使用方法:
.select(covar_samp(col("id"),col(pp)))
源码:
def covar_samp(column1:Column,column2:Column):Column = withAggregateFunction{
CovSample(column.expr.column.expr)
}
可以看到,实现方法和 covar_pop 几乎一致,只是在 CovSample 多了一些判断
他有一个重载的方法;
def covar_samp(columnName1:String,columnName2:String):Column={
covar_samp(Column(columnName1),Column(columnName2))
}
19.first:返回一列中第一个数据
使用方法:
//如果不需要忽略null值,则
.select(first(col("id")))
//如果需要忽略null值,则
.select(first(col("id"),true))
+---------+
|first(id)|
+---------+
| 1|
+---------+
源码:
def first(e:Column,ignoreNulls:Boolean):Column = withAggregateFunction{
First(e.expr,ignoreNulls)
}
first 接受两个参数,Column 列类型的 e 和 Boolean 类型的 ignoreNull,其中 ignoreNull 表示是否忽略null
后面的 withAggregateFunction 表示这个方法是一个聚合函数
最后将两个参数传给 First 方法
提问:
first 有两个参数,并且都没有设置默认值,为什么在调用 first 的时候,却可以不为 ignoreNull 设置值呢?
答:
看源码:
case class First(child:Expression,ignoreNulls:Boolean)
extends DeclarativeAggregate with ExpectsInputType
def this(child:Expression) = this(child,false)//重点
def this(child:Expression,ignoreNullsExpr:Expression) = {
this(child,FirstLast.validateIgnoreNullExpr(ignoreNullsExpr,"first"))
}
First 拥有两个形参,并且拥有两个构造器引用:主构造器引用 和 辅助构造器引用
当传入两个参数时,参数会被主构造器引用接入,随后调用的就是主构造器
当传入一个参数时,参数会被主构造器判断,不满足则被辅助构造器接受,辅助构造器只接受 child,然后为第二个参数默认给一个 false,再调回 First
这个方法还有其他三个重载方法:
def first(columnName:String,ignoreNulls:Boolean):Column = {
first(Column(columnName),ignoreNulls)
}
这个重载方法相较于先前的方法,只有接受的 列 参数有更改,改为接受一个字符串,然后逻辑上调用 先前的 first,本质上和先前的 first一致,只不过使用时的写法不一致
这也是为什么 spark 很灵活的原因之一
def first(e:Column):Column = first(e,ignoreNulls = false)
在我们不写入 ignoreNulls 参数时调用 first,会直接调到这个方法下,这个方法在传入给 先前的 first 时为 ignoreNulls 设置了默认值 false,这里需要传的参数是 列类型
def first(columnName:String):Column = first(Column(columnName))
这个重载的方法 调用了前一个 first,与之不同的是,这个 first 传入的是字符串
20.grouping:对分级分组使用,判断当前字段是否正常分组,0为正常,1为异常
正常分组:基于具体的列值进行分组操作
非正常分组:也成为超级边界分组,表示对所有的数据进行整体聚合
假设有以下元数据
+-------+---+
| Name | id|
+-------+---+
| Alice | 1 |
| Alice | 2 |
| Bob | 3 |
| Bob | 4 |
+-------+---+
正常分组:
+-------+-------+
| Name |sum(id)|
+-------+-------+
| Alice | 3 |
| Bob | 7 |
+-------+-------+
非正常分组(null):
+-------+-------+
| Name |sum(id)|
+-------+-------+
| Alice | 3 |
| Bob | 7 |
| null | 10 |
+-------+-------+
这就是对所有的数据进行整体聚合
使用方法:
spark.table("v_a")
.rollup("name").agg(sum(col("id")),grouping("name")).orderBy("name")
.show()
+-----+-------+--------------+
| name|sum(id)|grouping(name)|
+-----+-------+--------------+
| null| 16| 1|
|Alice| 9| 0|
| Bob| 7| 0|
+-----+-------+--------------+
rollup:汇总,即分级聚合
源码:
def grouping(e:Column):Column = Column(Grouping(e.expr))
grouping 获取一个 Column 列类型的参数 e,并且将 e 的表达式传递给 Grouping
这个方法有一个重载方法:
def grouping(columnName:String):Columns = grouping(Column(columnName))
21.grouping_id:用法与grouping一致,区别在于grouping_id可以拥有若干条列
22.kurtosis:求一列的峰度(只对数值类型有效)
峰度:
衡量实数随机变量概率分布的峰态,峰度高意味着方差大,是由低频度的大于或小于平均值的极端插值引起的
一般情况下,正态分布的峰值为3,所以峰值 bk < 3,则是分布具有不足的峰度,如果峰值 bk > 3,则分布具有过度的峰度,峰度可以用来检验分布的正态性
根据均值不等式可以确定,峰度(系数)的取值范围:它的下限不会低于1,上限不会高于数据的个数
实际上,峰度并没有标准值,只是一个测量的结果
正态分布的峰值为常数3,均匀分布的峰度为常数1.8等,一般情况下,我们把这两个典型的分布曲线作为评价峰态数据序列分布性态的参照
使用方法:
.select(kurtosis(col("id")))
+-----------------+
| kurtosis(id)|
+-----------------+
|2.948847071730202|
+-----------------+
源码:
def kurtosis(e:Column):Column = withAggregateFunction{
Kurtosis(e.expr)
}
kurtosis 接受一个 column 列类型的参数 e,withAggregateFunction 表明他是一个聚合函数,最终将参数 e 的表达式传递给 Kurtosis
Kurtosis 源码:
case class Kurtosis(child:Exprssion,nullOnDivideByZero:Boolean = !SQLConf.get.legacyStatisticalAggregate){
override val evaluateExpression:Expression = {
If(n === 0.0,Literal.create(null,DoubleType),
If(m2 === 0.0,divideByZeroEvalResult,n * m4 / (m2 * m2) - 3.0))
}
}
这是实现峰值的主要代码
这个算子有一个重载的方法:
def kurtosis(columnName:String):Column = kurtosis(Column(columnName))
23.last:返回一列中最后一个值
使用方法:
.select(last(col("id")))
+--------+
|last(id)|
+--------+
| 0.1|
+--------+
源码:
def last(e:Column,ignoreNulls:Boolean):Column = withAggregateFunction{
new Last(e.expr,ignoreNulls)
}
last 接受两个参数,Column 列类型 e 和 Boolean 布尔类型 ignoreNulls,意思是是否忽略null值
withAggregateFunction 表明这是聚合函数
最后创建一个 Last 实例,将 e 和 ignoreNulls 传入
last 拥有其他三个重载方法
def last(columnName:String,ignoreNulls:Boolean):Column ={
last(Column(columnName),ignoreNulls)
}
这个接受字符串,然后传给前面的哪个 last
def last(e:Column):Column = last(e,ignoreNulls = false)
接受列,但是没有了 boolean,然后把列传给第一个 last,并且传过去的时候给个默认的 false
def last(columnName:String):Column = last(Column(columnName),ignoreNulls = false)
和上一个类似,只不过接受的是字符串
24.max:求一列中最大的值
使用方法:
.select(max(col("id")))
+-------+
|max(id)|
+-------+
| 100.0|
+-------+
源码:
def max(e:Column):Column = withAggregateFunction{ Max(e.expr)}
接受一个 Column 列类型参数 e,是聚合函数,参数 e 传给 Max
这个方法拥有一个重载方法
def max(columnName:String):Column = max(Column(columnName))
25.mean:求一列的平均数
使用方法
.select(mean(col("id")))
+-------+
|avg(id)|
+-------+
| 15.85|
+-------+
源码:
def mean(e:Column):Column = avg(e)
接受 一个列,返回一个新列,赋给 avg(就是上面那个avg)
提问:为什么有一个 avg 了,还要定义一个 mean
答:增加可维护性,如果需要新增需求,则只需要对 mean 进行修改,而不需要对 avg 进行修改
这个方法拥有一个重载方法:
def mean(columnName:String):Column = avg(columnName)
26.min:求一列最小值
使用方法
.select(min(col("id")))
+-------+
|min(id)|
+-------+
| 0.1|
+-------+
源码:
def min(e:Column):Column = withAggregateFuinction{ Min(e.expr)}
接受 一个列,返回一个新列,声明聚合函数,将形参传给 Min
这个方法有一个重载方法
def min(columnName:String):Column = mun(Column(columnName))
27.percentile_approx:近似百位数
使用方式:
.select(percentile_approx(col("id"),lit(0.5),lit(100)))
+-------------------------------+
|percentile_approx(id, 0.5, 100)|
+-------------------------------+
| 0.3|
+-------------------------------+
源码:
def percentile_approx(e:Column,percentage:Column,accuracy:Column):Column = {
withAggregateFunction{
new ApproximatePercentile(
e.expr,percentage.expr,accuracy.expr
)
}
}
接收三个 column,返回一个新列,表明聚合函数,创建一个实例,并把形参列表赋给实例
注意:percentage 和 accuracy 需要用 lit 填充,lit 返回的是 column,所以不用担心类型不兼容
percentage 表示取多少,这里选择 0.5 表示取中位数,如果是 0.6 则先将总数算出,然后取60%
100表示迭代次数,越多越精确
28.skewness:偏度
偏度:是统计学中的一种说法
将数据展开,绘入二维平面图中,如果曲线为完全对称,则返回0
S = 0.0 时,曲线完全对称
S > 0.0 时,曲线向右偏移
S < 0.0 时,曲线向左偏移
使用方法:
.select(skewness(col("id")))
+-----------------+
| skewness(id)|
+-----------------+
|2.192869986112599|
+-----------------+
源码:
def skewness(e:Column):Column = withAggregateFunction{ Skewness(e.expr)}
接收一个列,返回一个列,声明聚合函数,把列的表达式传入Skewness
这个算子有一个重载方法
def skewness(columnName:String):Column = skewness(Column(columnName))
接收一个字符串,返回一个列,形参传入前一个 skewness
29.stddev:标准差
标准差:是指一列数据的偏移量
假设一列数据全是5,那么它的标准差就是0
使用方法:
.select(stddev(col("id")))
+------------------+
| stddev_samp(id)|
+------------------+
|1.1952286093343936|
+------------------+
源码:
def stddev(e:Column):Column = withAggregdateFunction { StddevSamp(e.expr)}
接收一个列,返回一个列,声明聚合函数,将形参传给 StddevSamp
这个算子有一个接收字符串的重载方法
30.stddev_samp:样本标准差
与 stddev 几乎一致
31.stddev_pop:总体标准差
与 stddev 的区别是 stddev 的计算对象是样本,而 stddev_pop 则是全体数据
32.sum:总计
使用方法:
.select(sum(col("id")))
+-------+
|sum(id)|
+-------+
| 20.0|
+-------+
源码:
def sum(e:Column):Column= withAggregateFunction { Sum(e.expr)}
接收一个列,返回一个新列,声明聚合函数,将形参传给 Sum
拥有一个重载,传入的是 string
33.sumDistinct:总计去重
使用方法:
.select(sumDistinct(col("id")))
源码:
def sumDistinct(e:Column):Column=withAggregateFunction{ Sum(e.expr),isDistinct = true}
传入一个列,返回一个新列,声明聚合,e 传入 Sum,去重为 true
有一个重载,传入为 String
34.varinance:方差
方差是一种衡量数据集中值的离散程度或异变程度的度量
它用于衡量数据点与其均值之间的偏离程度
方差越大,意味着数据点相对于均值的分散程度越大
使用方法:
.select(varinance(col("cont")))
+------------------+
| var_samp(id)|
+------------------+
|1.4285714285714284|
+------------------+
源码:
def variance(e:Column):Column = withAggregateFunction { VarianceSamp(e.expr)}
接受一个列,返回一个新列,声明聚合,传列给 VarinceSamp
拥有一个重载,传入字符串
35.var_samp:抽样方差
与方差不同的是,这个方法只用于抽样
36.var_pop:总体方差
与方差不同的是,这个方法用于总体
37.cume_dist:计算累积分布函数
cume_dist 是一个窗口函数
计算累计分布函数:用于返回数据在整列中位于什么分布位置
打个比方
在考试成绩中,100 分位于整个年级的前 0.1,而 0 分则位于整个年级的 1.0,50 分则位于整个年级的 0.5
使用方法:
//1.
.select(cume_dist().over(partitionBy("name").orderBy("id"))).show()
+------------------------------------------------------------------------------------+
|cume_dist() OVER (PARTITION BY name ORDER BY id ASC NULLS FIRST unspecifiedframe$())|
+------------------------------------------------------------------------------------+
| 0.25|
| 0.5|
| 0.75|
| 1.0|
| 0.25|
| 0.5|
| 0.75|
| 1.0|
+------------------------------------------------------------------------------------+
//2.
.withColumn("dis",cume_dis().over(partitionBy("name").orderBy("id")))
+---+-----------+---+-----+----+----+
| id|create_time| pp| name|cont| dis|
+---+-----------+---+-----+----+----+
|1.0| 2023-05-10| 5| Bob| 5|0.25|
|2.0| 2023-05-06| 3| Bob| 5| 0.5|
|3.0| 2023-05-25| 1| Bob| 5|0.75|
|4.0| 2023-05-06| 2| Bob| 5| 1.0|
|1.0| 2023-05-06| 7|Alice| 5|0.25|
|2.0| 2023-05-06| 7|Alice| 5| 0.5|
|3.0| 2023-05-06| 7|Alice| 5|0.75|
|4.0| 2023-05-06| 6|Alice| 5| 1.0|
+---+-----------+---+-----+----+----+
源码:
def cume_dist():Column = withExpr{new CumeDist}
38.dense_rank:对一组的特定列进行排序
使用方法:
//存在以下数据集
Bob 5 1 2023-05-10 5
Bob 5 2 2023-05-06 3
Bob 5 3 2023-05-25 1
Bob 5 4 2023-05-06 2
Alice 5 4 2023-05-06 6
Alice 5 3 2023-05-06 7
Alice 5 2 2023-05-06 7
Alice 5 1 2023-05-06 7
//说明:数据集将会被按照name进行分组,以pp(最后一列)进行排序,以展示dense_rank的作用
.withColumn("dis",dense_rank().over(partitionBy("name").orderBy("pp")))
+---+-----------+---+-----+----+---+
| id|create_time| pp| name|cont|dis|
+---+-----------+---+-----+----+---+
|3.0| 2023-05-25| 1| Bob| 5| 1|
|4.0| 2023-05-06| 2| Bob| 5| 2|
|2.0| 2023-05-06| 3| Bob| 5| 3|
|1.0| 2023-05-10| 5| Bob| 5| 4|
|4.0| 2023-05-06| 6|Alice| 5| 1|
|3.0| 2023-05-06| 7|Alice| 5| 2|
|2.0| 2023-05-06| 7|Alice| 5| 2|
|1.0| 2023-05-06| 7|Alice| 5| 2|
+---+-----------+---+-----+----+---+
可见,bense_rank 可以用来进行排名,常用的场景就是在一个班里计算分数排名,并且数值相同的则会并列,并且不会影响下一个序号
源码:
def dense_rank():Column = withExpr{ new DenseRank}
阅读窗口函数的源码没有特定的意义,只需要了解架构就好
窗口函数架构:
spark实现窗口函数架构大部分如下
1.数据分区:首先按照特定分区列进行划分,这样同分区值的数据将位于同一节点或者任务中了,这样做是为了确保每个分区中的数据能被正确处理
2.数据排序,每个分区中,数据将按照指定的排序列进行排序,这样可以通过使用快速排序等算法来实现,排序后,每个分区中的数据将按照指定的顺序排列
3.计算 DENSE_RANK:排序后的数据集中,spark遍历每行数据,并根据排序列的值以及前一行的排序值计算出 DENSE_RANK 值,具体来说,如果当前行的排序值(被排序的列的数据)和前一行相同,则当前行的 DENSE_RANK 值(DENSE_RANK的值)与前一行相同,否则,当前行的 DENSE_RANK 的值为前一行的 DENSE_RANK 值 加一。spark 会维护一个变量来跟踪前一行的排序值和 DENSE_RANK
4.汇总结果:在每个分区计算完 DENSE_RANK 值后,spakr 将各个分区的结果合并起来,以获取最终的 DENSE_RANK 值
实际上 spark 处理窗口函数的逻辑很简单,重要的是 saprk 会处理内存不足或者数据量大的时候,这时候将会使用外部排序和磁盘交换等技术来保证窗口函数的正确执行
逻辑并不是代码的核心,使得逻辑能够拥有更好的容错才是
39:lag:获取指定列的第前n行的数据,前方不满足条件可以自定义填充,默认使用null
lag翻译:落后的
如何使用:
//存在数据源
Bob 5 1 2023-05-10 5
Bob 5 2 2023-05-06 3
Bob 5 3 2023-05-25 1
Bob 5 4 2023-05-06 2
Alice 5 4 2023-05-06 6
Alice 5 3 2023-05-06 7
Alice 5 2 2023-05-06 7
Alice 5 1 2023-05-06 7
.withColumn("dis",lag("id",2).over(partitionBy("name").orderBy("pp")))
//结果
+---+-----------+---+-----+----+----+
| id|create_time| pp| name|cont| dis|
+---+-----------+---+-----+----+----+
|3.0| 2023-05-25| 1| Bob| 5|null|
|4.0| 2023-05-06| 2| Bob| 5|null|
|2.0| 2023-05-06| 3| Bob| 5| 3.0|
|1.0| 2023-05-10| 5| Bob| 5| 4.0|
|4.0| 2023-05-06| 6|Alice| 5|null|
|3.0| 2023-05-06| 7|Alice| 5|null|
|2.0| 2023-05-06| 7|Alice| 5| 4.0|
|1.0| 2023-05-06| 7|Alice| 5| 3.0|
+---+-----------+---+-----+----+----+
解读:
lag 也是窗口函数,根据 name 进行分区,根据 pp 进行排序,随后对 id 进行操作,2是指获取往前第二个数据,如果填1则是往前获取第一个数据
源码:
def lag(e:Column,offset:Int,defaultValue:Any):Column = withExpr{
Lag(e.expr,Literal(offset),Literal(defaultValue))
}
40.lead:获取指定列的第后n行的数据,后方不满足条件可以自定义填充,默认使用null
lead翻译:领先的
使用方法:
.withColumn("dis",lead(col("id"),1).over(partitionBy("name").orderBy("pp")))
+---+-----------+---+-----+----+----+
| id|create_time| pp| name|cont| dis|
+---+-----------+---+-----+----+----+
|3.0| 2023-05-25| 1| Bob| 5| 4.0|
|4.0| 2023-05-06| 2| Bob| 5| 2.0|
|2.0| 2023-05-06| 3| Bob| 5| 1.0|
|1.0| 2023-05-10| 5| Bob| 5|null|
|4.0| 2023-05-06| 6|Alice| 5| 3.0|
|3.0| 2023-05-06| 7|Alice| 5| 2.0|
|2.0| 2023-05-06| 7|Alice| 5| 1.0|
|1.0| 2023-05-06| 7|Alice| 5|null|
+---+-----------+---+-----+----+----+
源码:
def lead(e:Column,offset:Int,defaultValue:Any):Column = withExpr{
Lead(e.expr,Literal(offset),Literal(defaultValue))
}
41.nth_value:获取一列中一个分区内第n行数据,在n前的数据将会使用null填充
nth_value翻译:第n个值
使用方法:
.withColumn("dis",nth_value(col("id"),2).over(partitionBy("name").orderBy("pp")))
+---+-----------+---+-----+----+----+
| id|create_time| pp| name|cont| dis|
+---+-----------+---+-----+----+----+
|3.0| 2023-05-25| 1| Bob| 5|null|
|4.0| 2023-05-06| 2| Bob| 5| 4.0|
|2.0| 2023-05-06| 3| Bob| 5| 4.0|
|1.0| 2023-05-10| 5| Bob| 5| 4.0|
|4.0| 2023-05-06| 6|Alice| 5|null|
|3.0| 2023-05-06| 7|Alice| 5| 3.0|
|2.0| 2023-05-06| 7|Alice| 5| 3.0|
|1.0| 2023-05-06| 7|Alice| 5| 3.0|
+---+-----------+---+-----+----+----+
42.ntile:将数据尽可能均匀地分发到指定的数量中
使用方法:
.withColumn("dis",ntile(2).over(partitionBy("name").orderBy("pp")))
+---+-----------+---+-----+----+---+
| id|create_time| pp| name|cont|dis|
+---+-----------+---+-----+----+---+
|3.0| 2023-05-25| 1| Bob| 5| 1|
|4.0| 2023-05-06| 2| Bob| 5| 1|
|2.0| 2023-05-06| 3| Bob| 5| 2|
|1.0| 2023-05-10| 5| Bob| 5| 2|
|4.0| 2023-05-06| 6|Alice| 5| 1|
|3.0| 2023-05-06| 7|Alice| 5| 1|
|2.0| 2023-05-06| 7|Alice| 5| 2|
|1.0| 2023-05-06| 7|Alice| 5| 2|
+---+-----------+---+-----+----+---+
43.percent_rank:返回当前行在整列中在前百分之几
percent翻译:百分比
使用方法:
.witColumn("dis",percent_rank().over(partitionBy("name").orderBy("pp")))
+---+-----------+---+-----+----+------------------+
| id|create_time| pp| name|cont| dis|
+---+-----------+---+-----+----+------------------+
|3.0| 2023-05-25| 1| Bob| 5| 0.0|
|4.0| 2023-05-06| 2| Bob| 5|0.3333333333333333|
|2.0| 2023-05-06| 3| Bob| 5|0.6666666666666666|
|1.0| 2023-05-10| 5| Bob| 5| 1.0|
|4.0| 2023-05-06| 6|Alice| 5| 0.0|
|3.0| 2023-05-06| 7|Alice| 5|0.3333333333333333|
|2.0| 2023-05-06| 7|Alice| 5|0.3333333333333333|
|1.0| 2023-05-06| 7|Alice| 5|0.3333333333333333|
+---+-----------+---+-----+----+------------------+
44.rank:对数据进行排序,排序序号为列数,如果有重复的则并列,但不影响以后的序号
rank翻译:等级
使用方法:
.withColumn("dis",rank.over(partitionBy("name").orderBy("pp")))
+---+-----------+---+-----+----+---+
| id|create_time| pp| name|cont|dis|
+---+-----------+---+-----+----+---+
|3.0| 2023-05-25| 1| Bob| 5| 1|
|4.0| 2023-05-06| 2| Bob| 5| 2|
|2.0| 2023-05-06| 3| Bob| 5| 3|
|1.0| 2023-05-10| 5| Bob| 5| 4|
|4.0| 2023-05-06| 6|Alice| 5| 1|
|3.0| 2023-05-06| 7|Alice| 5| 2|
|2.0| 2023-05-06| 7|Alice| 5| 2|
|1.0| 2023-05-06| 8|Alice| 5| 4|
+---+-----------+---+-----+----+---+
45.row_number:对数据进行排列,排列序号为列数,如果数据有重复不影响序号
row number翻译:行编号
使用方法:
.withColumn("dis",row_number().partitionBy("name").orderBy("pp"))
+---+-----------+---+-----+----+---+
| id|create_time| pp| name|cont|dis|
+---+-----------+---+-----+----+---+
|3.0| 2023-05-25| 1| Bob| 5| 1|
|4.0| 2023-05-06| 2| Bob| 5| 2|
|2.0| 2023-05-06| 3| Bob| 5| 3|
|1.0| 2023-05-10| 5| Bob| 5| 4|
|4.0| 2023-05-06| 6|Alice| 5| 1|
|3.0| 2023-05-06| 7|Alice| 5| 2|
|2.0| 2023-05-06| 7|Alice| 5| 3|
|1.0| 2023-05-06| 8|Alice| 5| 4|
+---+-----------+---+-----+----+---+
46.array:将列中每一行的数据转换成数组
使用方法:
.select(array(col("pp")))
+---------+
|array(pp)|
+---------+
| [5]|
| [3]|
| [1]|
| [2]|
| [6]|
| [7]|
| [7]|
| [8]|
+---------+
array可以接收任意数量的列,这些列的类型必须一致
.select(array(col("pp"),col("id")))
+-------------+
|array(pp, id)|
+-------------+
| [5.0, 1.0]|
| [3.0, 2.0]|
| [1.0, 3.0]|
| [2.0, 4.0]|
| [6.0, 4.0]|
| [7.0, 3.0]|
| [7.0, 2.0]|
| [8.0, 1.0]|
+-------------+
47.map:将列的每一行数据转换成键值对
使用方法:
.select(map(col("pp"),col("id")))
+-----------+
|map(pp, id)|
+-----------+
| {5 -> 1.0}|
| {3 -> 2.0}|
| {1 -> 3.0}|
| {2 -> 4.0}|
| {6 -> 4.0}|
| {7 -> 3.0}|
| {7 -> 2.0}|
| {8 -> 1.0}|
+-----------+
.select(map(col("pp"),col("id"),col("number"),lit(1)))
+----------------------+
|map(pp, id, number, 1)|
+----------------------+
|{5 -> 1.0, 12 -> 1.0} |
|{3 -> 2.0, 11 -> 1.0} |
|{1 -> 3.0, 13 -> 1.0} |
|{2 -> 4.0, 16 -> 1.0} |
|{6 -> 4.0, 18 -> 1.0} |
|{7 -> 3.0, 19 -> 1.0} |
|{7 -> 2.0, 17 -> 1.0} |
|{8 -> 1.0, 14 -> 1.0} |
+----------------------+
48.map_from_arrays:将array转换成map
使用方法:
//存在数据源
Bob 5 12 1 2023-05-10 5
Bob 5 11 2 2023-05-06 3
Bob 5 13 3 2023-05-25 1
Bob 5 16 4 2023-05-06 2
Alice 5 18 4 2023-05-06 6
Alice 5 19 3 2023-05-06 7
Alice 5 17 2 2023-05-06 7
Alice 5 14 1 2023-05-06 8
.select(map_from_arrays(array(col("pp"),col("id")),array(col("number"),lit(1)))
+------------------------------------------------+
|map_from_arrays(array(pp, id), array(number, 1))|
+------------------------------------------------+
|{5.0 -> 12, 1.0 -> 1} |
|{3.0 -> 11, 2.0 -> 1} |
|{1.0 -> 13, 3.0 -> 1} |
|{2.0 -> 16, 4.0 -> 1} |
|{6.0 -> 18, 4.0 -> 1} |
|{7.0 -> 19, 3.0 -> 1} |
|{7.0 -> 17, 2.0 -> 1} |
|{8.0 -> 14, 1.0 -> 1} |
+------------------------------------------------+
可见 map_from_arrays 是交叉键值对的
49.coalesce:对空值进行填充
使用方法:
.select(coalesce(col("cont"),lit(1)))
+-----------------+
|coalesce(cont, 1)|
+-----------------+
|5 |
|5 |
|5 |
|5 |
|1 |
|5 |
|5 |
|5 |
+-----------------+
50.input_file_name:展示数据的来源(文件)
这个算子仅仅作用于数据从文件中读取的场景
使用方式:
.select(input_file_name().over(partitionBy("name").orderBy("id"))
51.isnan:是否为nan空,是则true,否则false
使用方法:
.select(isnan(col("cont")))
+-----------+
|isnan(cont)|
+-----------+
|false |
|false |
|false |
|false |
|false |
|false |
|false |
|false |
+-----------+
52.isnull:是否为null空,是则true,否则false
使用方法:
.select(isnull(col("cont")))
+--------------+
|(cont IS NULL)|
+--------------+
|false |
|false |
|false |
|false |
|true |
|false |
|false |
|false |
+--------------+
53.monotonicallyIncreasingId:生成一个从0开始单调递增的id列
使用方法:
.select(monotonicallyIncreasingId())
+-----------------------------+
|monotonically_increasing_id()|
+-----------------------------+
|0 |
|1 |
|2 |
|3 |
|4 |
|5 |
|6 |
|7 |
+-----------------------------+
def monotonicallyIncreasingId():Column = monotonically_increasing_id()
可见这个方法调用的是 monotonically_increasing_id 方法
这个方法被官方标记为弃用方法
54.monotonically_increasing_id:生成一个从0开始单调递增的id列
使用方法:
.select(monotonically_increasing_id())
+-----------------------------+
|monotonically_increasing_id()|
+-----------------------------+
|0 |
|1 |
|2 |
|3 |
|4 |
|5 |
|6 |
|7 |
+-----------------------------+
55.nanvl:判断空值,如果为空,返回第二个列的数据
使用方法:
.select(nanvl(col("cont"),col("id")))
+--------------+
|nanvl(cont, 1)|
+--------------+
|5.0 |
|5.0 |
|5.0 |
|5.0 |
|null |
|5.0 |
|5.0 |
|5.0 |
+--------------+
nanvl 是 Oracle的函数
56.negate:取相反数
使用方法:
.select(negate(col("cont")))
+--------+
|(- cont)|
+--------+
|-5 |
|-5 |
|-5 |
|-5 |
|null |
|-5 |
|-5 |
|-5 |
+--------+
.select(negate(lit(-1)))
+------+
|(- -1)|
+------+
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
+------+
57.not:逻辑取反
.select(not(lit(true)))
+----------+
|(NOT true)|
+----------+
|false |
|false |
|false |
|false |
|false |
|false |
|false |
|false |
+----------+
58.rand、rand、randn、randn:随机取值
随机范围是0 - 1,取值为类似0.45388730051720416
.select(rand())
+--------------------------+
|rand(-9040068917910605829)|
+--------------------------+
|0.45388730051720416 |
|0.08721754314018149 |
|0.6960345673954665 |
|0.26480657334388125 |
|0.6205650539538721 |
|0.9198830448210091 |
|0.2842366167457836 |
|0.7506318257672192 |
+--------------------------+
59.spark_partition_id:返回当前行的 spark 分区id号
使用方法:
.repartition(3)//三个分区
.select(spark_partition_id())
+--------------------+
|SPARK_PARTITION_ID()|
+--------------------+
|0 |
|0 |
|1 |
|1 |
|1 |
|2 |
|2 |
|2 |
+--------------------+
60.sqrt:开根
使用方法:
.select(sqrt(col("id")))
+------------------+
|SQRT(id) |
+------------------+
|2.0 |
|1.0 |
|1.4142135623730951|
|2.0 |
|1.7320508075688772|
|1.4142135623730951|
|1.0 |
|1.7320508075688772|
+------------------+
61.struct:结构
使用方法:
.select(struct(col("id"),col("cont")))
+----------------+
|struct(id, cont)|
+----------------+
|{4.0, 5} |
|{4.0, null} |
|{3.0, 5} |
|{2.0, 5} |
|{1.0, 5} |
|{3.0, 5} |
|{2.0, 5} |
|{1.0, 5} |
+----------------+
62.when:流程判断
when 就和 if 一样
使用方法:
.select(when(判断条件,如果真则返回).otherwise(均不满足则返回))
.select(when(col("id") < col("cont"),1).otherwise(2))
+---------------------------------------+
|CASE WHEN (id < cont) THEN 1 ELSE 2 END|
+---------------------------------------+
|1 |
|2 |
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
+---------------------------------------+
63.bitwiseNot:以二进制按位取反
使用方法:
.select(bitwiseNOT(lit(1)))
+---+
|~1 |
+---+
|-2 |
|-2 |
|-2 |
|-2 |
|-2 |
|-2 |
|-2 |
|-2 |
+---+
64.abs:取绝对值
使用方法:
.select(abs(lit(-1)))
+-------+
|abs(-1)|
+-------+
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
|1 |
+-------+
65.acos:计算acos值
使用方法:
.select(acos(col("id")))
+--------+
|ACOS(id)|
+--------+
|NaN |
|0.0 |
|NaN |
|NaN |
|NaN |
|NaN |
|0.0 |
|NaN |
+--------+
66.acosh:计算 双曲反余弦函数
使用方法:
.select(acosh(col("id")))
+------------------+
|ACOSH(id) |
+------------------+
|2.0634370688955608|
|0.0 |
|1.3169578969248166|
|2.0634370688955608|
|1.7627471740390859|
|1.3169578969248166|
|0.0 |
|1.7627471740390859|
+------------------+
67.asin:计算 sin
使用方法:
.select(asin(col("id")))
+------------------+
|ASIN(id) |
+------------------+
|NaN |
|1.5707963267948966|
|NaN |
|NaN |
|NaN |
|NaN |
|1.5707963267948966|
|NaN |
+------------------+
68.asinh:计算 双曲反正弦函数
使用方法:
,select(asinh(col("id")))
+------------------+
|ASINH(id) |
+------------------+
|2.0947125472611012|
|0.8813735870195429|
|1.4436354751788103|
|2.0947125472611012|
|1.8184464592320668|
|1.4436354751788103|
|0.8813735870195429|
|1.8184464592320668|
+------------------+
69.atan:计算 tan
使用方法:
.select(atan(col("id")))
+------------------+
|ATAN(id) |
+------------------+
|1.3258176636680326|
|0.7853981633974483|
|1.1071487177940904|
|1.3258176636680326|
|1.2490457723982544|
|1.1071487177940904|
|0.7853981633974483|
|1.2490457723982544|
+------------------+
.select(atan2(col("id"),col("cont")))
+-------------------+
|ATAN2(id, cont) |
+-------------------+
|0.6747409422235527 |
|null |
|0.5404195002705842 |
|0.3805063771123649 |
|0.19739555984988078|
|0.5404195002705842 |
|0.3805063771123649 |
|0.19739555984988078|
+-------------------+
70.atanh:计算 双曲反正切函数
使用方法:
.select(atanh(col("id")))
+---------+
|ATANH(id)|
+---------+
|NaN |
|Infinity |
|NaN |
|NaN |
|NaN |
|NaN |
|Infinity |
|NaN |
+---------+
71.bin:转换成二进制
使用方法:
.select(bin(col("id")))
+-------+
|bin(id)|
+-------+
|100 |
|1 |
|10 |
|100 |
|11 |
|10 |
|1 |
|11 |
+-------+
72.cbrt:立方根
使用方法:
.select(cbrt(col("id")))
+------------------+
|CBRT(id) |
+------------------+
|1.5874010519681996|
|1.0 |
|1.2599210498948732|
|1.5874010519681996|
|1.4422495703074083|
|1.2599210498948732|
|1.0 |
|1.4422495703074083|
+------------------+
73.ceil:向上取整
使用方法:
.select(ceil(col("id")))
+--------+
|CEIL(id)|
+--------+
|4 |
|1 |
|2 |
|4 |
|3 |
|2 |
|1 |
|3 |
+--------+
74.conv:卷积
卷积是数学和信号处理中常用的运算,也广泛用于图像处理和深度学习
卷积基本原理就是 将两个函数(或者信号)进行加权求和,其中一个函数是另一个函数的翻转和平移,这种运算可以用来提取信号中的特征、滤波图像、处理时间序列数据等
使用方法:
.select(conv(col("id"),1,1))
+--------------+
|conv(id, 1, 1)|
+--------------+
|null |
|null |
|null |
|null |
|null |
|null |
|null |
|null |
+--------------+
源码:
def conv(num:Column,fromBase:Int,toBase:Int):Column = withExpr{
Conv(num.expr,lit(fromBase).expr,lit(toBase).expr)
}
75.cos:余弦
使用方法:
.select(cos(col("id")))
+-------------------+
|COS(id) |
+-------------------+
|-0.6536436208636119|
|0.5403023058681398 |
|-0.9899924966004454|
|-0.4161468365471424|
|-0.6536436208636119|
|null |
|-0.4161468365471424|
|0.5403023058681398 |
|-0.9899924966004454|
+-------------------+
76.cosh:曲线反余弦
使用方法:
.select(cosh(col("id")))
+------------------+
|COSH(id) |
+------------------+
|27.308232836016487|
|1.543080634815244 |
|10.067661995777765|
|3.7621956910836314|
|27.308232836016487|
|null |
|3.7621956910836314|
|1.543080634815244 |
|10.067661995777765|
+------------------+
77.exp:指数函数
指数函数:e,即 2.718
这个函数是指:使用一个整数与e相乘
使用方法:
.select(exp(col("id")))
+------------------+
|EXP(id) |
+------------------+
|null |
|2.7182818284590455|
|2.7182818284590455|
|7.38905609893065 |
|7.38905609893065 |
|20.085536923187668|
|20.085536923187668|
|54.598150033144236|
|54.598150033144236|
+------------------+
78.expm1:指数-1
表示指数 e -1的结果
使用方法:
.select(expm1(col("id")))
+------------------+
|EXPM1(id) |
+------------------+
|null |
|1.718281828459045 |
|1.718281828459045 |
|6.38905609893065 |
|6.38905609893065 |
|19.085536923187668|
|19.085536923187668|
|53.598150033144236|
|53.598150033144236|
+------------------+
79.factorial:乘阶
乘阶:4! = 1 * 2 * 3 * 4 = 24
使用方法:
.select(factorial(col("id")))
+-------------+
|factorial(id)|
+-------------+
|null |
|1 |
|1 |
|2 |
|2 |
|6 |
|6 |
|24 |
|24 |
+-------------+
80.floor:向下取整
使用方法:
.select(floor(col("id")))
+---------+
|FLOOR(id)|
+---------+
|null |
|1 |
|1 |
|2 |
|2 |
|3 |
|3 |
|4 |
|4 |
+---------+
81.greatest:接收若干列,取出同一行中最大的数据,只有一列时无效(报错)
使用方法:
.select(greatest(col("id"),col("cont"),col("number")))
+--------------------------+
|greatest(id, cont, number)|
+--------------------------+
|null |
|14.0 |
|12.0 |
|11.0 |
|17.0 |
|19.0 |
|13.0 |
|18.0 |
|16.0 |
+--------------------------+
82.hex:十六进制
将数值转换成十六进制
使用方法:
.select(hex(col("number")))
+-----------+
|hex(number)|
+-----------+
|null |
|E |
|C |
|B |
|11 |
|D |
|13 |
|10 |
|12 |
+-----------+
83.勾股定理
传入两个值,计算斜边长
使用方法:
.select(hypot(col("id"),lit(2)))
+------------------+
|HYPOT(id, 2) |
+------------------+
|null |
|2.23606797749979 |
|2.23606797749979 |
|2.8284271247461903|
|2.8284271247461903|
|3.605551275463989 |
|3.605551275463989 |
|4.47213595499958 |
|4.47213595499958 |
+------------------+
84.least:接收若干列,取出同一行中最小的数据,只有一列时无效(报错),与greatest相反
使用方法:
.select(least(col("id"),col("pp")))
+-------------+
|least(id, pp)|
+-------------+
|null |
|1.0 |
|1.0 |
|2.0 |
|2.0 |
|3.0 |
|1.0 |
|4.0 |
|2.0 |
+-------------+
85.log:对数函数
使用方式:
.select(log(col("id")))
+------------------+
|ln(id) |
+------------------+
|null |
|0.0 |
|0.0 |
|0.6931471805599453|
|0.6931471805599453|
|1.0986122886681096|
|1.0986122886681096|
|1.3862943611198906|
|1.3862943611198906|
+------------------+
86.pow:求幂
使用方式:
.select(pow(lit(2),lit(2)))//等价于 2^2,即2的2次方
+-----------+
|POWER(2, 2)|
+-----------+
|4.0 |
|4.0 |
|4.0 |
|4.0 |
|4.0 |
|4.0 |
|4.0 |
|4.0 |
|4.0 |
+-----------+