【问题标题】:spark extract columns from stringspark从字符串中提取列
【发布时间】:2020-11-12 23:31:34
【问题描述】:

在解析字符串时需要帮助,其中包含每个属性的值。下面是我的示例字符串...

otherPartofString Name=<Series VR> Type=<1Ac4> SqVal=<34> conn ID=<2>

有时,字符串可以包含其他具有不同分隔符的值,例如

otherPartofString Name=<Series X> Type=<1B3> SqVal=<34> conn ID=<2> conn Loc=sfo dest=chc bridge otherpartofString.. 

输出列将是

Name      | Type | SqVal | ID | Loc  | dest 
-------------------------------------------
Series VR | 1Ac4 | 34    | 2  | null | null
Series X  | 1B3  | 34    | 2  | sfo  | chc 

【问题讨论】:

  • 封闭的&lt; &gt; 是否用于包含具有空格的值?否则很难将dest=chcbridge otherpartofString 分开?我们将需要对最后一个捕获字段进行一些预处理或后处理。

标签: python apache-spark pyspark


【解决方案1】:

正如我们所讨论的,要对您的示例数据使用str_to_map 函数,我们可以将 pairDelimkeyValueDelim 设置为以下内容:

pairDelim: '(?i)>? *(?=Name|Type|SqVal|conn ID|conn Loc|dest|$)'
keyValueDelim: '=<?'

其中 pariDelim 不区分大小写 (?i) 带有可选的 &gt; 后跟零个或多个空格,然后是预定义的键之一(我们使用 '|'.join(keys) 生成它是动态的)或字符串锚$的结尾。 keyValueDelim 是一个带有可选&lt; 的“=”。

from pyspark.sql import functions as F

df = spark.createDataFrame([                                               
   ("otherPartofString Name=<Series VR> Type=<1Ac4> SqVal=<34> conn ID=<2>",),   
   ("otherPartofString Name=<Series X> Type=<1B3> SqVal=<34> conn ID=<2> conn Loc=sfo dest=chc bridge otherpartofString..",)
],["value"])

keys = ["Name", "Type", "SqVal", "conn ID", "conn Loc", "dest"]

# add the following conf for Spark 3.0 to overcome duplicate map key ERROR
#spark.conf.set("spark.sql.mapKeyDedupPolicy", "LAST_WIN")

df.withColumn("m", F.expr("str_to_map(value, '(?i)>? *(?={}|$)', '=<?')".format('|'.join(keys)))) \
    .select([F.col('m')[k].alias(k) for k in keys]) \
    .show()
+---------+----+-----+-------+--------+--------------------+
|     Name|Type|SqVal|conn ID|conn Loc|                dest|
+---------+----+-----+-------+--------+--------------------+
|Series VR|1Ac4|   34|      2|    null|                null|
| Series X| 1B3|   34|      2|     sfo|chc bridge otherp...|
+---------+----+-----+-------+--------+--------------------+

我们需要对最后一个映射键的值进行一些后期处理,因为没有锚或模式可以将它们与其他不相关的文本区分开来(这可能是一个问题,因为它可能发生在任何键上) ,如果您可以指定任何模式,请告诉我。

编辑:如果使用 map 对于不区分大小写的搜索效率较低,因为它需要一些昂贵的预处理,请尝试以下操作:

ptn = '|'.join(keys)
df.select("*", *[F.regexp_extract('value', r'(?i)\b{0}=<?([^=>]+?)>? *(?={1}|$)'.format(k,ptn), 1).alias(k) for k in keys]).show()

如果尖括号 &lt;&gt; 仅在值或其下一个相邻键包含任何非单词字符时使用,则可以通过一些预处理来简化:

df.withColumn('value', F.regexp_replace('value','=(\w+)','=<$1>')) \
    .select("*", *[F.regexp_extract('value', r'(?i)\b{0}=<([^>]+)>'.format(k), 1).alias(k) for k in keys]) \
    .show()

Edit-2:添加了一个字典来处理关键别名:

keys = ["Name", "Type", "SqVal", "ID", "Loc", "dest"]

# aliases are case-insensitive and added only if exist
key_aliases = {
    'Type': [ 'ThisType', 'AnyName' ],
    'ID': ['conn ID'],
    'Loc': ['conn Loc']
}

# set up regex pattern for each key differently
key_ptns = [ (k, '|'.join([k, *key_aliases[k]]) if k in key_aliases else k) for k in keys ]  
#[('Name', 'Name'),
# ('Type', 'Type|ThisType|AnyName'),
# ('SqVal', 'SqVal'),
# ('ID', 'ID|conn ID'),
# ('Loc', 'Loc|conn Loc'),
# ('dest', 'dest')]  

df.withColumn('value', F.regexp_replace('value','=(\w+)','=<$1>')) \
    .select("*", *[F.regexp_extract('value', r'(?i)\b(?:{0})=<([^>]+)>'.format(p), 1).alias(k) for k,p in key_ptns]) \
    .show()
+--------------------+---------+----+-----+---+---+----+
|               value|     Name|Type|SqVal| ID|Loc|dest|
+--------------------+---------+----+-----+---+---+----+
|otherPartofString...|Series VR|1Ac4|   34|  2|   |    |
|otherPartofString...| Series X| 1B3|   34|  2|sfo| chc|
+--------------------+---------+----+-----+---+---+----+

【讨论】:

  • 顺便说一句。由于您使用的是 Spark 2.3 和不区分大小写的搜索,因此 pandas_udf 存在问题。如您之前的问题之一所示,使用 regex_extract 获取这些键的值可能是更好的选择。
  • 嗨@jxc..抱歉回复晚了。假设如果字符串是 'Name= Location Type=' 即,如果没有 值,则 str_to_map 失败..但是您使用 'regexp_extract' 提供的最后一个解决方案是预期的。再次感谢您的详细解释并一路帮助我。你是最棒的:)
  • @marc,很高兴它有帮助,函数str_to_map 有限制,除了两个分隔符之外,对、键/值之间不应有任何间隙,在您的示例中,您可能必须设置键=Location Type 而不是 Type。并且 map 在处理不区分大小写的搜索时也效率不高(需要一些后期或预处理)。
  • 顺便说一句。你能投票并接受我的回答吗,晚安! :)
  • 对不起.. 忘记接受了。再次感谢,晚安@jxc :)
猜你喜欢
  • 2021-07-27
  • 1970-01-01
  • 1970-01-01
  • 2022-09-28
  • 2021-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多