比较两个大的 CSV 文件(每个 90GB)并输出到另一个 csv

比较两个大的 CSV 文件(每个 90GB)并输出到另一个 csv

这是来自的后续问题对大型 CSV 文件 (90GB) 进行排序,超出磁盘配额。所以现在我有两个 CSV 文件排序,如 file1.csv 和 file2.csv 每个 CSV 文件有 4 列,例如

文件1:

ID Date Feature Value
01 0501 PRCP    150
01 0502 PRCP    120
02 0501 ARMS    5.6
02 0502 ARMS    5.6

文件2:

ID Date Feature Value
01 0501 PRCP    170
01 0502 PRCP    120
02 0501 ARMS    5.6
02 0502 ARMS    5.6

理想情况下,我想以这样的方式比较这两个文件:如果两个文件中的两行具有相同的 ID、日期和功能,但值不同,则输出如下所示的内容:

ID Date Feature Value1 Value2

当然,这个要求可能有点过高。就像是

ID1 Date1 Feature1 Value1       ID2 Date2 Feature2 Value2

也有效。在上面的例子中,我想输出

01 0501 PRCP    150 170

或者

01 0501 PRCP    150 01 0501 PRCP    150

我认为主要问题是如何以这种方式进行比较以及如何输出到 csv 文件。谢谢。

Gilles 答案的示例输出:comm 的输出是

$ head -20 comm_output.txt ACW00011604,19490101,PRCP,0 AE000041196,20070402,TAVG,239 AE000041196,20070402,TAVG,244 AE000041196,20080817,TMIN,282 AE000041196,20130909,TAVG,350 AE000041196,20130909,TMAX,438 AE000041196,20130909,TMIN,294 AE000041196,20130910,TAVG,339 AE000041196,20130910,TAVG,341 AE000041196,20150910,TAVG,344awk 的输出是

$ head awk_output.csv , ACW00011604,19490101,PRCP,0,,, AE000041196,20070402,TAVG,239,,, AE000041196,20070402,TAVG,244,,, AE000041196,20080817,TMIN,282,,, AE000041196,20130909,TAVG,350,,, AE000041196,20130909,TMAX,438,,, AE000041196,20130909,TMIN,294,,, AE000041196,20130910,TAVG,339,,, AE000041196,20130910,TAVG,341,,, AE000041196,20150910,TAVG,344,,, 如果您坚持的话,这是示例输入

head file1.csv

ACW00011604,19490101,PRCP,0 ACW00011604,19490101,SNOW,0 ACW00011604,19490101,SNWD,0 ACW00011604,19490101,TMAX,289 ACW00011604,19490101,TMIN,217 ACW00011604,19490102,PRCP,30 ACW00011604,19490102,SNOW,0 ACW00011604,19490102,SNWD,0 ACW00011604,19490102,TMAX,289 ACW00011604,19490102,TMIN,228

 head file2.csv

ACW00011604,19490101,SNOW,0 ACW00011604,19490101,SNWD,0 ACW00011604,19490101,TMAX,289 ACW00011604,19490101,TMIN,217 ACW00011604,19490102,PRCP,30 ACW00011604,19490102,SNOW,0 ACW00011604,19490102,SNWD,0 ACW00011604,19490102,TMAX,289 ACW00011604,19490102,TMIN,228 ACW00011604,19490102,WT16,1

答案1

让我们回顾一下以某种方式将两个文件逐行组合在一起的工具:

  • 粘贴逐行组合两个文件,而不注意内容。
  • 通讯合并排序的文件,注意相同的行。这可以清除相同的行,但随后组合不同的行将需要不同的工具。
  • 加入组合排序的文件,将相同的字段匹配在一起。
  • 种类可以合并两个文件。
  • awk 可以根据您给它的任何规则组合多个文件。但对于如此大的文件,您可能通过使用最合适的专用工具而不是通用工具来获得最佳性能。

我假设没有重复项,即在一个文件中不存在具有相同 ID、日期和功能的两行。如果存在重复项,那么如何处理它们取决于您想要如何处理它们。我还假设文件已排序。我还假设你的外壳有流程替代,例如 bash 或 ksh 而不是普通的 sh,并且您有 GNU coreutils(非嵌入式 Linux 和 Cygwin 就是这种情况)。

我不知道你的分隔符是空格还是制表符。我假设有空格;如果分隔符始终恰好是一个制表符,则将制表符声明为分隔符(cut -d $'\t', join -t $'\t', sort -t $'\t')并使用 \t 代替应该[ \t]\+会压缩一点点性能。

将区域设置设置为纯 ASCII ( LC_ALL=C) 以避免与多字节字符相关的任何性能损失。

由于join只能根据一个字段组合行,因此我们需要将字段 1-3 安排为单个字段。为此,请更改分隔符,可以在 1 和 2 之间、2 和 3 之间或在 3 和 4 之间。我将更改 1–3 以;代替空格。这样您就可以获得所有线路组合,无论它们是否相同。然后,您可以使用 sed 删除具有相同值的行。

join -a 1 -a 2 <(sed 's/[ \t]\+/;/; s/[ \t]\+/;/' file1.csv) <(sed 's/[ \t]\+/;/; s/[ \t]\+/;/' file2.csv) |
sed '/[ \t]\(.*\)[ \t]\+\1$/d' |
tr ';' '\t'

请注意,不可配对的行最终会形成 4 列行,并且不会指示它们是来自文件 1 还是文件 2。删除-a 1 -a 2以抑制所有不可配对的行。

如果你有大部分相同的行,这会浪费时间连接它们并清除它们。另一种方法是使用comm -3来清除相同的行。这会生成一个输出流,其中各行按顺序排列,但文件 2 中的行有一个前导制表符。然后,您可以使用 awk 合并两个文件具有相同字段 1-3 的连续行。由于这涉及到 awk,如果有很多不同的行,它最终可能会变慢。

comm -3 file1.csv file2.csv |
awk '
    $1 "\t" $2 "\t" $3 == k { if ($4 != v) print k "\t" v "\t" $4; next; }
    { print k "\t" v }
    { k=$1 "\t" $2 "\t" $3; v=$4; }
'

答案2

我找到了一种快速比较 2 个包含 100 万行的文件的方法。我的需要是两个文件必须相同。 diff 命令很慢,但为了使其更快,只需在比较文件之前对文件进行排序即可。

所以基本上:

sort file01.txt > file01_sorted.txt
sort file02.txt > file02_sorted.txt

然后运行 ​​diff 命令:

diff file01_sorted.txt file02_sorted.txt

或者您也可以对排序后的文件执行 md5sum

md5sum file01_sorted.txt
md5sum file02_sorted.txt

答案3

编辑:这个答案可能适合任何有周围的人系统上有 200GB 可用 RAM。哎呀。


diff --side-by-side --suppress-common-lines file1.csv file2.csv

默认用|, 和 130 个字符的宽线分隔(如果需要,可以换行)。我猜这与您的输入格式不一样。

你可以尝试类似的东西

diff --old-line-format="%l$(printf '\t')" --new-line-format="%L" --unchanged-line-format="" file1.csv file2.csv

相关内容