通过多列中的非唯一值比较两个csv文件,输出到匹配的csv额外数据

时间:2017-07-10 18:04:47

标签: python csv awk

我的搜索字词用完了,已经完整了。我已经阅读并尝试了大量的问题和答案,我认为这是一个相当简单的任务,但没有快乐。

我有两个csv文件。

**file1.csv** (2,000 + lines)<br/>
product_code, colour, size, sku, more cols..., barcode<br/>
<span style="color: red">
item98, red, XL, adfd56384678, ..., null<br/>
item99, black, L, adfgk9087467, ..., null<br/>
item98, red, S, adfgad240568, ..., null<br/>
</span>

**file2.csv** (20,000 + lines)<br/>
ITEM_CODE, COLOUR, SIZE, BAR_CODE<br/>
<span style="color: red">
item98, RED, XL, 090900887<br/>
item98, RED, S, 43581034<br/>
item97, BLUE, M, 519685371<br/>
</span>

我需要输出:

**matched-result.csv** (2,000 + lines)<br/>
product_code, colour, size, sku, more cols..., barcode<br/>
<span style="color: red">
item98, red, XL, adfd56384678, ..., 090900887<br/>
item99, black, L, adfgk9087467, ..., null<br/>
item98, red, S, adfgad240568, ..., 519685371<br/>
</span>

sku和条形码是唯一值,只能通过匹配product_code,大小和颜色来识别。我需要在file1.csv末尾的新列中使用条形码。我的第一次成功尝试是使用awk。

<pre>
awk -F',' -v OFS=, 'NR==FNR{a[$1,tolower($2),$3]=$4;next}{if( b = a[$1,tolower($2),$3]){ print $1,$2,$3,$4,b }}' file1.csv file2.csv > matched-result.csv
</pre>

我努力输出整个 file1.csv 的结果,尝试打印$ 0,b ,这会创建一个值为b的新行。我也希望所有file1.csv输出都为非匹配的空值和尽可能的头。我必须对齐列以匹配此awk工作方法,但原始字段不对齐。这没什么大不了的,但我想知道是否有必要解决这个问题?

之后我尝试使用python脚本将 file1.csv 与awk输出文件 matched-result.csv 合并。

    import csv
    from collections import OrderedDict

    with open('file1.csv') as f:
        r = csv.reader(f, delimiter=',')
        dict1 = {row[0]: row[1:] for row in r}

    with open('matched-result.csv') as f:
        r = csv.reader(f, delimiter=',')
        dict2 = OrderedDict((row[0], row[1:]) for row in r)

    result = OrderedDict()
    for d in (dict1, dict2):
        for key, value in d.iteritems():
            result.setdefault(key, []).extend(value)

    with open('desired-result.csv', 'wb') as f:
        w = csv.writer(f)
        for key, value in result.iteritems():
            w.writerow([key] + value)

输出不是所需的结果。订单错误,记录的数量与 file1.csv 完全匹配还有一行?这个两步法似乎脱节了,如果做得好,感觉就像其中一个可以做到这一点?

我已经尝试过csvkit来加入/合并文件,但输出20,000 +行,其中一些是重复的。我认为它将product_code / ITEM_CODE列视为唯一值,但它们并非如此。我已经考虑过加入和grep,但它们似乎也不是答案。

我已经安装了panda和powerShell,并愿意给他们一个去,但不知道从哪里开始,那里需要明确的指示。哦,我和所有这些语言和程序都是菜鸟,但并不是完全湿透了。

希望我已经提供足够的信息继续下去。我会发布链接到我读过的帖子。你可以把它的工作用于它的90%以上。

请回复,提供代码示例,最好不要使用全新的语言或方法。

//更新

我已经投票赞成@zwer回答,因为它或多或少地在方框栏中进行了一次小调整,请参阅评论。但值得指出的是@RavinderSingh13 awk方法同样有效。当/如果我收到回复并更新此位,我将采用@acushner熊猫方法。

如果有人关心,我认为这是最好的方法吗? Tomatoto,tomarto,不确定是否适用于印刷品。就个人而言,python更容易阅读。 awk很好而且简短,我认为它是一种基于正则表达式的语言,我觉得它更难以理解并且学习曲线更陡峭。

感谢所有评论过的人。我很久以来一直在努力解决这个问题,并将其排在这里。

3 个答案:

答案 0 :(得分:1)

我认为您的输出存在一些不一致,理想情况下,如果我们考虑将$ 1,$ 2和$ 3作为file2.csv文件的索引,则显示输出的最后一行不应该存在。

如果是,这是一个错字,那么以下可能会帮助你。

awk -F", " 'FNR==NR{;a[$1,tolower($2),$3]=$NF;next} (($1,$2,$3) in a){$NF=a[$1,$2,$3]} 1' file2.csv file1.csv

编辑:由于OP在Input_file中控制了M个字符,因此现在也添加了以下内容。

awk -F", " '{gsub('/\r/',"")} FNR==NR{;a[$1,tolower($2),$3]=$NF;next} (($1,$2,$3) in a){$NF=a[$1,$2,$3]} 1' file2.csv file1.csv

答案 1 :(得分:0)

一种简单而通用的Python方法:

JSON.parse(`{"person": {
    "name": "John",

    "lastname": "Doe",
    "interests": [
        "hiking",
        "skiing"
    ],
    "age": 40
}}`);

如果import csv # load the second file in memory as we need it for a barcode lookup barcodes = {} # will hold a map for our barcode values with open("file2.csv", "rU") as f: # open file2.csv for reading reader = csv.reader(f) # create a CSV reader, you may need to tweak its options next(reader) # skip the header for row in reader: # loop it row by row # store code:color:size map as a tuple in our barcodes map barcodes[(row[0], row[1].lower(), row[2])] = row[3] # now you can get a barcode from it like: barcodes[("item98", "red", "XL")] # open file1.csv for reading and matched-result.csv for writing with open("file1.csv", "rU") as f, open("matched-result.csv", "w") as out: reader = csv.reader(f) # create a CSV reader, you may need to tweak its options header = next(reader) # get the header indexes = {column: index for index, column in enumerate(header)} # map column indexes writer = csv.writer(out, lineterminator="\n") # create a CSV writer for our output writer.writerow(header) # write the same header to the output file for row in reader: # loop through file1.csv rows # create a search map entry search = (row[indexes["product_code"]], row[indexes["colour"]], row[indexes["size"]]) if search in barcodes: # we have a barcode entry row[indexes["barcode"]] = barcodes[search] # update it in the current row writer.writerow(row) # write the potentially modified row to our output file 列的顺序可能不同,则必须确保映射部分(file2.csv)与列顺序匹配,即

编辑 - 由于我以前没有测试过,我写了一个简单的测试,看看它是否正常工作:

barcodes[(row[0], row[1].lower(), row[2])] = row[3]

product_code,colour,size,sku,added_col,barcode
item98,red,XL,adfd56384678,1,null
item99,black,L,adfgk9087467,2,null
item98,red,S,adfgad240568,3,null

file1.csv

ITEM_CODE,COLOUR,SIZE,BAR_CODE
item98,RED,XL,090900887
item98,RED,S,43581034
item97,BLUE,M,519685371

导致file2.csv包含:

product_code,colour,size,sku,added_col,barcode
item98,red,XL,adfd56384678,1,090900887
item99,black,L,adfgk9087467,2,null
item98,red,S,adfgad240568,3,43581034

如果您的CSV文件遵循问题中提供的结构,就像这样,它应该可以正常工作。我可能在您的数据中看到的唯一问题是在用逗号分隔值之后有一个空格。如果您的数据确实如此,请将matched-result.csv添加到每个skipinitialspace=True的参数中(例如csv.reader())以占用额外的空间。正如我在代码中所写的那样,您可能需要调整reader = csv.reader(f, skipinitialspace=True)选项以匹配您的CSV方言。

答案 2 :(得分:0)

使用pandas非常简单。你会做这样的事情:

import pandas as pd

df_sku = pd.read_csv('file1.csv', index_col=False)
df_bc = pd.read_csv('file2.csv', index_col=False)
res = df_sku.merge(df_bc, left_on=['product_code', 'colour', 'size'], right_on=['ITEM_CODE', 'COLOUR', 'SIZE'])

试一试,让我知道你是怎么做出来的。另外,如果你要做一堆csv的东西,pandas是值得学习的。{/ p>