3

たとえば、0.csv という 2 つの csv ファイルがあります。

100a,a,b,c,c
200a,b,c,c,c
300a,c,d,c,c

および 1.csv

100a,Emma,Thomas
200a,Alex,Jason
400a,Sanjay,Gupta
500a,Nisha,Singh

出力を次のようにしたい

100a,a,b,c,c,Emma,Thomas
200a,b,c,c,c,Alex,Jason
300a,c,d,c,c,0,0
400a,0,0,0,0,Sanjay,Gupta
500a,0,0,0,0,Nisha,Singh

Unix シェルスクリプトまたは Perl でこれを行うにはどうすればよいですか? 私は UNIX の「結合」コマンドを知っていますが、それは小さなファイルでうまく機能します。たとえば、結果を取得するには、次のことができます

join -t , -a 1 -a 2 -1 1 -2 1 -o 0 1.2 1.3 1.4 1.5 2.2 2.3 -e "0" 0.csv 1.csv

しかし、実際のデータ ファイルには 100 万を超える列 (ギガバイト単位の合計データ サイズ) があり、UNIX コマンドも 100 万文字を超える長さになるため、これは私の目的には適していません。非効率的なコードはすぐに行き詰まるため、これが最も重要な頭痛の種になる可能性があります。

また、欠落しているデータがある場合は常にプレースホルダー文字「0」が必要であることにも注意してください。これにより、これを単純に使用できなくなります

join -t , -a 1 -a 2 -1 1 -2 1 0.csv 1.csv

初心者の Perl プログラマーでもあるので、詳細を歓迎します。私は解決策がperlまたはシェルスクリプトであることを望んでいますが、実際に機能するものは何でも問題ありません.

4

6 に答える 6

2

各ファイルにヘッダーを追加できる場合は、タブレータを使用して問題を解決できます。例:

0.csv:

key,letter_1,letter_2,letter_3,letter_4
100a,a,b,c,c
200a,b,c,c,c
300a,c,d,c,c

1.csv:

key,name_1,name_2
100a,Emma,Thomas
200a,Alex,Jason
400a,Sanjay,Gupta
500a,Nisha,Singh

その後tbljoin -lr -n 0 0.csv 1.csv、生成します

key,letter_1,letter_2,letter_3,letter_4,name_1,name_2
100a,a,b,c,c,Emma,Thomas
200a,b,c,c,c,Alex,Jason
300a,c,d,c,c,0,0
400a,0,0,0,0,Sanjay,Gupta
500a,0,0,0,0,Nisha,Singh

(純粋な UNIX コマンドとは対照的にjoin) 入力ファイルをソートする必要がないことに注意してください。また、実装は UNIX ソートに基づいており、大きなファイルにはファイルベースのマージソートを使用するため、メモリ消費について心配する必要はありません。

于 2015-04-04T07:42:14.937 に答える
1

でこれを行うこともできますawk

両方のファイルで最も幅の広い行の長さを決定し、 と に保存しmax0ますmax1

awk -F, '
  ARGIND == 1 && NF > max0 { max0 = NF }
  ARGIND == 2 && NF > max1 { max1 = NF }
  END { print max0, max1 }
' 0.csv 1.csv | read max0 max1

この awk スクリプトを使用して結合を行います。

foo.awk

BEGIN { 
  max1--
  FS  = OFS = ","
}

ARGIND == 1 {
  A[$1] = $2

  # Copy columns from first file to key
  for(i=3; i<=NF; i++)
    A[$1] = A[$1] FS $i

  # Pad until we have max0 columns
  for( ; i<=max0; i++)
    A[$1] = A[$1] FS "0"
}

ARGIND == 2 {
  # Pad rows which are only in second file
  if(A[$1] == "") {
    A[$1] = 0
    for(i=3; i<=max0; i++)
      A[$1] = A[$1] FS "0"
  }

  # Copy columns from second file to key
  for(i=2; i<=NF; i++)
    A[$1] = A[$1] FS $i

  # Pad until we have max1 columns
  for( ; i<=max1; i++)
    A[$1] = A[$1] FS "0"
}

END { 
  for(key in A) {
    # Pad rows which are only in first file
    split(A[key], fields, ",")
    for(i=1; i <= max0+max1-length(fields)-1; i++)
      A[key] = A[key] FS "0"

    # Finally print key and accumulated column values
    print key, A[key]
  }
}

実行:

awk -f foo.awk -v max0=$max0 -v max1=$max1 0.csv 1.csv | sort -n

で最も幅の広い行の値を渡します-v。出力はハッシュから取得され、ソートされていないためsort -n、表示する前に.

于 2012-08-20T22:10:15.513 に答える
0

大量のデータを処理し、両方のソースのサイズがほぼ同じ場合は、マージ結合が最適です。これは、両方の (それぞれの) ソースがソートされると、一定量のメモリを使用するためです。マージ結合も完全な外部結合に適した選択であり、Perl で非常にエレガントに記述できます。

次の Perl スクリプトを使用するには、両方のファイルを最初の列のキーで辞書順にソートする必要があり、キーは一意である必要があります。また、両方のファイルの各行にまったく同じ数の列があることも前提としています。

#!/usr/bin/perl

use strict;
use warnings;
use Text::CSV_XS;

die "Usage $0 file1.csv file2.csv" unless @ARGV > 1;

my ( $file1, $file2 ) = @ARGV;

open my $fh1, '<', $file1 or die "Can't open $file1: $!";
open my $fh2, '<', $file2 or die "Can't open $file2: $!";

my $csv = Text::CSV_XS->new( { binary => 1, eol => "\n" } );

my $r1 = $csv->getline($fh1) or die "Missing data in $file1";
my $r2 = $csv->getline($fh2) or die "Missing data in $file2";

# same amount of zeros as number of fields in each file
my @cols1 = (0) x ( @$r1 - 1 );
my @cols2 = (0) x ( @$r2 - 1 );

while ( $r1 || $r2 ) {    # there are some data

    # compare keys only if there are rows in both files
    # zero silences warnings in numeric comparisons below
    my $cmp = $r1 && $r2 && ( $$r1[0] cmp $$r2[0] ) || 0;

    # row is defined and has less or equal key than another one
    my $le1 = $r1 && $cmp < 1;
    my $le2 = $r2 && $cmp > -1;

    $csv->print(
        *STDOUT,
        [   $le1 ? $$r1[0] : $$r2[0],    # key
            ( $le1 ? @$r1[ 1 .. @cols1 ] : @cols1 ),    # first file fields
            ( $le2 ? @$r2[ 1 .. @cols2 ] : @cols2 )     # second file fields
        ]
    );

    #read next rows
    $r1 = $csv->getline($fh1) if $le1;
    $r2 = $csv->getline($fh2) if $le2;
}

使用法はscript.pl 0.csv 1.csv > result.csvsort -u -d -t, -k1,1ソートされていない場合は、ファイルのソートに使用します。

スクリプトは線形時間 (既にソートされている場合) で動作し、各ファイルから 1 行、つまり「一定の」サイズを格納するためだけにメモリを使用します。

を使用して、スクリプトからファイルを並べ替えることができます

$ENV{LC_ALL} = 'C';
open my $fh1, "( sed '1!d' $file1; sed 1d $file1 | sort -u -d -t, -k1,1 ) |"
    or die "Can't sort $file1: $!";
open my $fh2, "( sed '1!d' $file2; sed 1d $file2 | sort -u -d -t, -k1,1 ) |"
    or die "Can't sort $file2: $!";
于 2014-10-23T18:11:43.690 に答える
-1

csvkitcsv ファイルを処理し、そのような結合を可能にするツールです (他の機能の中でも特に)。

を参照してくださいcsvjoin。そのコマンド ライン インターフェイスはコンパクトで、多数の csv 形式 (tsv、その他の区切り文字、エンコーディング、エスケープ文字など) を処理します。

あなたが求めたことは、次を使用して実行できます。

csvjoin --columns 0 0.csv 1.csv
于 2014-10-23T08:20:15.857 に答える