根据列中的匹配项合并 2 个 CSV 文件,而不考虑标题行


Combine 2 CSV files based on a match within a column disregarding the header row

我一直在 ole 互联网上搜索这个解决方案,但没有找到任何成功的东西。 我有一个来自一个脚本的 CSV 输出,该脚本以特定方式呈现数据,我需要匹配该脚本并与另一个文件合并。如果我能四舍五入到简单的 2 x 小数点,则增加了奖金。

文件 1

:dataset1.csv(使用第 1 列作为主键或我想搜索其他文件的内容。

5033db62b38f86605f0baeccae5e6cbc,20.875,20.625,41.5
5033d9951846c1841437b437f5a97f0a,3.3529411764705882,12.4117647058823529,13.7647058823529412
50335ab3ab5411f88b77900736338bc6,6.625,1.0625,3
5033db62b38f86605f0baeccae5e6cbc,2.9375,1,1.4375
文件 2

:数据集 2.csv(如果第 2 列与文件 2 中的文件连接列 1 的第 1 列匹配,则替换文件 1 第 1 列中的数据。

"dc2","5033db62b38f86605f0baeccae5e6cbc"
"dc1","5033d9951846c1841437b437f5a97f0a"

期望的结果:

文件

1(或新文件 3):

dc1,3.35,12.41,13.76
dc2,20.875,20.625,41.5

只是为了证明我一直在努力寻找一种方法,而不仅仅是随机问一个问题,希望别人能解决我的问题。

我找到了很多资源说使用join。

join -o 1.1,1.2,1.3,1.4,2.3 file 1 file 2等 我已经用许多不同的方法对此进行了测试。 我读到一些帖子,结果需要排序 - 这么长的字符串有点困难。 更不用说文件 1 可能有 30 到 40 个条目,但文件 2 可能只有 10 个条目。 我只需要一个与长字符串相关联的名称。

我开始研究 grep - 但随后我将需要一个 forEach 循环来循环浏览所有结果,并且必须有一种更简单的方法。

我也看过AWK - 现在这是一个有趣的,试图弄清楚如何做到这一点。

awk 'FNR==NR {a[$2]; next} $2 in a' file.csv testfile2.csv

是的。。。。尝试了很多方法来进行比较,因为这似乎是一般的想法......但仍然没有让它工作。 我希望这是某种类型的 shell 脚本,用于 linux 非常简单,我可以从 php 页面调用并运行它。 就像用户点击刷新一样,它会搅动它并消化数据。

任何帮助将不胜感激!

谢谢。

J.

你可以使用 sort 和 gnu awk 的组合:

mergef.awk:

BEGIN   { FS= "[ ,'"]+"; }
FNR == NR { if ( !($1 in vals) ) vals [ $1 ] = sprintf("%.2f,%.2f,%.2f", $2, $3,$4) ;}
FNR != NR { print $2 "," vals[ $3 ]; }

假设您的文件是 f1.csv 和 f2.csv然后使用以下命令:

awk -f mergef.awk f1.csv f2.csv | sort
  • 脚本中的第一行处理第二个文件中存在的引号(由于此设置,第二个文件有一个空字段$1
  • 第二行在第一个文件中读取。if注意只使用第一次出现的密钥。
  • 最后一行打印第二个文件中的新键以及第一个文件中存储的值,通过旧键检索
  • 第一个文件FNR == NR为 true

使用 pythonpandas 库:

import pandas as pd
# Read in the csv files.
df1 = pd.read_csv(dataset1.csv, header=None, index_col=0)
df2 = pd.read_csv(dataset2.csv, header=None, index_col=1)
# Round values in the first file to two decimal places.
df1 = df1.round(2)
# Merge the two files.
df3 = pd.merge(df2, df1, how='inner', left_index=True, right_index=True)
# Write the output.
df3.to_csv(output.csv, index=False, header=False)

除了格式化数字之外,这就可以完成工作

$ join -t, -1 1 -2 2 -o2.1,1.2,1.3,1.4 <(sort file1) <(tr -d '"' <file2 | sort -t, -k2)
dc1,3.3529411764705882,12.4117647058823529,13.7647058823529412
dc2,2.9375,1,1.4375
dc2,20.875,20.625,41.5

请注意,DC2 有两个匹配项。

奖励:对于所需的格式管道,将前一个脚本的输出到

$ ... | tr ',' ' ' | xargs printf "%s,%.2f,%.2f,%.2f'n"
dc1,3.35,12.41,13.76
dc2,2.94,1.00,1.44
dc2,20.88,20.62,41.50

但是,也许awk是一个更好的选择。 这是为了表明,如果您可以使用现有的 unix 工具集,则无需编程。

这是PHP的解决方案:

foreach (file("dataset1.csv") as $line_no => $csv) {
    if (!$line_no) continue; // in case you have a header on first line
    $fields = str_getcsv($csv);
    $key = array_shift($fields);
    $data1[$key] = array_map(function ($v) { return number_format($v, 2); }, $fields);
};
foreach (file("dataset2.csv") as $csv) {
    $fields = str_getcsv($csv);
    if (!isset($data1[$fields[1]])) continue;
    $data2[$fields[0]] = array_merge(array($fields[0]), $data1[$fields[1]]);
};
ksort($data2);
$csv = implode("'n", array_map(function ($v) {
    return implode(',', $v);
}, $data2));
file_put_contents("dataset3.csv", $csv);

注意:正如您提到的,第一个文件将使用第 1 列作为主键,因此不应出现重复的键值。如果是这样,则以最后一次出现为准。