perlでCSVをソートする方法について
perl初心者です。いつもありがとうございます。
perlでcsvファイル(1行のカラム数は200)、総行数は約3万行のファイルを37番目のカラム(-25以上25未満の数値データ)で降順ソートしその値によって行数がだいたい均等になるよう3分割し、2番目のカラムに文字でも数字でもよいのですがその4つのグループごとにフラグ(例えば1,2,3)を入れたいと思ってます。グループ化については境目の37番カラムの値は重複している場合が多いと思うのですがその場合は下(別に上でもかまいません)に入れるものとします。
ソートロジックは過去の質問を参照して理解しましたがグループ化しフラグを入れるルーチンがうまく作れません。下記のように作ったのですがこの先同じことを何度もやらなくてはならないので先に進めません。どなたかお助けください。最終的にやりたいことはカラム37でグループ化→カラム2にフラグを立てる、次にカラム2とカラム38(-25から0までの数値)でソートし同様に同じ行数になるようにグループ化→カラム3にフラグを立てる、さらにカラム2とカラム3とカラム39(-25以上25未満の数値データ)でソートし・・・同様に繰り返し最終的に1グループが100件(行)~150件(行)になるようにしたいのです。つまり約3万件のデータを3*4*2*4*2=192分割(5列の値で分類)したい、そしてどのような範囲で分割したかという情報も得たいのです。
use strict;
use warnings;
use utf8;
use Encode;
binmode STDOUT, ':encoding(utf-8)';
my $dir = './data'; # 処理するディレクトリ
my $motoFile = 'customer.txt'; # もとファイル
open my $fh, '<:encoding(cp932)', "$dir/$motoFile" or die 'ファイルが開けません。',"$!";
my %sorted;
while (my $line = <$fh>) {
my $key = (split /,/, $line)[37];
push @{$sorted{$key}}, $line;
if (@{$sorted{$key}} == 1000) {
open OUT, '>>:encoding(cp932)', "$dir/$key.tmp" or die "Can't open: $!";
print OUT @{$sorted{$key}};
close OUT;
@{$sorted{$key}} = ();
}
}
open OUT, '>:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!";
foreach my $key (sort { $b <=> $a } keys %sorted) {
if (-e "$key.tmp") {
open IN, '<:encoding(cp932)', "$dir/$key.tmp" or die "Can't open: $!";
print OUT while <IN>;
close IN;
}
print OUT @{$sorted{$key}} if @{$sorted{$key}};
}
close OUT;
#↓↓↓↓ここからフラグを作成するルーチン
# 行数を調べ3つに分けるルーチン
my @colum37;
open IN, '<:encoding(cp932)', "$dir/out.txt" or die 'ファイルが開けません。',"$!";
my @in = <IN>;
close IN;
my $gyousuu = scalar(@in);
my $amari = $gyousuu % 3;
if ($amari == 0) {
my $groupGyousuu = ($gyousuu-$amari)/3;
print "総行数は$gyousuu","で、1グループの行数は$groupGyousuu","ほど、余りは$amari\n";
# あまりが0の時、group1は@inの0行 ~$groupGyousuu-1行まで
# group2は@inの$groupGyousuu行 ~$groupGyousuu*2-1行まで
# group3は@inの$groupGyousuu*2行~$groupGyousuu*3-1行まで
foreach my $num (1..2) {
push @colum37, (split /,/, $in[$groupGyousuu*$num])[37]; # これは境目の先頭の37番目
}
print "@colum37\n"; #これでここまでは完成、分けるべき値がこの配列に入っている。
open OUT, '>:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!";
foreach my $line (@in) {
my @line = split /,/,$line;
if ($line[37]>=$colum37[0]) {
$line[1] = 1;
}elsif ($line[37]>=$colum37[1] and $line[37]<$colum37[0]) {
$line[1] = 2;
}elsif ($line[37]<$colum37[1]) {
$line[1] = 3;
}
$line = join (',',@line);
print OUT $line;
}
close OUT;
}
elsif ($amari == 1) { この後未作成
お礼
sakusaker7様へ 連絡が遅れましてすみませんでした。朝お礼しようと思っていたのですが、ここのサイトの更新ができませんでしたので、お礼が遅れて しまいました。改めまして ご回答ありがとうございまいした。 今後は、redoを使用するときは、変数宣言の位置に気をつけていきたい と思います。ありがとうございました。