SSS —— Spark Structured Streaming 之单列拆分成多列

前言:
你既然点开了这篇文章,说明你正在有将一列拆分成多列的需求,因为在博主前面一篇入门Spark Structured Streaming 中已经说了,在结构化流编程当中我们受到了很多的限制,就比如很多的静态的DataFrame方法在这个上面并不能使用,这就给开发上带来了很多的困扰,今天这篇文章就来讲一讲如何将一列变成多列.

一. 分割函数

既然要一变多那肯定少不了分割函数 ,在Spark SQL 中有这样一个函数 split(str, pattern),源代码中的介绍是这样的:

@since(1.5)
@ignore_unicode_prefix
def split(str, pattern):
    """
    Splits str around pattern (pattern is a regular expression).

    .. note:: pattern is a string represent the regular expression.

    >>> df = spark.createDataFrame([('ab12cd',)], ['s',])
    >>> df.select(split(df.s, '[0-9]+').alias('s')).collect()
    [Row(s=[u'ab', u'cd'])]
    """
    sc = SparkContext._active_spark_context
    return Column(sc._jvm.functions.split(_to_java_column(str), pattern))

通过源码可以看到这个函数的功能还是很强大的,支持以正则表达式的方式进行分割.
字符串分割过后就变成了一个字符串列表.
下面来做一个测试(省略了一些无关紧要的代码):

from pyspark.sql import functions as f

source_df的结构是这样的:
source_df.show()
'''
+------------------+
|       value      |
+------------------+
|      a,b,c,d     |
+------------------+
'''
split_df = f.split(source.value,",")
type(split_df)
# 查看分割后得到的这个结果是什么类型:
# <class pyspark.sql.Column >
# 可以看到返回的是一个列类型,我们要把他合并在DataFrame中才能显示
# 利用下面这种方式就可以了:
append_split_column = source_df.withColumn("split_value",split_df)
append_split_column.show()
'''
+------------------+-------------------+
|       value      |    split_value    |
+------------------+-------------------+
|      a,b,c,d     | ['a','b','c','d'] |
+------------------+-------------------+
'''

二. 获取列表列的项

上面我们只是将列中的字符串通过分隔符进行分割成了列表,还是以列表的方式保存在了一列当中,但是我们想要的是每一个元素单独分成一列.在Pyspark的内置函数当中,有一个函数叫做explode 爆炸,它呢能实现将一个列表分成多行,显然不是我们需要的,对于列表拆分成多列Spark没有内置这样的方式,我们只能自己实现:
首先来看:

add_one_column = split_df.getItem(0)
source_df.withColumn("one",add_one_column).show()

'''
+------------------+-------------+
|       value      |     one     |
+------------------+-------------+
|      a,b,c,d     |      a      |
+------------------+-------------+
'''

从上面的代码我们可以看到已经从列表中提取出了单独的列,然后通过withColumn方法添加到了原始DataFrame的后面,下面就依葫芦画瓢,把剩下的以链式调用的方式,全部和平起来吧

merge_df = source_df.withColumn("1",split_df.getItem(0)) \
					.withColumn("2",split_df.getItem(1)) \
					.withColumn("3",split_df.getItem(2)) \
					.withColumn("4",split_df.getItem(3)) \
					.drop("value")
merge_df.show()
'''
+-------+-------+-------+-------+
|   1   |   2   |   3   |   4   |
+-------+-------+-------+-------+
|   a   |   b   |   c   |   d   |
+-------+-------+-------+-------+
'''

最后我们把原始的列删除了,这样我们便完成了一列变多列

三. JSON字符串分割成多列

前面讲了最简单的固定分隔符的字符串,分割并拆分成多列的方法,这里就来讲一下如何解析DataFrame中的JSON列并将其分割成多列,
既然要讲,就直接上一个比较复杂一点的JSON字符串也好应对不同的场景:
现在原始列的数据格式是这样的:

{
    "name":"zs",
    "age":19,
    "interests":[
        "basketball",
        "football",
        "tennis"
    ],
    "edu":{
        "primary":{
            "name":"ttt",
            "graduation":1587880706
        }
    }
}

下面来看如何解析这样的格式:

# 首先还是需要SpparkSQL中内置的函数方法
from pyspark.sql import functions as f

source_df.show()
# 由于篇幅原因所以自动截断了
'''
+--------------------------------------------------------------------+
|                               value                                |
+--------------------------------------------------------------------+
| {"name": "zs", "age": 19, "interests": ["basketball", "footba....  | 
+--------------------------------------------------------------------+

'''

我们需要用到functions 中的 from_json(col, schema, options={})
函数的源码:

@ignore_unicode_prefix
@since(2.1)
def from_json(col, schema, options={}):
    """
    Parses a column containing a JSON string into a :class:`MapType` with :class:`StringType`
    as keys type, :class:`StructType` or :class:`ArrayType` with
    the specified schema. Returns `null`, in the case of an unparseable string.

    :param col: string column in json format
    :param schema: a StructType or ArrayType of StructType to use when parsing the json column.
    :param options: options to control parsing. accepts the same options as the json datasource

    .. note:: Since Spark 2.3, the DDL-formatted string or a JSON format string is also
              supported for ``schema``.

    >>> from pyspark.sql.types import *
    >>> data = [(1, '''{"a": 1}''')]
    >>> schema = StructType([StructField("a", IntegerType())])
    >>> df = spark.createDataFrame(data, ("key", "value"))
    >>> df.select(from_json(df.value, schema).alias("json")).collect()
    [Row(json=Row(a=1))]
    >>> df.select(from_json(df.value, "a INT").alias("json")).collect()
    [Row(json=Row(a=1))]
    >>> df.select(from_json(df.value, "MAP<STRING,INT>").alias("json")).collect()
    [Row(json={u'a': 1})]
    >>> data = [(1, '''[{"a": 1}]''')]
    >>> schema = ArrayType(StructType([StructField("a", IntegerType())]))
    >>> df = spark.createDataFrame(data, ("key", "value"))
    >>> df.select(from_json(df.value, schema).alias("json")).collect()
    [Row(json=[Row(a=1)])]
    >>> schema = schema_of_json(lit('''{"a": 0}'''))
    >>> df.select(from_json(df.value, schema).alias("json")).collect()
    [Row(json=Row(a=1))]
    >>> data = [(1, '''[1, 2, 3]''')]
    >>> schema = ArrayType(IntegerType())
    >>> df = spark.createDataFrame(data, ("key", "value"))
    >>> df.select(from_json(df.value, schema).alias("json")).collect()
    [Row(json=[1, 2, 3])]
    """

    sc = SparkContext._active_spark_context
    if isinstance(schema, DataType):
        schema = schema.json()
    elif isinstance(schema, Column):
        schema = _to_java_column(schema)
    jc = sc._jvm.functions.from_json(_to_java_column(col), schema, options)
    return Column(jc)

根据函数中的文档我们很快便知道如何使用,
我们先要有个和JSON字符串对应的模式(Schema)

# 导入SparkSql中所有的类型
from pyspark.sql.types import *
# 根据上面的json字符串创建一个对应模式
my_schema = StructType([
    StructField("name", StringType()),
    StructField("age", IntegerType()),
    StructField("interests", ArrayType(StringType())),
    StructField("edu", StructType([
        StructField("primary", StructType([
            StructField("name", StringType()),
            StructField("graduation", TimestampType())
        ]))
    ]))
])

模式创建完成之后只需要将对应的json字符串列和模式传入from_json() 函数即可

json_column = f.from_json(source_df,my_schema)
# 和前面分割字符串一样,这里得到的也只是一个json结构的单独的列
# 我们还是需要手动将这些字段提取出来
# 因为我这里是演示,就提几个重要的字段,明白方法即可
# 我们这里换一种方法来添加列,用select的方式来提取
merge_df = source_df.select(
	json_column.getItem("name").alias("student_name"),
	json_column.getItem("age").alias("age"),
	json_column.getItem("edu").getItem("primary").getItem("name").alias("primary_name"),
	json_column.getItem("edu").getItem("primary").getItem("graduation").alias("primary_graduation"),
	json_column.getItem("interests").getItem(0).alias("interests1")
)
merge_df.show()
'''
+------+-----+--------------+--------------------+------------+
| name | age | primary_name | primary_graduation | interests1 |
+------+-----+--------------+--------------------+------------+
|  zs  | 19  |     ttt      |     1587880706     | basketball |
+------+-----+--------------+--------------------+------------+
'''

这就完成了在Spark Structured Streaming中单列变多列,其核心其实就是要围绕着这个source_df也就是输入表,流式表进行操作,这样就可以使用很多的静态DataFrame上的方法了

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值