【问题标题】:How to convert single String column to multiple columns based on delimiter in Apache Spark如何根据 Apache Spark 中的分隔符将单个字符串列转换为多列
【发布时间】:2021-06-01 16:56:13
【问题描述】:

我有一个带有字符串列的数据框,我想从中创建多个列。

这是我的输入数据,pagename 是我的字符串列

我想从中创建多个列。字符串的格式相同 - col1:value1 col2:value2 col3:value3 ... colN:valueN 。在输出中,我需要多列 - col1 to colN,每列的值为行。这是输出 -

我怎样才能在火花中做到这一点? Scala 或 Python 都适合我。下面的代码创建输入数据框 -

scala> val df = spark.sql(s"""select 1 as id, "a:100 b:500 c:200" as pagename union select 2 as id, "a:101 b:501 c:201" as pagename """)
df: org.apache.spark.sql.DataFrame = [id: int, pagename: string]

scala> df.show(false)
+---+-----------------+
|id |pagename         |
+---+-----------------+
|2  |a:101 b:501 c:201|
|1  |a:100 b:500 c:200|
+---+-----------------+

scala> df.printSchema
root
 |-- id: integer (nullable = false)
 |-- pagename: string (nullable = false)

注意 - 该示例在此处仅显示 3 列,但总的来说,我希望处理 100 多列。

【问题讨论】:

    标签: apache-spark pyspark apache-spark-sql


    【解决方案1】:

    您可以使用str_to_map,分解生成的地图并旋转:

    val df2 = df.select(
        col("id"), 
        expr("explode(str_to_map(pagename, ' ', ':'))")
    ).groupBy("id").pivot("key").agg(first("value"))
    
    df2.show
    +---+---+---+---+
    | id|  a|  b|  c|
    +---+---+---+---+
    |  1|100|500|200|
    |  2|101|501|201|
    +---+---+---+---+
    

    【讨论】:

    • 我们可以将a,b,c 列中的数字转换为我们想要的任何数据类型吗?例如,现在输出显示为string,但我可能需要这些列为intfloat 等。
    • 您可以根据需要在agg 中投射,例如agg(first("value").cast("int"))
    • str_to_map() 是内置函数吗?
    • 在 SQL API 中可用,但在 scala/python 中不可用
    • 如果有多个列我想应用此功能,那么获取数据的最佳方法是什么?例如,如果pagename 列和pagename1 列都有这样的分隔数据,那么输出列的数量将是id,a,b,c,a1,b1,c1,其中a1,b1,c1 存在于pagename1 列中。行数相同,但列应展开
    【解决方案2】:

    所以立即想到两个选项

    分隔符

    你有一些明显的分隔符可以分割。为此使用split function

        from pyspark.sql import functions as F
    
        delimiter = ":"
        
        df = df.withColumn(
            "split_column", 
            F.split(F.col("pagename"), delimiter)
        )
        
        # "split_column" is now an array, so we need to pull items out the array
        df = df.withColumn(
            "a",
            F.col("split_column").getItem(0)
        )
    

    不理想,因为您仍然需要进行一些字符串操作来删除空格,然后进行 int 转换器 - 但这很容易应用于多个列。

    正则表达式

    由于格式非常固定,您可以使用正则表达式来做同样的事情。

        import re
        
        regex_pattern = r"a\:() b\:() c\:()"
        match_groups = ["a", "b", "c"]
        
        for i in range(re.compile(regex_pattern).groups):
            df = df.withColumn(
                match_groups[i], 
                F.regexp_extract(F.col(pagename), regex_pattern, i + 1),
            )
    

    CAVEAT:在尝试运行任何东西之前检查该正则表达式(因为我手边没有编辑器)

    【讨论】:

    • 你已经标记了pyspark,所以我用 Python 回复了,但在 Scala 中的要点是一样的
    猜你喜欢
    • 2022-01-17
    • 2021-08-20
    • 2010-10-19
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多