Perlを使用して大容量CSVファイルから特定のCSV列と重複行を削除する

Perlを使用して大容量CSVファイルから特定のCSV列と重複行を削除する

大容量のCSVファイル(300MB以上)があり、列2、3、6〜8のみを削除し、Perlを使用して重複行を削除したいと思います。

注1:すべての列は,(カンマ)で区切られていますが、私のセル値に,(最後の行、列9、10を参照)で区切られた1つ以上が含まれているため、,csv"ファイルを処理できるようにしたい場合があります。 、,セル内部にある場合も同様です。

注2:input.csvファイルとoutput.csvファイルへのリンクを追加しました。

入力.csv

Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
info 1,info 2,info 3,...,info 10
address 1,address 2,....,address 10
city 1,city 2,city 3,city 4,city 5,city 6,city 7,city 8,"city 9, extra","city 10, new"

出力.csv

Col1,Col4,Col5,Col9,Col10
info 1,info 4,info 5,info 9,info 10
address 1,address 4,address 5,address 9,address 10
city 1,city 4,city 5,"city 9, extra","city 10, new"

正規表現を使用して最後の列を削除するPerlコマンドを見つけましたが、これが十分かどうか、私の状況に合わせて調整する方法がわかりません(他の提案は非常に歓迎されています!)。

perl -pe 's/.*\K,.*//'

Perlを使用して2、3、6〜8列だけを削除し、重複行を削除することは可能ですか?

PS:重複行を含むようにinput.csvファイルを更新しました。

ありがとうございます!

ベストアンサー1

最も簡単な方法はミラー別名mlr、CSV、json、およびその他の入力または出力形式のデータ操作に最適なツールです。たとえば、

$ mlr --csv --implicit-csv-header --headerless-csv-output \
    cut -x -f 2,3,6,7,8 \
    then uniq -a input.csv  
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10

--implicit-csv-headerオプションを一緒に使用すると、--headerless-csv-outputヘッダー行が効果的に無視され(つまり、他のデータ行と同じように作成され)、名前ではなく数字に切り捨てるフィールドを指定できます。

不足しているフィールドにいくつかのジャンクデータを追加するには、サンプルinput.csvファイルを編集する必要がありました。 mlrそうでなければ文句を言うでしょう。また、重複排除が機能しているかどうかをテストするために、重複入力行を追加しました。

$ cat input.csv 
Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
info 1,info 2,info 3,info 4,5,6,7,8,9,info 10
info 1,info 2,info 3,info 4,5,6,7,8,9,info 10
address 1,address 2,3,4,5,6,7,8,9,address 10
city 1, city 2,3,4,5,6,7,8,9,city 10

Perlでしたい場合:

  1. 単純なカンマ区切りの入力のみを処理する必要がある場合:
$ perl -F, -lane '
  next if $seen{$_}++;
  splice @F,5,3;
  splice @F,1,2;
  print join ",", @F' input.csv
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10

これはPerlの-aオプションを使用して、各入力行をという配列に自動的に分割します@F。この-Fオプションは、使用する区切り文字を示します。

注1:Perl配列は1ではなく0から始まります。したがって、配列要素5は列6である。 splice @$row, 5, 3配列内の要素5(つまり、列6、7、8)から始まり、3つの要素を削除します。perldoc -f splice詳細より。

注2:ここでは逆の順序で列を削除します(つまり、番号の高い列今後低い数字)。そうではなく、列5、6、7を削除する前に列2と3を削除すると、最初の削除によってその列の番号が再割り当てされます(3、4、5)。

  1. 使用テキスト::CSV有効なCSV(カンマを含む複数行で囲まれた引用符列などを含む)を処理します。
$ perl -MText::CSV -e '
  my $csv = Text::CSV->new();
  while (my $row = $csv->getline(*ARGV)) {
    next if $seen{join ",", @$row}++;
    splice @$row, 5, 3;
    splice @$row, 1, 2;
    $csv->say(*STDOUT, $row);
  }' input.csv
Col1,Col4,Col5,Col9,Col10
"info 1","info 4",5,9,"info 10"
"address 1",4,5,9,"address 10"
"city 1",4,5,9,"city 10"

ここで注目すべき4つの点は次のとおりです。

  1. Text::CSVコアPerlモジュールではないので、インストールが必要です。すべてではありませんが、ほとんどのLinuxディストリビューションで動作します。たとえば、Debianではsudo apt-get install libtext-csv-perl。それ以外の場合は、cpanPerlに付属のコマンドを使用してインストールできます。

  2. Text::CSV のメソッドgetline()(上記に示す$row = $csv->getline(*ARGV)) は、配列または arrayref への参照を返します。これは配列全体を指すスカラー値です(詳細を確認しman perlrefman perldata確認してください)。

  3. $row上記のコードにはarrayrefが含まれています。 $ rowの使用/操作は、参照するデータではなく参照自体に適用されます。したがって、たとえば、$row2 = $rowデータではなく参照がコピーされます。両方の参照は同じデータを指します。@$rowarrayrefを配列として「逆参照」することで、他の配列と同じように使用できます。

  4. *ARGVinは、コマンドラインで指定されたすべてのファイル名引数から入力を読み取る特別なファイルハンドルですgetline(*ARGV)(これらの引数はPerlの@ARGVという配列に格納されます)。ファイル名ではなく引数(スクリプトにオプションを処理するコードがある場合はオプション)が処理され、@ARGVから削除されたと想定されます。存在しない、または開くことができないファイル名(たとえば、権限のため)はエラーメッセージを生成します。つまり、ユーザーが指定した1つ以上のファイル名を読み取ります。引数は-標準入力として扱われるため、ファイル、標準入力、またはその両方から入力を読み取ることができます。

これは Text::CSV の機能と使い方の非常に簡単で原始的な例です。詳細と例については、マニュアルページを読んでください。

上記の出力例に示すように、デフォルトでは、Text :: CSVはスペースを含むテキストフィールドを引用します。これを望まない場合は、プロパティをquote_space0に設定するか、new次のように$csvオブジェクトを作成してオーバーライドできます。

my $csv = Text::CSV->new({ quote_space => 0 });

またはそれ以降:

my $csv = Text::CSV->new();
$csv->quote_space(0);

その後、出力は次のようになります。

Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10

おすすめ記事