网络中的正则表达式字段匹配和替换-Python

Regex Field Match and Replace in a network-Python

提问人:Jackson Dunn 提问时间:10/6/2023 更新时间:10/17/2023 访问量:64

问:

我有一个大的 csv(+1000000 行),我需要对其进行正则表达式搜索和替换功能。简而言之,我需要取两列并找到它们之间的匹配项;然后使用匹配的行将第三个字段中的值替换为匹配的行。它基本上将网络中的某些组件与其上游组件相匹配。下面是一个简化的小示例:

OID的 集会 上游 字段 1
1 ABC123系列 1
2 def456型 ABC123系列 2
3 GHI789型 JKL101型 3
4 JKL101型 4

这将是预期的结果:

OID的 集会 上游 字段 1
1 ABC123系列 1
2 def456型 ABC123系列 1
3 GHI789型 JKL101型 4
4 JKL101型 4

如您所见,在“assembly”字段中出现的任何具有上游值的行都会获得与其上游邻居相等的 Field1 值。

我有一个完全可用但非常慢(写入速度约为 15kb/s)的代码,我目前正在使用 python 中的正则表达式模块。我的问题是,有什么更有效的方法呢?由于 ram 大小有限,Pandas 是不可能的,除了 csv 之外的其他数据格式也是如此。过去我尝试过 dask,但从未让它正常工作,可能是因为在我(非常)受限的 IT 条件下 - 我无法访问 python 中的环境路径变量。

代码如下:

import csv
import re

#csv files
input_file = 'L:\\Dev_h\\Device Heirarchy\\fulljoin_device_flow2.csv'
output_file = 'L:\\Dev_h\\Device Heirarchy\\output2.csv'

# output fields
output_fields = ['gs_attached_assembly_guid', 'gs_upstream_aa_guid', 'Field1_num','Dev_no', 'gs_guid', 'gs_display_feature_guid', 'field2', 'gs_network_feature_name', 'gs_assembly_guid', 'gs_display_feature_name', 'Field1', 'gs_network_feature_guid', 'OID_']


with open(input_file, 'r', newline='') as in_csv, open(output_file, 'w', newline='') as out_csv:
    reader = csv.DictReader(in_csv)
    writer = csv.DictWriter(out_csv, fieldnames=output_fields)
    writer.writeheader()

    # Build Regex
    patterns = {row['gs_attached_assembly_guid']: row['Field1_num'] for row in reader}
    pattern = re.compile('|'.join(map(re.escape, patterns.keys())))

    # restart loop
    in_csv.seek(0)
    next(reader) # Skip header row

    #for loop allowing pattern matching
    for row in reader:

        # Step 6: Define a function to search the 'gs_upstream_aa_guid' column using the regex pattern
        def search_and_replace(match):
            matched_guids = match.group().split(',')
            replacement_values = []
            for matched_guid in matched_guids:
                if matched_guid in patterns and patterns[matched_guid] != '':
                    replacement_values.append(patterns[matched_guid])
                else:
                    # Return an empty string instead of the gs_attached_assembly_guid
                    replacement_values.append('')

            return ','.join(replacement_values)

        # check for matches in 'gs_upstream_aa_guid' value
        match = pattern.search(row['gs_upstream_aa_guid'])

        #If there is a match, replace the 'Field1_num' value with the matched value
        if match:
            row['Field1'] = search_and_replace(match)
        #Otherwise skip
        else:
            pass

        #Write the updated row out to the output CSV
        writer.writerow(row)

print("End")

那么问题来了,如何加快这一过程呢?

python 正则表达式 优化 替换 dask

评论

1赞 Good Night Nerd Pride 10/6/2023
为什么还要为此使用正则表达式?改为执行 2 次传递:首先构建一个从值到值的字典/映射。然后再次读取 CSV 并使用字典/映射更新值。assemblyOIDField1
0赞 Jackson Dunn 10/6/2023
@GoodNightNerdPride在过去两个月左右的时间里,我自学了python,但不知道这是一种选择。你能解释一下我吗?

答:

0赞 Aurelien 10/6/2023 #1

无需构建大型正则表达式,只需删除和替换即可

match = pattern.search(row['gs_upstream_aa_guid'])

match = row['gs_upstream_aa_guid'] in patterns

正则表达式可以很快,但绝不会像检查字典中是否存在值那样快,因为复杂度O(1)。

O(1) 表示检查包含 1 个值的字典中是否存在值与检查包含 1,000,000 个值的字典中是否存在值一样快。

评论

0赞 Jackson Dunn 10/7/2023
这是个好主意,但它给我带来了一个错误:Bool 对象没有属性组。我会尝试调试,看看它是否有效,谢谢!
1赞 Nick 10/7/2023 #2

更新

由于内存限制,您无法使用 pandas,因此最简单的方法是在读取 csv 的第一遍中构建一个替换字典,然后在第二遍中使用它来替换值。使用您的代码作为起点,针对问题中的示例数据进行了修改:Field1

output_fields = ['OID', 'assembly', 'upstream', 'Field1']

with open(input_file, 'r', newline='') as in_csv, open(output_file, 'w', newline='') as out_csv:
    reader = csv.DictReader(in_csv)
    writer = csv.DictWriter(out_csv, fieldnames=output_fields)
    writer.writeheader()
    
    # Build replacements dict
    reps = { row['assembly'] : row['Field1'] for row in reader }
    
    # restart loop
    in_csv.seek(0)
    next(reader) # Skip header row
    
    for row in reader:
        # update if required
        # use dict.get to allow keeping the original value when no replacement
        row['Field1'] = reps.get(row['upstream'], row['Field1'])
        # Write the updated row out to the output CSV
        writer.writerow(row)

示例数据的输出:

OID,assembly,upstream,Field1
1,abc123,,1
2,def456,abc123,1
3,ghi789,jkl101,4
4,jkl101,,4

原始答案

你可以只使用 pandas,使用合并将值与值匹配并获取适当的值:upstreamassemblyField1

df = pd.read_csv(input_file)
df['Field1'] = (df
    .merge(df, left_on='upstream', right_on='assembly', how='left')['Field1_y']
    .fillna(df['Field1'])
     # necessary because the presence of NaN after the merge changes type to float
    .astype(int)
)
df.to_csv(output_file, index=False)

评论

0赞 Jackson Dunn 10/9/2023
不幸的是,由于体积大,pandas 只能以块的形式工作,它正在最大化内存。我可以分块完成,但我必须构建一个字典,因为从技术上讲,可以在数据集的任何一行中找到匹配项。
0赞 Nick 10/16/2023
@JacksonDunn很抱歉回复缓慢,一直在度假。1M 行对熊猫来说应该不是问题,你系统中有多少 RAM?在读取期间或合并期间是否耗尽了 RAM?
0赞 Jackson Dunn 10/16/2023
我有 8gb,但行很大。该文件远远超过 1.2GB,我的电脑是烤面包机:(
0赞 Nick 10/16/2023
@JacksonDunn可以成功地将文件读入 pandas?
1赞 Nick 10/17/2023
@JacksonDunn啊,好吧,熊猫绝对不是解决方案:-P请看我根据你的原始代码编辑。这与Aurelien的回答采用相同的方法