【问题标题】:Split string in a spark dataframe column by regular expressions capturing groups通过捕获组的正则表达式拆分火花数据框列中的字符串
【发布时间】:2019-04-05 00:17:03
【问题描述】:

鉴于下面的数据框,我想将数字列拆分为数组中原始数字的每个元素 3 个字符的数组

给定数据框:

+---+------------------+
| id|           numbers|
+---+------------------+
|742|         000000000|
|744|            000000|
|746|003000000000000000|
+---+------------------+

预期的数据框:

+---+----------------------------------+
| id|           numbers                |
+---+----------------------------------+
|742| [000, 000, 000]                  |
|744| [000, 000]                       |
|746| [003, 000, 000, 000, 000, 000]   |
+---+----------------------------------+

我尝试了不同的正则表达式,同时使用下面给出的split 函数和我认为应该在第一次尝试时使用的正则表达式:

import pyspark.sql.functions as f

df = spark.createDataFrame(
    [
        [742, '000000000'], 
        [744, '000000'], 
        [746, '003000000000000000'], 
    ],
    ["id", "numbers"]
)

df = df.withColumn("numbers", f.split("numbers", "[0-9]{3}"))

df.show()

结果是

+---+--------------+
| id|       numbers|
+---+--------------+
|742|      [, , , ]|
|744|        [, , ]|
|746|[, , , , , , ]|
+---+--------------+

我想了解我做错了什么。是否有可能设置全局标志以获取所有匹配项,或者我完全错过了正则表达式中的某些内容?

【问题讨论】:

    标签: python-3.x apache-spark pyspark apache-spark-sql


    【解决方案1】:

    以下是不使用udf 的方法:

    df = df.withColumn(
        "numbers",
        f.split(f.regexp_replace("numbers", "([0-9]{3})(?!$)", r"$1,"), ",")
    )
    
    df.show(truncate=False)
    #+---+------------------------------+
    #|id |numbers                       |
    #+---+------------------------------+
    #|742|[000, 000, 000]               |
    #|744|[000, 000]                    |
    #|746|[003, 000, 000, 000, 000, 000]|
    #+---+------------------------------+
    

    首先使用pyspark.sql.functions.regexp_replace 将3 位数字的序列替换为后跟逗号的序列。然后用逗号分割生成的字符串。

    替换模式"$1," 表示第一个捕获组,后跟逗号。

    在匹配模式中,我们还包括一个否定的字符串结尾预测,(?!$),以避免在字符串结尾添加逗号。

    参考:REGEXP_REPLACE capturing groups

    【讨论】:

    • 这很聪明-
    • @Psidom 的一个缺点是它假定字符串中不存在其他逗号。
    【解决方案2】:

    split 将删除字符串被分割的模式;您需要为此创建一个 udf:

    from pyspark.sql.functions import udf
    from pyspark.sql.types import ArrayType, StringType
    import re
    
    # create a udf with re.findall
    split_by_three = f.udf(lambda s: re.findall(r'\d{3}', s), ArrayType(StringType()))
    df.withColumn('numbers', split_by_three('numbers')).show(3, False)
    
    #+---+------------------------------+
    #|id |numbers                       |
    #+---+------------------------------+
    #|742|[000, 000, 000]               |
    #|744|[000, 000]                    |
    #|746|[003, 000, 000, 000, 000, 000]|
    #+---+------------------------------+
    
    df.withColumn('numbers', split_by_three('numbers')).printSchema()
    #root
    # |-- id: long (nullable = true)
    # |-- numbers: array (nullable = true)
    # |    |-- element: string (containsNull = true)
    

    【讨论】:

    • 每当我看到 您需要为此创建一个 udf 时,我都将其视为个人挑战。
    【解决方案3】:

    @pault 和@Psidom 方式都很棒!这是另一种选择;

    >>> split_udf = F.udf(lambda x: ','.join([''.join(i) for i in zip(*[iter(x)]*3)]))
    >>> df.withColumn('numbers', F.split(split_udf('numbers'),',')).show(truncate=False)
    +---+------------------------------+
    |id |numbers                       |
    +---+------------------------------+
    |742|[000, 000, 000]               |
    |744|[000, 000]                    |
    |746|[003, 000, 000, 000, 000, 000]|
    +---+------------------------------+
    

    【讨论】:

      猜你喜欢
      • 2015-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多