Csv

區分兩個大型 CSV 文件(每個 90GB)並輸出到另一個 csv

  • September 15, 2021

這是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,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

讓我們回顧一下以某種方式將兩個文件逐行組合在一起的工具:

  • paste將兩個文件逐行合併,不注意內容。
  • comm合併排序的文件,注意相同的行。這可以清除相同的行,但隨後組合不同的行將需要不同的工具。
  • join組合排序的文件,將相同的欄位匹配在一起。
  • sort可以合併兩個文件。
  • awk 可以根據您給它的任何規則組合多個文件。但是對於如此大的文件,與使用通用工具相比,使用最合適的專用工具可能會獲得最佳性能。

我假設沒有重複,即在一個文件中沒有兩行具有相同的 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; }
'

引用自:https://unix.stackexchange.com/questions/350631