Perlコマンドのパフォーマンスの問題

Perlコマンドのパフォーマンスの問題

次のコマンドを使用して数値列(.CSVファイル)の特殊文字を削除していますが、正常に動作していますが、ここで問題はパフォーマンスです。私のCSVファイル番号列データには、次のものが含まれます。データから1000個の区切り文字コンマを削除するには、次のPerlコマンドを使用しました。

payment        
"4,326.34"        
590.20          
"12,499.40" 

注:私のファイル区切り文字は "、"カンマです。

input file :               
    Organization,Amount,Revenue,Balance,Desc
    Congos,"4,233.78","3,233.78","1,233.78",Payment
    Toyoto,590.2,390.2,190.2,Payment
    lenives,"5,234.89","2,234.89","1,234.89",Payment
            
            
Excepted OutPut:                    
    Organization,Amount,Revenue,Balance,Desc
    Congos,4233.78,3233.78,1233.78,Payment
    Toyoto,590.2,390.2,190.2,Payment
    lenives,5234.89,2234.89,1234.89,Payment

注文する: cat | perl -p -e 's/,(?=[\d,.]\d")//g and s/"(\d[\d,.])"/\1/g' 'test.csv' >> newfile.csv

ファイルデータ数:1,100万個のデータ

質問:データから1,000個の個別の「カンマ」を削除するのに約10分かかりました。

パフォーマンスを向上させるためのより良いソリューションはありますか?

答え1

サンプル入力をファイルに保存し、データ行を数百万回コピーし、約1,100万のレコードを含めました。

$ wc -l input2.csv 
11100445 input2.csv

$ ls -l input2.csv 
-rw-r--r-- 1 cas cas 505070243 Apr 10 22:32 input2.csv

速くて汚い一行:

$ time perl -pe 's/"([0-9-.]+),?([0-9-.]+)"/$1$2/g' input2.csv >output.csv

real 0m37.922s  user 0m36.371s  sys 0m1.347s

"これにより、数値フィールドからカンマと引用符()文字が削除されます。残念ながら、この機能はコンマが1つしかない数値フィールド(下の数字など)でのみ機能します 1,000,000。数値フィールドの複数のカンマに一致する正規表現を見つけることは価値があるとは思わず、代わりにcsv解析ライブラリを使用します。

Text::CSV モジュールの使用は遅いですが、より「正確」です。

このバージョンでは、各フィールドを調べて数値であることを確認し、その場合はすべてのコンマを削除するCSV解析Perlモジュールを使用します。これは、すべての値の数値フィールドに適用されます。

数値フィールドの周りの引用符文字を削除する必要はありません。 Text::CSV はフィールド引用符を自動的に処理します。

#!/usr/bin/perl

use Text::CSV;

my $filename = shift;

my $csv = Text::CSV->new;
open my $fh, $filename or die "$filename: $!";
while (my $row = $csv->getline ($fh)) {
    my @row = map { tr/,//d if ( m/^[0-9,.-]+$/); $_ } @{ $row };
    $csv->print(*STDOUT, \@row);
    print "\n";
}
close($fh);

たとえば、別の名前で保存しstrip.plて実行可能にしますchmod +x strip.pl

$ time ./strip.pl input2.csv > output2.csv

real 1m32.379s  user 1m30.422s  sys 0m1.609s

これはおそらく多くの最適化が可能です。より速くするために何の努力もしませんでした。 1,100万のレコードを処理し、各レコードの5つのフィールドを確認/修正するのに1.5分も悪くないようです。

$ ls -l input2.csv output.csv output2.csv 
-rw-r--r-- 1 cas cas 505070243 Apr 10 22:32 input2.csv
-rw-r--r-- 1 cas cas 430142246 Apr 10 22:40 output2.csv
-rw-r--r-- 1 cas cas 430142246 Apr 10 22:37 output.csv

$ cmp output.csv output2.csv 
$ echo $?
0

両方の出力ファイルは同じであるため、CSVファイルに> = 100万値の数値フィールドがないようです。高速で汚れたバージョンが動作します。

パフォーマンスの問題

あなたのPerlコードの1行は私のシステム(スレッドリッパー1950x、64GB RAM、zfsミラーペアの2つのNVME SSD *)で約15秒間実行されますが、何もしません。出力は入力と同じです。

あなたのパフォーマンスの問題は、次のいずれかまたは組み合わせによって引き起こされるようです。

  • 遅いCPU
  • ハードドライブが遅い。
  • 最も重要なのはメモリ不足です。

つまり、少なくともシステムを大幅にアップグレードするまで、アルゴリズムを最適化してパフォーマンスを向上させることはできません。うまくいかないことを無視すると、1行のコードが私のコードよりも約3倍速く、Text :: CSVバージョンより約7倍速くなります。

一つは可能パフォーマンスを大幅に向上させる方法(遅いディスクI / Oが問題の主な原因である場合)は、CSVファイルを圧縮してgzip使用zcat可能な単一のコード行に変換することです。 10MB以下に圧縮できます -読む10MB未満のディスク上のデータは、ディスクより約50倍高速です。読む500MB以上。 xzそして、xzcat同様の圧縮プログラムが良くない場合でも、同様にうまく機能します。

これは、プロセス全体が50倍速く実行されるという意味ではなく、ファイルをはるかに高速に読み取ることができるという意味です。 Perlはまだ各行を個別に処理する必要があります。


*しかし、NVMEは高速ですが、zfsは少し遅いです(ext4やxfsなどの単純なファイルシステムと比較して)。 OTOH lz4圧縮を有効にすると、ファイルの読み取り速度が再び高速になります。

答え2

使用csvkit:

$ in2csv file.csv
Organization,Amount,Revenue,Balance,Desc
Congos,4233.78,3233.78,1233.78,Payment
Toyoto,590.2,390.2,190.2,Payment
lenives,5234.89,2234.89,1234.89,Payment

このin2csvユーティリティは、さまざまな表形式のデータ型をCSVに変換します。完全に有効なCSVファイルを変換すると、in2csvそのロケールの番号がen_USデフォルトで再解釈され、その番号が削除されます,

これらのcsvkitツールは、Pythonで実装されたCSVパーサーに基づいて構築されています。

関連情報