【问题标题】:PySpark - String matching to create new columnPySpark - 字符串匹配以创建新列
【发布时间】:2018-03-06 18:12:12
【问题描述】:

我有一个像这样的数据框:

ID             Notes
2345          Checked by John
2398          Verified by Stacy
3983          Double Checked on 2/23/17 by Marsha 

例如,假设只有 3 名员工要检查:John、Stacy 或 Marsha。我想像这样创建一个新专栏:

ID                Notes                              Employee
2345          Checked by John                          John
2398         Verified by Stacy                        Stacy
3983     Double Checked on 2/23/17 by Marsha          Marsha

这里是正则表达式还是 grep 更好?我应该尝试什么样的功能?谢谢!

编辑:我一直在尝试一堆解决方案,但似乎没有任何效果。我应该放弃并为每个员工创建具有二进制值的列吗?即:

ID                Notes                             John       Stacy    Marsha
2345          Checked by John                        1            0       0
2398         Verified by Stacy                       0            1       0
3983     Double Checked on 2/23/17 by Marsha         0            0       1

【问题讨论】:

  • 这是一个基本问题。如果您的 Notes 列有员工姓名是任何地方,并且 Notes 列中可以有任何字符串,我的意思是“由 John 进行检查”或“Marsha 在 2/23/17 进行双重检查”等。没有办法除非您为所有可能的组合找到正确的正则表达式,否则请查找员工姓名。现在理论上这可能是无限多的。另外,如果出现新模式,您将如何找到正确的正则表达式?
  • 你能用“BY”分割字符串并取返回数组的最后一个索引吗?

标签: python regex apache-spark pyspark apache-spark-sql


【解决方案1】:

简而言之:

regexp_extract(col('Notes'), '(.)(by)(\s+)(\w+)', 4))

此表达式从 任何位置 中提取 员工姓名,它在 by 之后的位置,然后是 空格( s) 在文本栏中(col('Notes'))


详细说明:

创建一个示例数据框

data = [('2345', 'Checked by John'),
('2398', 'Verified by Stacy'),
('2328', 'Verified by Srinivas than some random text'),        
('3983', 'Double Checked on 2/23/17 by Marsha')]

df = sc.parallelize(data).toDF(['ID', 'Notes'])

df.show()

+----+--------------------+
|  ID|               Notes|
+----+--------------------+
|2345|     Checked by John|
|2398|   Verified by Stacy|
|2328|Verified by Srini...|
|3983|Double Checked on...|
+----+--------------------+

执行所需的导入

from pyspark.sql.functions import regexp_extract, col

df 使用regexp_extract(column_name, regex, group_number) 从列中提取Employee 名称。

这里 regex('(.)(by)(\s+)(\w+)') 表示

  • (.) - 任何字符(换行符除外)
  • (by) - 文字中的字by
  • (\s+) - 一个或多个空格
  • (\w+) - 长度为 1 的字母数字或下划线字符

并且 group_number 为 4,因为组 (\w+) 在表达式中位于第 4 位

result = df.withColumn('Employee', regexp_extract(col('Notes'), '(.)(by)(\s+)(\w+)', 4))

result.show()

+----+--------------------+--------+
|  ID|               Notes|Employee|
+----+--------------------+--------+
|2345|     Checked by John|    John|
|2398|   Verified by Stacy|   Stacy|
|2328|Verified by Srini...|Srinivas|
|3983|Double Checked on...|  Marsha|
+----+--------------------+--------+

Databricks notebook

注意:

regexp_extract(col('Notes'), '.by\s+(\w+)', 1)) 似乎更干净的版本和check the Regex in use here

【讨论】:

  • 如何在第二组中包含大括号 - exp。 (由{)?
  • @mrsrinivas - 请检查我的问题中的“添加代码”并告诉我为什么 Folder_num 没有在我的框架中显示任何数据? stackoverflow.com/questions/64602504/…
  • @AJR:感谢您在这里发帖。请考虑接受/反馈您之前问题的答案。
【解决方案2】:

简介

以其最简单的形式,根据提供的示例,这个答案应该足够了,尽管如果存在其他样本,并且名称应该以by以外的任何单词开头,则 OP 应该发布更多样本。


代码

See code in use here

正则表达式

^(\w+)[ \t]*(.*\bby[ \t]+(\w+)[ \t]*.*)$

替换

\1\t\2\t\3

结果

输入

2345          Checked by John
2398          Verified by Stacy
3983          Double Checked on 2/23/17 by Marsha 

输出

2345    Checked by John John
2398    Verified by Stacy   Stacy
3983    Double Checked on 2/23/17 by Marsha     Marsha

注意:上面的输出用制表符\t字符分隔每一列,所以肉眼可能看起来不正确,而只是使用在线正则表达式解析器并插入@987654328 @ 进入正则表达式匹配部分应该向您显示每列的开始/结束位置。


说明

正则表达式

  • ^在行首断言位置
  • (\w+) 捕获一个或多个单词字符 (a-zA-Z0-9_) 到组 1
  • [ \t]* 匹配任意数量的空格或制表符([ \t] 可以替换为 \h 在某些正则表达式风格中,例如 PCRE)
  • (.*\bby[ \t]+(\w+)[ \t]*.*) 将以下内容捕获到第 2 组
    • .* 匹配任何字符(换行符除外,除非使用了s 修饰符)
    • \bby匹配单词边界\b,后跟by字面意思
    • [ \t]+ 匹配一个或多个空格或制表符
    • (\w+) 捕获一个或多个单词字符 (a-zA-Z0-9_) 到第 3 组
    • [ \t]* 匹配任意数量的空格或制表符
    • .* 匹配任意字符任意次数
  • $在行尾断言位置

替换

  • \1 匹配第一个捕获组最近匹配的相同文本
  • \t制表符
  • \1 匹配第二个捕获组最近匹配的相同文本
  • \t制表符
  • \1 匹配第三个捕获组最近匹配的相同文本

【讨论】:

    【解决方案3】:

    当我再次阅读该问题时,OP 可能会提到固定的员工列表(“假设例如只有 3 名员工要检查:John、Stacy 或 Marsha”)。 如果这确实是一个已知列表,那么最简单的方法是检查这个带有单词边界的名称列表:

    regexp_extract(col('Notes'), '\b(John|Stacy|Marsha)\b', 1)
    

    【讨论】:

      【解决方案4】:

      这样的东西应该可以工作

      import org.apache.spark.sql.functions._
      dataFrame.withColumn("Employee", substring_index(col("Notes"), "\t", 2))
      

      如果您想使用正则表达式来提取正确的值,您需要类似

       dataFrame.withColumn("Employee", regexp_extract(col("Notes"), 'regex', <groupId>)
      

      【讨论】:

      • 如果员工姓名可能位于字符串的开头、中间或结尾怎么办?这还能用吗?
      • 不,在这种情况下,您需要使用正则表达式。我的解决方案严格选择最后一个作为员工的姓名。但必须有一些模式。如果你想使用 patten,你可以使用 regexp_extract(col("Notes"), , )
      猜你喜欢
      • 2018-05-03
      • 1970-01-01
      • 2022-01-26
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      • 1970-01-01
      • 2021-12-19
      • 1970-01-01
      相关资源
      最近更新 更多