pyspark.sql.functions

文章目录

    • array_distinct(col)
    • array_except(col1, col2)
    • array_intersect(col1, col2)
    • array_sort(col)
    • array_union(col1, col2)
    • collect_list(col)
    • collect_set(col)
    • concat(*cols)、concat_ws(sep, *cols)
    • countDistinct(col, *cols)
    • create_map(*cols)
    • isnull(col)
    • regexp_extract(str, pattern, idx)
    • regexp_replace(str, pattern, replacement)
    • substring(str, pos, len)
    • when(condition, value)
    • udf(f, returnType=StringType)
    • pandas_udf(f, returnType, functionType)
      • pandas_udf简介
      • Pandas_UDF与toPandas的区别
      • Scalar类型的Pandas_UDF()
      • Grouped Map类型的Pandas_UDF()
      • GROUPED_AGG类型的Pandas_UDF()

pyspark.sql.functions是一个内置函数的集合,该模块功能强大,API函数众多,可能并不会每个都会讲到,但是重点的、难的、经常使用的一定会讲解到的。

array_distinct(col)

array_distinct(col)是一个集合函数,表示从数组中删除重复值。

>>> df = spark.createDataFrame([([1, 2, 3, 2],), ([4, 5, 5, 4],)], ['data'])
>>> df.select(array_distinct(df.data)).collect()
[Row(array_distinct(data)=[1, 2, 3]), Row(array_distinct(data)=[4, 5])]

array_except(col1, col2)

相当于求差集,并且返回col1中剩余的元素

>>> from pyspark.sql import Row
>>> df = spark.createDataFrame([Row(c1=["b", "a", "c"], c2=["c", "d", "a", "f"])])
>>> df.select(array_except(df.c1, df.c2)).collect()
[Row(array_except(c1, c2)=['b'])]

array_intersect(col1, col2)

集合函数:求两列集合的交集

>>> from pyspark.sql import Row
>>> df = spark.createDataFrame([Row(c1=["b", "a", "c"], c2=["c", "d", "a", "f"])])
>>> df.select(array_intersect(df.c1, df.c2)).collect()
[Row(array_intersect(c1, c2)=['a', 'c'])]

array_sort(col)

集合函数:排序

>>> df = spark.createDataFrame([([2, 1, None, 3],),([1],),([],)], ['data'])
>>> df.select(array_sort(df.data).alias('r')).collect()
[Row(r=[1, 2, 3, None]), Row(r=[1]), Row(r=[])]

array_union(col1, col2)

集合函数:并集

>>> from pyspark.sql import Row
>>> df = spark.createDataFrame([Row(c1=["b", "a", "c"], c2=["c", "d", "a", "f"])])
>>> df.select(array_union(df.c1, df.c2)).collect()
[Row(array_union(c1, c2)=['b', 'a', 'c', 'd', 'f'])]

collect_list(col)

聚合函数,返回列表

>>> df2 = spark.createDataFrame([(2,), (5,), (5,)], ('age',))
>>> df2.agg(collect_list('age')).collect()
[Row(collect_list(age)=[2, 5, 5])]

collect_set(col)

聚合函数,返回set集合

>>> df2 = spark.createDataFrame([(2,), (5,), (5,)], ('age',))
>>> df2.agg(collect_set('age')).collect()
[Row(collect_set(age)=[5, 2])]

concat(*cols)、concat_ws(sep, *cols)

合并两列、以连接符何必两列

>>> df = spark.createDataFrame([('abcd','123')], ['s', 'd'])
>>> df.select(concat(df.s, df.d).alias('s')).collect()
[Row(s='abcd123')]
>>> df.select(concat_ws('-', df.s, df.d).alias('s')).collect()
[Row(s='abcd-123')]

countDistinct(col, *cols)

这里需要注意一下,不仅可以求一列、还可以求多列去重之后的数目。

>>> df.agg(countDistinct(df.age, df.name).alias('c')).collect()
[Row(c=2)]

create_map(*cols)

生成字典

>>> df.select(create_map('name', 'age').alias("map")).collect()
[Row(map={'Alice': 2}), Row(map={'Bob': 5})]
>>> df.select(create_map([df.name, df.age]).alias("map")).collect()
[Row(map={'Alice': 2}), Row(map={'Bob': 5})]

isnull(col)

查看列值是否为空

>>> df = spark.createDataFrame([(1, None), (None, 2)], ("a", "b"))
>>> df.select(isnull("a").alias("r1"), isnull(df.a).alias("r2")).collect()
[Row(r1=False, r2=False), Row(r1=True, r2=True)]

regexp_extract(str, pattern, idx)

从指定的字符串列中提取与Java正则表达式匹配的特定组。 如果正则表达式不匹配,或者指定的组不匹配,则返回空字符串。

>>> df = spark.createDataFrame([('100-200',)], ['str'])
>>> df.select(regexp_extract('str', r'(\d+)-(\d+)', 1).alias('d')).collect()
[Row(d='100')]

regexp_replace(str, pattern, replacement)

正则替换

>>> df = spark.createDataFrame([('100-200',)], ['str'])
>>> df.select(regexp_replace('str', r'(\d+)', '--').alias('d')).collect()
[Row(d='-----')]

substring(str, pos, len)

返回特定位置的子串

>>> df = spark.createDataFrame([('abcd',)], ['s',])
>>> df.select(substring(df.s, 1, 2).alias('s')).collect()
[Row(s='ab')]

when(condition, value)

计算条件列表并返回多个可能的结果表达式之一。 如果未调用Column.otherwise(),则为不匹配的条件返回None。

>>> df.select(when(df['age'] == 2, 3).otherwise(4).alias("age")).collect()
[Row(age=3), Row(age=4)]
>>> df.select(when(df.age == 2, df.age + 1).alias("age")).collect()
[Row(age=3), Row(age=None)]

udf(f, returnType=StringType)

简单来说,pyspark.sql.functions.udf()用来将用户自定义的函数进行转化,使其可以在pyspark中被使用,udf是用户自定义函数用法中最基础的,下面举例说明。

>>>def age_name(age,name):
    return str(age)+name
>>>age_name_udf=fn.udf(age_name,StringType())
>>>df=spark.createDataFrame([(5,"Alice",80),(5,"Bob",80),\
                          (10,"Tom",80)],["age","name","height"])
>>>slen_udf = fn.udf(lambda s: len(s), IntegerType())
>>>df.select(age_name_udf(df.age,df.name).alias("age_name"),\
          slen_udf(df.name).alias("name_len")).collect()
[Row(age_name='5Alice', name_len=5),
 Row(age_name='5Bob', name_len=3),
 Row(age_name='10Tom', name_len=3)]

用户可以根据实际需求通过udf自定义函数实现相应的功能。

pandas_udf(f, returnType, functionType)

pandas_udf简介

用过pandas和pyspark的人可能知道,pandas对于数据的操作有时候确实比pyspark强大或者说是方便,所以,有时候用pyspark处理数据不方便的时候,需要用到pandas,这时候就得将pyspark的df转化为pandas,即to_Pandas(),但是pyspark是分布式处理,而pandas是本地单机处理,一旦数据量的大的时候,to_pandas是行不通的。这时候pandas_udf派上用场了。
Pandas_UDF是在PySpark2.3中新引入的API,由Spark使用Arrow传输数据,使用Pandas处理数据。Pandas_UDF是使用关键字pandas_udf作为装饰器或包装函数来定义的,不需要额外的配置。目前,有三种类型的Pandas_UDF,分别是Scalar(标量映射)和Grouped Map(分组映射)、GROUPED_AGG(分组聚合)。其中,f为自定义函数,returnType为返回类型,functionType为函数类型,默认为Scalar类型。

Pandas_UDF与toPandas的区别

  • pandas_udf 创建一个向量化的用户定义函数(UDF),利用了panda的矢量化特性,是udf的一种更快的替代方案,因此适用于分布式数据集。
  • toPandas将分布式spark数据集转换为pandas数据集,对pandas数据集进行本地化,并且所有数据都驻留在驱动程序内存中,因此是单机运行。
  • 总结起来就是:pandas_udf使用panda API来处理分布式数据集,而toPandas()将分布式数据集转换为本地数据,然后使用pandas进行处理。

Scalar类型的Pandas_UDF()

Scalar Pandas UDF用于向量化标量操作。常常与 pyspark.sql.DataFrame.withColumn() and pyspark.sql.DataFrame.select()等函数一起使用。其中调用的Python函数需要使用pandas.Series作为输入并返回一个具有相同长度的pandas.Series。具体执行流程是,Spark将列分成批,并将每个批作为数据的子集进行函数的调用,进而执行panda UDF,最后将结果连接在一起。

from pyspark.sql.functions import pandas_udf,PandasUDFType
from pyarrow.lib import cpu_count,set_cpu_count
from pyspark.sql.types import IntegerType
slen = pandas_udf(lambda s: s.str.len(), IntegerType())
@pandas_udf(StringType())  
def to_upper(s):
    return s.str.upper()
@pandas_udf(IntegerType(),PandasUDFType.SCALAR)  
def add_one(x):
    return x + 1
df = spark.createDataFrame([(1, "John Doe", 21)],("id", "name", "age"))  
df.select(slen("name").alias("slen(name)"), to_upper("name"), add_one("age")).collect() 
#[Row(slen(name)=8, to_upper(name)='JOHN DOE', add_one(age)=22)]

其中,@pandas_udf是注解形式的用法,用起来更为方便。

Grouped Map类型的Pandas_UDF()

Grouped map(分组映射)panda_udf与groupBy().apply()一起使用,实现了“split-apply-combine”模式。“split-apply-combine”包括三个步骤:

  • 使用DataFrame.groupBy将数据分成多个组
  • 对每个分组应用一个函数。函数的输入和输出都是pandas.DataFrame。输入数据包含每个组的所有行和列。
  • 将分组结果合并,并返回。

返回的pandas.DataFrame的列标签必须与定义的returnType模式中的字段名称匹配, 返回的pandas.DataFrame的长度可以是任意的。

df=spark.createDataFrame([(26,"tan",170),(24,"qian",171),(20,"tao",183),
                          (24,"liu",168),(26,"song",158),(26,"jiang",168)],
                         ["age","name","height"])
ls=[StructField("age",IntegerType(),True),StructField("name",StringType(),True),StructField("height",IntegerType(),True)]
@pandas_udf(StructType(ls),PandasUDFType.GROUPED_MAP)
def agg(pdf):
    return pdf.assign(name="".join(list(pdf.name)))
df.groupby("age").apply(agg).show()
+---+------------+------+
|age|        name|height|
+---+------------+------+
| 26|tansongjiang|   170|
| 26|tansongjiang|   158|
| 26|tansongjiang|   168|
| 20|         tao|   183|
| 24|     qianliu|   171|
| 24|     qianliu|   168|
+---+------------+------+

返回值类型,可以是StructType类型,也可以是字符串表示,如下例所示:

>>> from pyspark.sql.functions import pandas_udf, PandasUDFType
>>> df = spark.createDataFrame(
...     [(1, 1.0), (1, 2.0), (2, 3.0), (2, 5.0), (2, 10.0)],
...     ("id", "v"))  
>>> :pandas_udf("id long, v double", PandasUDFType.GROUPED_MAP)  
... def normalize(pdf):
...     v = pdf.v
...     return pdf.assign(v=(v - v.mean()) / v.std())
>>> df.groupby("id").apply(normalize).show()  
+---+-------------------+
| id|                  v|
+---+-------------------+
|  1|-0.7071067811865475|
|  1| 0.7071067811865475|
|  2|-0.8320502943378437|
|  2|-0.2773500981126146|
|  2| 1.1094003924504583|
+---+-------------------+

GROUPED_AGG类型的Pandas_UDF()

Grouped aggregate Panda UDF类似于Spark聚合函数。Grouped aggregate Panda UDF常常与groupBy().agg(),它定义了来自一个或多个的聚合。级数到标量值,其中每个pandas.Series表示组中的一列。 需要注意的是,这种类型的UDF不支持部分聚合,组的所有数据都将加载到内存中。returnType应该是基本数据类型,例如DoubleType。 返回的标量可以是python原始类型,例如int或float或numpy数据类型,比如numpy.int64或numpy.float64。

df=spark.createDataFrame([(26,"tan",170),(24,"qian",171),(20,"tao",183),
                          (24,"liu",168),(26,"song",158),(26,"jiang",168)],
                         ["age","name","height"])
@pandas_udf("string",PandasUDFType.GROUPED_AGG)
def list_udf(v):
    return str(v.to_list())
@pandas_udf("int",PandasUDFType.GROUPED_AGG)
def mean_udf(v):
    return v.mean()
df.groupBy("age").agg(list_udf("name"),mean_udf("height")).show()
+---+--------------------+----------------+
|age|      list_udf(name)|mean_udf(height)|
+---+--------------------+----------------+
| 26|['tan', 'song', '...|             165|
| 20|             ['tao']|             183|
| 24|     ['qian', 'liu']|             169|
+---+--------------------+----------------+

pyspark.sql.functions的基本用法到这里就讲完了,后续会陆续更新的内容。

你可能感兴趣的:(pyspark系列)