【问题标题】:Match Two CSVs Based on Pattern Matching in One Column根据一列中的模式匹配匹配两个 CSV
【发布时间】:2019-09-27 21:49:04
【问题描述】:

我有两个这些格式的 CSV 文件:

==

FirstName | LastName | Email
Steven | Smith | stevesmith1@gmail.com
Jane | Brown | jb155@yahoo.com
Paul | Gibson | paulgibbs@outlook.com

==

ID | FirstName | LastName | IncompleteEmail
1028332982 | Steven | Smith | s*****1@g*l.com
1028820233 | Jane | Brown | j******n@yahoo.com
934943823 | Paul | Gibson | p*****s@h****l.com

==

我想在此基础上匹配两个 CSV 文件 - 如果 FirstName 和 LastName 相同,则第一个 CSV 中的 Email 匹配第二个 CSV 中的 IncompleteEmail 的模式,应创建包含ID | Email的输出

在上面的示例中,输出如下所示:

ID | Email
1028332982 | stevesmith1@gmail.com

这是因为“Steve”和“Smith”在两个 CSV 中相同,并且 IncompleteEmail 模式与电子邮件匹配。其他输入不匹配,因为IncompleteEmail 模式与电子邮件不匹配。

我以前使用过join 脚本(例如join -i -t '|' -j 1 -o 2.2,2.3 1.txt 2.txt > out.txt)来处理类似的文件,但我不知道如何修改连接脚本以使用模式而不是完全匹配。我知道 AWK 可能会采用类似的方法,但我愿意接受建议。

需要可以处理大量输入的东西(两个 CSV,每个 1000 万行以上)。

提前致谢。

【问题讨论】:

  • 您能解释一下IncompleteEmail 模式的工作原理吗? (还有 CSV?您的样本是 |- 分隔的,而不是逗号分隔的......)
  • pattern 这个词非常模糊和模棱两可,因此最好避免使用。话虽如此,我没有看到正则表达式匹配,也没有看到 * 是通配符的 char-for-char 匹配,也没有我熟悉的其他任何东西 - 这些“模式”到底是什么?在您的数据中,每个| 的每一侧是否有真的 空白字符?如果没有,请修复您的示例,使其看起来像您的真实数据。
  • 这实际上是我的问题,现在真实数据的 | 两边都没有空格——我把它们放进去让它看起来更清晰。
  • 不要发布看起来不像你的真实数据的样本数据,因为它具有完全相反的效果,你最终得到的解决方案要么不起作用,要么不起作用不是最适合您的真实数据。

标签: python bash shell join awk


【解决方案1】:

我认为您的第一个csv 文件为df1,而第二个csv 为df2。所以,你可以试试这个:

import pandas as pd
import re

df_new = pd.merge(df1,df2, on=['FirstName','LastName'], how='inner')
mails = []
regex = "((\w{1})\D.*(\w{1}\@\w{1})\D.*(\w{1}[\.]\D.+)"
for d in range(len(df_new)):
    inmail = re.findall(regex,df_new.iloc[d]["IncompleteEmail"])
    commail = re.findall(regex,df_new.iloc[d]["Email"])
    if inmail == commail:
        mails.append([df_new.iloc[d]['ID'],df_new.iloc[d]["Email"]])
pd.DataFrame(mails, columns=["ID","Email"])

输出:

           ID                  Email
0  1028332982  stevesmith1@gmail.com

【讨论】:

  • 但是输出不正确,因为您在此处列出的第二个和第三个输出在 Email 和 IncompleteEmail 之间不匹配
  • 上面可以看到。
  • 已更新。再次检查!
【解决方案2】:

假设您希望*s 的任何重复被视为与.* 在正则表达式中被处理的方式相同,并且每个其他 RE 元字符(例如.)被按字面处理并且^ 可以' t 出现在电子邮件地址中:

$ cat tst.awk
BEGIN { FS=" [|] "; OFS=" | " }
FNR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }
}
{ name = $(f["FirstName"]) FS $(f["LastName"]) }
NR==FNR {
    name2fullEmail[name] = $(f["Email"])
    next
}
FNR==1 {
    print "ID", "Email"
    next
}
name in name2fullEmail {
    fullEmail = name2fullEmail[name]
    partEmail = $(f["IncompleteEmail"])
    gsub(/./,"[&]",partEmail)
    gsub(/[[][*][]]/,".*",partEmail)
    if (fullEmail ~ "^"partEmail"$") {
        print $(f["ID"]), fullEmail
    }
}

$ awk -f tst.awk file1 file2
ID | Email
1028332982 | stevesmith1@gmail.com

【讨论】:

    【解决方案3】:

    awk 中的另一个:

    $ awk -F" [|] " -v OFS="|" '   # set field separators
    NR==FNR {                      # process first file
        a[$1 OFS $2]=$3            # hash email, use name as key
        next
    }
    ((i=$2 OFS $3) in a) {         # process second file
        gsub(/\./,"\\.",$4)        # escaping:      . -> \.
        gsub(/\*+/,".*",$4)        #           *{1,n} -> .*
        if(FNR==1 || a[i]~$4)      # if header record or regex match
            print $1,a[i]          # then output
    }' file1 file2                 # mind the order
    

    输出:

    ID|Email
    1028332982|stevesmith1@gmail.com
    

    在处理第二个文件时file2gsub(/\*+/,".*",$4) 尝试使电子邮件正则表达式ish:s*****1@g*l.com -> s.*1@g.*l\.com。提供的示例数据除了* 和. 之外没有任何其他正则表达式元字符,但其他(如+)可能也应该被转义以避免错误匹配,但我不确定你是否对它们有特殊含义,比如你有*。

    此外,它不会容忍file1 中的重复名称。最后一个实例获胜。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-05
      • 1970-01-01
      • 2020-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      相关资源
      最近更新 更多