如何在文字中保留数字

时间:2019-10-15 14:16:03

标签: regex dataframe pyspark

我有一个pyspark数据框,我想改善正则表达式。 我想添加条件或将正则表达式修改为:

  • 恢复最后附加到/letter的所有号码。

案例1的示例:

column_example                                        |   new_column
------------------------------------------------------|-----------------                                       |
mundo por el número de NJU/LOK 12345T98789-hablantes  |   12345
hispanohablantes ZES/UJ86758/L87586:residentes en     |   86758

示例2:

  • 我不应该接受ABC字后面的数字。

列示例:

    My_column                                             |         new_column
------------------------------------------------------|---------------------
mundo por el número de ABC 8567 hablantes             |           []
------------------------------------------------------|---------------------
con dominio nativo ABC 987480 millones de personas    |           []
------------------------------------------------------|---------------------
hispanohablantes residentes en ABC98754 otros países  |           []

以下代码是:

ptn = re.complie(r'^(?:MOD)?[0-9]{4,6}$')
array_filter = udf(lambda arr: [ x.lstrip('MOD') for x in arr if re.match(ptn, x) ] if type(arr) is list else arr, ArrayType(StringType()))

我该怎么办? 谢谢

1 个答案:

答案 0 :(得分:2)

在版本 2.4.0 之前不使用udf的Spark的一种方法:

from pyspark.sql.functions import split, regexp_replace

df.withColumn('new_column'
   , split(
       regexp_replace(
           regexp_replace('My_column', r'.*?(?<!ABC\s{0,5})(?<!\d)(\d{4,6})(?=[A-Z/])', '$1\0')
         , '\0?[^\0]*$'
         , ''
       )
     ,'\0')
   ) \
  .show(truncate=False)
+-----------------------------------------------------------------------+--------------+
|My_column                                                              |new_column    |
+-----------------------------------------------------------------------+--------------+
|23458/ mundo por el nmero de NJU/LOK 12345T98789 hablantes             |[23458, 12345]|
|con dominio nativo ABC 987480 millones ZES/UJ86758/L87586:residentes en|[86758]       |
|hispanohablantes  residentes en ABC98754/ otros pases                  |[]            |
+-----------------------------------------------------------------------+--------------+

位置:

  • 使用regexp_replace:替换与以下模式匹配的文本

    .*?(?<!ABC\s{0,5})(?<!\d)(\d{4,6})(?=[A-Z/])
    

带有$1\0,可删除 NUMBER_NEEDED 之前的所有无关文本(保存在 $ 1 中),该文本之前不包含ABC\s{0,5}和{{1} },但后跟\d。在每个匹配的[A-Z/]的末尾放置一个NULL char \0

  • 使用$1将上述文本转换为数组,请注意,数组的最后一项无关紧要,应排除在外

  • 使用另一个split(text, '\0')删除结尾的不相关文本,然后再运行上述 split()函数

注释:

  • regexp_replace(text, '\0?[^\0]*$', '')将允许测试(?<!ABC\s{0,5}) NUMBER_NEEDED 之间的0-5空格。由于正则表达式负向后搜索不支持ABC,因此,如果文本之间可能包含更多空格,则可以将(?<!ABC\s*)调整为更大的数字。顺便说一句。 5适用于PySpark,但在Python (?<!ABC\s{0,5})模块中无效,该模块仅允许使用固定宽度的模式

  • re之前加上(?s),以允许点号模式(如果任何文本包含换行符)

  • 我假定您的原始文本中未显示NULL char \0,因为它不是匹配项的一部分,因此您可以在运行上述3个字符之前将其全部删除(regexp_replace(text, '\0', ''))功能。

使用udf的另一种方法:

import re
from pyspark.sql.types import ArrayType, StringType
from pyspark.sql.functions import udf

ptn = re.compile(r'(?<!ABC)(?<!\d)(\d{4,6})(?=[A-Z/])')

find_number = udf(lambda x: re.findall(ptn, re.sub(r'(?<=ABC)\s+', '', x)) if x else [], ArrayType(StringType()))

df.withColumn('new_column', find_number('My_column')).show()