【问题标题】:pyspark `substr' without lengthpyspark `substr' 没有长度
【发布时间】:2020-01-11 23:49:17
【问题描述】:

有没有办法在 pyspark 中对 DataFrame 列执行 substr 函数,而不指定长度?即,类似于df["my-col"].substr(begin)

【问题讨论】:

  • 从这里开始是什么?
  • 那么,如果一个字符串是Alex Shtof 那么df["my-col"].substr(3) 应该返回ex Shtof?基本上,整个字符串都来自begin!这就是你要找的吗?

标签: python pyspark apache-spark-sql


【解决方案1】:

不,我们需要同时指定参数 pos 和 len 但是请确保两者都应该是相同的类型,否则会出错。

错误:列不可迭代。

你可以这样做:

df = df.withColumn("new", F.col("previous").substr(F.lit(5), F.length("previous")-5))

【讨论】:

    【解决方案2】:

    我不知道为什么这个函数没有在pysaprk.sql.functions 模块中作为 api 公开。

    SparkSQL 支持 substring 函数,无需定义 len 参数 substring(str, pos, len)

    您可以将它与functions 模块的expr api 一起使用,如下所示:

    df.withColumn('substr_name', f.expr("substring(name, 2)")).show()
    +----------+---+-----------+
    |      name| id|substr_name|
    +----------+---+-----------+
    |Alex Shtof|  1|  lex Shtof|
    |      SMaZ|  2|        MaZ|
    +----------+---+-----------+
    

    spark 在内部是如何做到的:

    现在,如果您看到上述语句的physical plan,那么会注意到如果我们不通过len,那么spark 将自动添加2147483647

    正如@pault 在评论中所说,2147483647 是 32 位有符号二进制整数 (2^31 -1) 的最大正值。

    df.withColumn('substr_name', f.expr("substring(name, 2)")).explain()
    == Physical Plan ==
    *Project [name#140, id#141L, substring(name#140, 2, 2147483647) AS substr_name#169]
    +- Scan ExistingRDD[name#140,id#141L] --> 2147483647 is automatically added
    

    functions 模块的substring api 实现中,它希望我们显式传递长度。如果你愿意,你可以在len 中给出任何更高的数字,它可以覆盖你的列的最大长度。

    df.withColumn('substr_name', f.substring('name', 2, 100)).show()
    +----------+---+-----------+
    |      name| id|substr_name|
    +----------+---+-----------+
    |Alex Shtof|  1|  lex Shtof|
    |      SMaZ|  2|        MaZ|
    +----------+---+-----------+
    
    >>> df.withColumn('substr_name', f.substring('name', 2, 100)).explain()
    == Physical Plan ==
    *Project [name#140, id#141L, substring(name#140, 2, 100) AS substr_name#189]
    +- Scan ExistingRDD[name#140,id#141L] --> 100 is what we passed
    
    

    【讨论】:

    【解决方案3】:

    如果目标是从参数begin 给定的位置到字符串的末尾创建一个子字符串,那么你可以这样做:

    import pyspark.sql.functions as f
    
    l = [(1, 'Prague'), (2, 'New York')]
    df = spark.createDataFrame(l, ['id', 'city'])
    
    begin = 2
    
    l = (f.length('city') - f.lit(begin) + 1)
    (
      df
      .withColumn('substr', f.col('city').substr(f.lit(begin), l))
    ).show()
    
    +---+--------+-------+
    | id|    city| substr|
    +---+--------+-------+
    |  1|  Prague|  rague|
    |  2|New York|ew York|
    +---+--------+-------+
    

    【讨论】:

    • 这比使用udf 更好,但就像注释一样,任何比字符串长的长度都可以工作。所以你不必计算l。只需使用字符串的长度就足够了:f.col('city').substr(f.lit(begin), f.length('city')),或者一个非常大的数字:f.col('city').substr(f.lit(begin), f.lit(1000000))
    【解决方案4】:

    我会创建 udf。

    >>> import pyspark.sql.functions as F
    >>> from pyspark.sql.types import StringType
    
    >>> df = spark.createDataFrame([('Alice', 23), ('Brian', 25)], schema=["name", "age"])
    >>> df.show()
    +-----+---+
    | name|age|
    +-----+---+
    |Alice| 23|
    |Brian| 25|
    +-----+---+
    
    >>> @F.udf(returnType=StringType())
    ... def substr_udf(col):
    ...    return str(col)[2:]
    
    
    >>> df = df.withColumn('substr', substr_udf('name'))
    >>> df.show()
    +-----+---+------+
    | name|age|substr|
    +-----+---+------+
    |Alice| 23|   ice|
    |Brian| 25|   ian|
    +-----+---+------+
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 2015-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-14
      • 1970-01-01
      相关资源
      最近更新 更多