【问题标题】:Extracting multiple columns from column in PySpark DataFrame using named regex使用命名正则表达式从 PySpark DataFrame 中的列中提取多列
【发布时间】:2018-11-02 15:49:40
【问题描述】:

假设我在 pySpark 中有一个 DataFrame df,格式如下:

| id | type | description                                                  |
|  1 | "A"  | "Date: 2018/01/01\nDescr: This is a test des\ncription\n     |
|  2 | "B"  | "Date: 2018/01/02\nDescr: Another test descr\niption\n       |
|  3 | "A"  | "Date: 2018/01/03\nWarning: This is a warnin\ng, watch out\n |

这当然是一个虚拟集合,但对于这个例子来说就足够了。

我已经做了一个带有命名组的正则表达式语句,可用于从描述字段中提取相关信息,类似于:

^(?:(?:Date: (?P<DATE>.+?)\n)|(?:Descr: (?P<DESCR>.+?)\n)|(?:Warning: (?P<WARNING>.+?)\n)+$

再次,虚拟正则表达式,实际的正则表达式稍微复杂一些,但目的是捕获三个可能的组:

| DATE       | DESCR                        | WARNING                        |
| 2018/01/01 | This is a test des\ncription | None                           |
| 2018/01/02 | Another test descr\niption   | None                           |
| 2018/01/03 | None                         | This is a warnin\ng, watch out |

现在我想将作为正则表达式匹配结果的列添加到原始 DataFrame(即将这个问题中的两个虚拟表合并为一个)。

我已经尝试了几种方法来实现这一点,但还没有一种方法可以提供完整的解决方案。我尝试过的一件事是:

def extract_fields(string):
   patt = <ABOVE_PATTERN>
   result = re.match(patt, string, re.DOTALL).groupdict()
   # Actually, a slight work-around is needed to overcome the None problem when 
   #   no match can be made, I'm using pandas' .str.extract for this now
   return result

df.rdd.map(lambda x: extract_fields(x.description))

这将产生第二个表,但我认为无法将其与来自df 的原始列结合起来。我试图构建一个新的Row(),但后来我遇到了@ 987654329@-constructor,导致数据框的列全部混乱。我怎样才能实现我想要的,即一个包含六列的 DataFrame:idtypedescriptionDATEDESCRWARNING

备注。实际上,描述字段不仅仅是一个字段,而是几列。使用concat_ws,我将这些列连接成一个新列description,其中描述字段用\n 分隔,但也许可以以更好的方式合并。

【问题讨论】:

  • 为什么不试试 if re.match() # date 存储它,再次检查 Desc 是否匹配,最后检查其他人。返回这些匹配项并使用它们。
  • 您应该可以使用pyspark.sql.functions.regexp_extract 并避免使用udf,但我不相信它支持命名组。您必须按索引提取。
  • @Prazy 好主意,但这仅在描述字段的结构(即,哪些“键”将出现在那里)已知并固定时才有效。但是,在我的数据框中,有六七个这样的字段,所有字段的值都不同
  • 不要使用 UDF。有很多 regexp_extract() 示例可供您使用。
  • @konewka 看看this post,它的功能与您想要的类似,尤其是第二部分,它概括了多种模式并检查是否存在。

标签: python regex apache-spark pyspark rdd


【解决方案1】:

我认为您可以在这种情况下使用 Pandas 功能。首先,我将 df 转换为 rdd 以拆分描述字段。我拉出 Pandas df,然后使用 Pandas df 创建 spark df。无论描述字段中的列号如何,它都可以工作

>>> import pandas as pd
>>> import re
>>> 
>>> df.show(truncate=False)
+---+----+-----------------------------------------------------------+
|id |type|description                                                |
+---+----+-----------------------------------------------------------+
|1  |A   |Date: 2018/01/01\nDescr: This is a test des\ncription\n    |
|2  |B   |Date: 2018/01/02\nDescr: Another test desc\niption\n       |
|3  |A   |Date: 2018/01/03\nWarning: This is a warnin\ng, watch out\n|
+---+----+-----------------------------------------------------------+

>>> #convert df to rdd
>>> rdd = df.rdd.map(list)
>>> rdd.first()
[1, 'A', 'Date: 2018/01/01\\nDescr: This is a test des\\ncription\\n']
>>> 
>>> #split description field
>>> rddSplit = rdd.map(lambda x: (x[0],x[1],re.split('\n(?=[A-Z])', x[2].encode().decode('unicode_escape'))))
>>> rddSplit.first()
(1, 'A', ['Date: 2018/01/01', 'Descr: This is a test des\ncription\n'])
>>> 
>>> #create empty Pandas df
>>> df1 = pd.DataFrame()
>>> 
>>> #insert rows
>>> for rdd in rddSplit.collect():
...     a = {i.split(':')[0].strip():i.split(':')[1].strip('\n').replace('\n','\\n').strip() for i in rdd[2]}
...     a['id'] = rdd[0]
...     a['type'] = rdd[1]
...     df2 = pd.DataFrame([a], columns=a.keys())
...     df1 = pd.concat([df1, df2])
... 
>>> df1
         Date                         Descr                         Warning  id type
0  2018/01/01  This is a test des\ncription                             NaN   1    A
0  2018/01/02     Another test desc\niption                             NaN   2    B
0  2018/01/03                           NaN  This is a warnin\ng, watch out   3    A
>>>
>>> #create spark df
>>> df3 = spark.createDataFrame(df1.fillna('')).replace('',None)
>>> df3.show(truncate=False)
+----------+----------------------------+------------------------------+---+----+
|Date      |Descr                       |Warning                       |id |type|
+----------+----------------------------+------------------------------+---+----+
|2018/01/01|This is a test des\ncription|null                          |1  |A   |
|2018/01/02|Another test desc\niption   |null                          |2  |B   |
|2018/01/03|null                        |This is a warnin\ng, watch out|3  |A   |
+----------+----------------------------+------------------------------+---+----+

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 2017-02-26
    • 2018-12-21
    相关资源
    最近更新 更多