Python 我应该如何使用多种过滤器类型过滤CSV的大型(70+;MB)文本文件

Python 我应该如何使用多种过滤器类型过滤CSV的大型(70+;MB)文本文件,python,bash,Python,Bash,tl;dr:我想更改列值,条件是在600k行csv中有3个不同过滤器的匹配列条目,怎么做? 我有几个数据文件,超过600000行。它们看起来像这样: random.website.com|1000002644|FunGRP_1000009280_OT|5556667777@random.website.com|User|5556667777|main|Y|6557|main||6557| 我想在匹配项中将第8列的值Y/N更改为N 我有一个列2(企业)3(集团)和6(电话号码)的过滤器列表,存储

tl;dr:我想更改列值,条件是在600k行csv中有3个不同过滤器的匹配列条目,怎么做?

我有几个数据文件,超过600000行。它们看起来像这样:

random.website.com|1000002644|FunGRP_1000009280_OT|5556667777@random.website.com|User|5556667777|main|Y|6557|main||6557|
我想在匹配项中将第8列的值
Y/N
更改为
N

我有一个列2(企业)3(集团)和6(电话号码)的过滤器列表,存储在换行符分隔的文本文件中,如下所示:

电话号码

5553690049
5553690050
5553690052
...
企业

Loud-YPOxXTFF
res-http
1700000004
...
团体

FunGRP_1000009280_OT
1300000004_CollabGrp_1
HostedVKL_1300000035_SA
...
现在我在bash中有一个程序,它迭代数据中的每个条目,提取我想用awk过滤的列(这意味着1800k个awk调用),然后在每个要检查的东西上循环三次,然后在循环中读取每个过滤器,然后检查过滤器是否匹配该项。如果是,则awk行(第4 awk)以替换第8列,并将其填充到输出文件中。如果没有与该行匹配的筛选器,则只需将未更改的行放入输出文件中。虽然效率极低,但它确实有效。代码如下:

filter () {
  while read -r entry || [[ -n "$entry" ]]; do
    phone="$(echo "$entry" | awk -F "|" '{ print $6 }')"
    group="$(echo "$entry" | awk -F "|" '{ print $3 }')"
    enterprise="$(echo "$entry" | awk -F "|" '{ print $2 }')"
    to_test=("$phone" "$group" "$enterprise")
    filters=("$NUMBER_FILTER_FILE" "$GROUP_FILTER_FILE" "$ENTERPRISE_FILTER_FILE")
    count=-1
    matched=""

    for item in "${to_test[@]}"; do
      count=$(( count+1 ))
      if [[ -n "$item" ]] && [[ -f "${filters[$count]}" ]]; then
        while read -r filter || [[ -n "$filter" ]]; do
          if [[ "$item" = "$filter" ]]; then
            echo "$entry" | awk -F "|" 'BEGIN {OFS = FS} $8="N" {print}' >> "$WORKING$OUTPUTFILE"
            matched="true"
            continue 2
          fi
        done < "${filters[$count]}"
      fi
    done

    # If no filter matches, put the original entry in the output
    [[ -z "$matched" ]] && echo "$entry" >> "$WORKING$OUTPUTFILE"
  done < "$WORKING$UNFILTEREDOUTPUTFILE"
}

我正在更新的原始程序是用bash编写的,这就是为什么我继续尝试用shell脚本解决这个问题,但我不是bash的向导,所以我开始研究python+pandas来实现这一点,因为我觉得这应该更容易。任何建议、策略或想法都会有所帮助。谢谢。

如果我理解正确,以下几点可以:

awk "
BEGIN {FS = OFS = \"|\"}
FILENAME=="\"$NUMBER_FILTER_FILE\"" {phone[\$0]++; next}
FILENAME=="\"$GROUP_FILTER_FILE\"" {group[\$0]++; next}
FILENAME=="\"$ENTERPRISE_FILTER_FILE\"" {enterprise[\$0]++; next}
FILENAME=="\"$WORKING$UNFILTEREDOUTPUTFILE\"" {
    if (phone[\$6] || group[\$3] || enterprise[\$2]) \$8 = \"N\"
    print
}" "$NUMBER_FILTER_FILE" "$GROUP_FILTER_FILE" "$ENTERPRISE_FILTER_FILE" "$WORKING$UNFILTEREDOUTPUTFILE" > "$WORKING$OUTPUTFILE"
恐怕你贴的例子没有提供足够的信息 (例如,
1000002644
行的第二个字段与任何行都不匹配 在
企业
)中,我做了一些假设。 如果我的代码不能正常工作,如果你能发布我的代码,我将不胜感激 查看我的代码的更多信息。比尔

[解释]
关键是我们如何减少计算量。你的原创 代码在中反复扫描筛选文件以查找关键字 主回路冗余且效率低下。我们可以彻底地 通过在
awk
中使用
关联数组
来减少冗余。 (请注意,相同的机制在许多语言中使用 不同的名称:
Perl中的
hash
Python中的
dictionary
,等等)

让我用中的第一行
FunGRP\u 100009280\u OT
进行说明
$GROUP\u FILTER\u文件
。通过使用关联数组在单词上做标记 通过说
组[“FunGRP\u 100009280\u OT”]++
,我们可以在后记中测试 单词以最小的计算成本包含在列表中

现在让我们回到我的代码。只是为了利用贝壳 变量如
$NUMBER\u FILTER\u FILE
等。我已附上
awk
脚本 使用双引号,而不是单引号。这可能不是一个标准方案,因为 它需要很多带有反斜杠的转义。(我本该通过的 通过
-v
选项显示外壳变量。)

  • BEGIN
    块在读取输入行和 我已将
    |
    分配给输入和输出字段分隔符
  • 其余代码在文件中的每个输入行上执行 指定为命令行参数
  • 模式
    FILENAME==“$NUMBER\u FILTER\u FILE”
    测试当前 输入文件是
    $NUMBER\u FILTER\u file
    和下面的块
    {…}
    如果模式匹配,则执行
  • 输入行中的单词将自动分配给
    awk
    变量
    $0
    。然后,
    $NUMBER\u FILTER\u文件的第一行作为
    
    phone[“5553690049”]++
    在上面做上如上所述的标记
  • 以下代码基于内容设置每个关联数组 每个文件的名称
  • 最后一行以
    FILENAME==“$WORKING$UNFILTEREDOUTPUTFILE”
    是迭代csv文件的主循环。这条线被分成两部分 用
    |
    分隔的字段,这些字段分配给
    $1
    $2
    。。。 按顺序
  • 如果
    if(…)
    测试为真,则第8个字段
    $8
    设置为
    “N”

希望这能有所帮助。

如果我理解正确,以下几点就行了:

awk "
BEGIN {FS = OFS = \"|\"}
FILENAME=="\"$NUMBER_FILTER_FILE\"" {phone[\$0]++; next}
FILENAME=="\"$GROUP_FILTER_FILE\"" {group[\$0]++; next}
FILENAME=="\"$ENTERPRISE_FILTER_FILE\"" {enterprise[\$0]++; next}
FILENAME=="\"$WORKING$UNFILTEREDOUTPUTFILE\"" {
    if (phone[\$6] || group[\$3] || enterprise[\$2]) \$8 = \"N\"
    print
}" "$NUMBER_FILTER_FILE" "$GROUP_FILTER_FILE" "$ENTERPRISE_FILTER_FILE" "$WORKING$UNFILTEREDOUTPUTFILE" > "$WORKING$OUTPUTFILE"
恐怕你贴的例子没有提供足够的信息 (例如,
1000002644
行的第二个字段与任何行都不匹配 在
企业
)中,我做了一些假设。 如果我的代码不能正常工作,如果你能发布我的代码,我将不胜感激 查看我的代码的更多信息。比尔

[解释]
关键是我们如何减少计算量。你的原创 代码在中反复扫描筛选文件以查找关键字 主回路冗余且效率低下。我们可以彻底地 通过在
awk
中使用
关联数组
来减少冗余。 (请注意,相同的机制在许多语言中使用 不同的名称:
Perl中的
hash
Python中的
dictionary
,等等)

让我用中的第一行
FunGRP\u 100009280\u OT
进行说明
$GROUP\u FILTER\u文件
。通过使用关联数组在单词上做标记 通过说
组[“FunGRP\u 100009280\u OT”]++
,我们可以在后记中测试 单词以最小的计算成本包含在列表中

现在让我们回到我的代码。只是为了利用贝壳 变量如
$NUMBER\u FILTER\u FILE
等。我已附上
awk
脚本 使用双引号,而不是单引号。这可能不是一个标准方案,因为 它需要很多带有反斜杠的转义。(我本该通过的 通过
-v
选项显示外壳变量。)<
import pandas as pd

# Load all the data
data = pd.read_csv('input.csv', sep="|", names=['site', 'entreprise', 'group', 'mail', 'name', 'phone', 'a', 'yn', 'b', 'c', 'd', 'e', 'f'])
phones = pd.read_csv('phones.dat', header=None)
entreprises = pd.read_csv('entreprises.dat')
groups = pd.read_csv('groups.dat')

# Define the match function
def match(row):
    return row['phone'] in phones.values or row['entreprise'] in entreprises.values or row['group'] in groups.values

# Update the column with match function
data['yn'] = data.apply(match, axis=1)

# Write output
data.to_csv('output.csv', sep="|", header=False, index=False)