在 Python 中比较两个 csv 文件并在新的 csv 文件中返回匹配结果而不重复

问题描述 投票:0回答:4

我有两个 csv 文件,一个名为 web_file 的文件有 25,000 行,另一个名为 inv_file 的文件包含 320,000 行。

我需要通读 web_file 的第 1 列中的每一行,并从 inv_file 的第 1 列中的每一行中找到所有匹配值,并将 inv_file 中的行写入新的 csv 文件中。

使用只有 5-10 行的示例文件也没有显示问题,所以我在下面列出了一堆随机数。

示例 web_file:

Inv_SKU,Web_SKU,Brand,Barcode
225481-34,225481-34,brand1,987654321
0486592,0486592,brand2,654871233
AB56412,AB56412,brand2,651273214
LL-123456,LL-123456,brand3,748912349
JLPD-65,JLPD-65,brand6,341541648
20143966,20143966,brand3,82193714
39585824,39585824,brand5,36837329
78066099,78066099,brand4,98398987
44381051,44381051,brand1,9090428
86529443,86529443,brand4,6861670
DF 5645 12,DF 5645 12,brand1,489456138
9845671325,9845671325,brand4,498451315
59634923,59634923,brand4,35828574
85290760,85290760,brand2,64562216
41217184,41217184,brand4,12816236
AE48915,AE48915,brand1,342536125
93981723,93981723,brand2,58155601

示例inv_file:

Inv_SKU,Web_SKU,Brand,Barcode
0486592,0486592,brand2,654871233
LL-123456,LL-123456,brand3,748912349
9845671325,9845671325,brand4,498451315
OI3248967,OI3248967,brand2,891513211
AB56412,AB56412,brand2,651273214
DF 5645 12,DF 5645 12,brand1,489456138
225481-34,225481-34,brand1,987654321
123456789,123456789,brand5,654986413
9841531,9841531,brand3,543254512
AE48915,AE48915,brand1,342536125
JLPD-65,JLPD-65,brand6,341541648
MMMM,MMMM,brand7,384941542
23481-4323,23481-4323,brand3,489123157
98451321,98451321,brand4,498121354
23454152,23454152,brand2,894165123
10275690,10275690,brand2,25612670
20143966,20143966,brand3,82193714
59634923,59634923,brand4,35828574
65800253,65800253,brand5,72318134
67722613,67722613,brand6,93290033
92617199,92617199,brand7,95078073
15379652,15379652,brand1,56281224
85290760,85290760,brand2,64562216
78066099,78066099,brand4,98398987
41217184,41217184,brand4,12816236
87152990,87152990,brand4,95058925
73813369,73813369,brand1,2395994
50201544,50201544,brand1,9167830
93981723,93981723,brand2,58155601
39585824,39585824,brand5,36837329
29082963,29082963,brand3,23393947
23856043,23856043,brand8,57295562
74249006,74249006,brand8,83219065
94376071,94376071,brand8,94887004
14553763,14553763,brand8,14223230
44381051,44381051,brand1,9090428
7598085,7598085,brand1,48967969
56383025,56383025,brand2,68864452
44338055,44338055,brand4,47043853
86529443,86529443,brand4,6861670

我尝试使用这段代码,但最终得到了很多重复的行,我想避免这种情况,因为我实际使用的文件太大了,我最终得到了数百万行。

with open('inv_file.csv', 'r') as f1, open('web_file.csv', 'r') as f2:
    inv_file = f1.readlines()
    web_file = f2.readlines()


with open('result.csv', 'r+') as f3:
    result_file = f3.readlines()

    while len(result_file) < len(web_file):
        for row in inv_file:
            for row1 in web_file:
                if row[0] in row1[0]:
                    f3.write(row1)
        break
python csv file comparison string-matching
4个回答
0
投票

你真的应该使用 csv 库来解析 csv 文件。一种方法是存储网络 skus 列表(希望我的方法正确),然后对照它检查 inv skus。这可以通过传递给 csv

writerows()
方法的生成器有效地完成。

import csv
with open('inv_file.csv', 'r') as f1, open('web_file.csv', 'r') as f2, open('result.csv', 'w') as f3:
    web_skus = [row[0] for row in csv.reader(f2)]
    # web_skus = set([row[0] for row in csv.reader(f2)])  # uncomment to remove dupliate web skus
    inv_file = csv.reader(f1)
    rows = (row for row in inv_file if row[0] in web_skus)

    writer = csv.writer(f3)
    writer.writerows(rows)

0
投票

while
循环看起来很混乱而且没有必要。为什么你不只是做简单明显的事情?

import csv

with open('inv_file.csv', 'r') as f1, \
     open('web_file.csv', 'r') as f2, \
     open('result.csv', 'a') as f3:
  inv = [x[0] for x in csv.reader(f1)]
  writer = csv.writer(f3)
  for row in csv.reader(f2):
    if row[0] in inv:
        writer.writerow(row)

演示:https://ideone.com/g6j2lB

不清楚您为什么对输出文件使用

'r+'
模式,或者您是否希望我们也抑制文件中已有行的输出行。如果这是您的要求,也许可以提出一个包含更多详细信息的新问题,以及您在此处实际询问的问题的这个(或另一个)解决方案。


0
投票

我有两个想法可以解决你的问题。

1号: 在写

之前添加检查
row1
是否在
result_file

if row[0] in row1[0]:
    if row1 not in result_file:
        f3.write(row1)
        

请注意,您已经解析的值越多,这将花费更多时间。

2号: 写入后将

row1
添加到集合中,并在写入前检查
row1
是否在此集合中

written = set()
...
if row[0] in row1[0]:
    if row1 not in written:
        f3.write(row1)
        written.add(row1)

此版本可能更快(我不确定)但具有更高的存储需求,因为所有行都在集合和结果文件中。

如果可以只比较 SKU 编号,您也可以在两种情况下都使用它们,这应该更快,如果 2 也应该占用更少的存储空间。


0
投票

我要调用 web_file 你的 filter CSV 和 inv_file 你的 input CSV.

我模拟了一个包含 25_000 行的过滤器 CSV 和一个包含 320_000 行的输入 CSV。然后我尝试了将所有过滤器 ID 添加到列表的方法,然后遍历输入行并检查每个输入 ID 是否在该过滤器列表中,以及是否正在写入输出。

import csv

with open("filter.csv", newline="") as f_in:
    reader = csv.reader(f_in)
    next(reader)  # discard header

    filter_ids: list[str] = []
    for row in reader:
        filter_ids.append(row[0])


with (
    open("input.csv", newline="") as f_in,
    open("output.csv", "w", newline="") as f_out,
):
    reader = csv.reader(f_in)
    writer = csv.writer(f_out)

    writer.writerow(next(reader))

    for row in reader:
        if row[0] in filter_ids:
            writer.writerow(row)

运行大约需要 70 秒。

程序最多只能进行

25_000 x 320_000 = 8_000_000_000
(“8billion”)比较。通过使用字典来保存过滤器 ID,我们可以将其减少到只有 320_000 次比较。

...
    ...
    filter_ids: dict[str, None] = {}
    for row in reader:
        filter_ids[row[0]] = None

我们不必更改输入的实际过滤,相同的

if row[0] in filter_ids:
语法适用于字典。

运行时间为 0.13 秒,速度提高了 500 多倍。一般来说,在字典中查找键比检查一个项目是否在列表中快得多,尤其是对于大列表。在我的机器上,字典方法比列表方法多使用了大约 3MB 的内存。

您在输出中提到了重复的行。我在示例输入中没有看到重复的行,但是如果您需要检查以确保 ID 在输出中没有重复,您可以再次使用字典:

...
    ...
    output_ids: dict[str, None] = {}
    for row in reader:
        id_ = row[0]
        if id_ not in output_ids and id_ in filter_ids:
            writer.writerow(row)
            output_ids[id_] = None
© www.soinside.com 2019 - 2024. All rights reserved.