这是Sort large CSV files (90GB), Disk quota exceeded的后续问题。所以现在我有两个 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,344
awk 的输出是
$ 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
让我们回顾一下以某种方式将两个文件逐行组合在一起的工具:
我假设没有重复,即在一个文件中没有两行具有相同的 ID、日期和功能。如果有重复,那么如何处理它们取决于你想如何对待它们。我还假设文件已排序。我还假设您的 shell 具有进程替换,例如 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; }
'
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句