
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) { この後未作成
No.3ベストアンサー
- 回答日時:
## 面白そうだったので、自分流に書いてみました。
## 適当に書いたので、バグがあったらごめんなさいです。
## ← ##で始まる行は解説用コメントです
use strict;
use warnings;
use utf8;
binmode(STDOUT, ':utf8');
binmode(STDERR, ':utf8'); ## エラー出力も考慮しましょう
my $dir = './data'; # 処理するディレクトリ
my $motoFile = 'customer.txt'; # もとファイル
# データファイル読み込み
my @array;
open(my $fh, '<:encoding(cp932)', "$dir/$motoFile") or die 'ファイルが開けません。',"$!";
while(my $line = <$fh>) {
chomp $line;
## 「配列リファレンスの配列」を作る。複数カラムでのソートが楽になります
push(@array, [ split(/,/, $line) ]);
}
close($fh);
# 配列リファレンス内の指定カラム値を比較して配列をソート
## どこを使ってソートするかわからなかったので
## とりあえず[37]~[41]でソート
my @sorted = sort {
$b->[37] <=> $a->[37] ||
$b->[38] <=> $a->[38] ||
$b->[39] <=> $a->[39] ||
$b->[40] <=> $a->[40] ||
$b->[41] <=> $a->[41]
} @array;
# ソート済みデータファイル出力
## 分類用フラグは後付けで計算
open(my $fh_out, '>:encoding(cp932)', "$dir/out.txt") or die "Can't open: $!";
for(my $i=0; $i<192; $i++) {
# 192等分したブロックの先頭と終了のインデックスを求める
## 余りの分を先頭に近いブロックに寄せる方法については宿題としておきます(難しくないです)
my $start = int(@sorted * $i / 192);
my $end = int(@sorted * ($i+1) / 192) - 1;
# 分類用フラグの計算
my @flag_value = (
int($i / (4 * 2 * 4 * 2)) % 3 + 1,
int($i / (2 * 4 * 2)) % 4 + 1,
int($i / (4 * 2)) % 2 + 1,
int($i / 2) % 4 + 1,
$i % 2 + 1,
);
# 出力範囲の表示
## とりあえず標準出力に。
## データ形式に合わせて見やすいように整形するといいでしょう
printf(
"[%d, %d, %d, %d, %d] (%s, %s, %s, %s, %s) ~ (%s, %s, %s, %s, %s)\n",
@flag_value,
@{$sorted[$start]}[37..41],
@{$sorted[$end]}[37..41],
);
# ブロック毎にフラグを上書きしながら出力
for(my $j=$start; $j<=$end; $j++) {
## 分類用フラグをどこに立てるかわからなかったので
## とりあえず[2]~[6]に立てる
@{$sorted[$j]}[2..6] = @flag_value;
# 出力
print $fh_out join(',', @{$sorted[$j]}),"\n";
}
}
close($fh_out);
## 具体的な用途が不明なので以下は独り言ですが、
## 正直なところ、カラム内にインデックスフラグ立てるメリットは
## ほとんどない気がします・・。
## 結果を別ファイルに分けるならわかるのですが、それならなおさら
## カラム内にフラグを持たせる必要はないように思えます。
この回答への補足
動作結果です。
$ ./mainBunkatu3.pl
Malformed UTF-8 character (unexpected continuation byte 0x81, with no preceding start byte) at ./mainBunkatu3.pl line 64.
[1, 1, 1, 1, 1] (18.90, 0.00, 0.00, 0.00, 0.00) ` (8.70, 0.00, 0.00, 0.00, 0.00)
[1, 1, 1, 1, 2] (8.70, 0.00, 0.00, 0.00, 0.00) ` (7.40, 0.00, 0.00, 0.00, 0.00)
~中略~
[1, 4, 2, 4, 1] (-0.50, 0.00, 0.00, 0.00, 0.00) ` (-0.59, 0.00, 0.00, 0.00, 0.00)
[1, 4, 2, 4, 2] (-0.59, 0.00, 0.00, 0.00, 0.00) ` (-0.59, 1.00, 1.00, 0.00, 1.00)
[2, 1, 1, 1, 1] (-0.59, 0.00, 0.00, 0.00, 1.00) ` (-0.69, 3.00, 4.00, 1.00, 3.00)
[2, 1, 1, 1, 2] (-0.69, 1.00, 0.00, 0.00, 1.00) ` (-0.70, 0.00, 0.00, 0.00, 0.00)
~中略~
[2, 4, 2, 4, 1] (-3.90, 0.00, 1.00, 0.00, 2.00) ` (-4.00, 0.00, 0.00, 0.00, 0.00)
[2, 4, 2, 4, 2] (-4.00, 1.00, 1.00, 0.00, 0.00) ` (-4.00, 0.00, 0.00, 0.00, 0.00)
[3, 1, 1, 1, 1] (-4.00, 0.00, 0.00, 0.00, 0.00) ` (-4.10, 0.00, 0.00, 0.00, 1.00)
[3, 1, 1, 1, 2] (-4.10, 0.00, 0.00, 0.00, 0.00) ` (-4.20, 0.00, 0.00, 0.00, 0.00)
~中略~
[3, 4, 2, 3, 2] (-11.70, 0.00, 0.00, 0.00, 0.00) ` (-12.30, 0.00, 0.00, 0.00, 0.00)
[3, 4, 2, 4, 1] (-12.30, 0.00, 0.00, 0.00, 0.00) ` (-13.70, 0.00, 0.00, 0.00, 0.00)
[3, 4, 2, 4, 2] (-13.70, 4.00, 3.00, 0.00, 0.00) ` (-22.50, 0.00, 1.00, 0.00, 0.00)
実は境目の-0.59(一つめのフラグ)はどちらかに入れたいのです。同様に-4の人たちもです。
すごいです。動きました。ありがとうございます。私には読むのも時間がかかります。こんなに短い時間で書けるとは!ただ、ただ驚きです。私には読むだけでも2日~3日かかりますのでとりあえずお礼させていただきます。
No.4
- 回答日時:
各段階で等分するのなら、分割しながらソートしてもソートしてから分割しても
同じ事なので、本当は値の範囲ごとに区分したいんだと思いますが。
Unix系ならシェルスクリプトで手軽にできますよ。
#!/bin/sh
line=$(( $(sed -n '$=' customer.txt) / (3 * 4 * 2 * 4 * 2) + 1 ))
sort -s -t, -k38nr -k39nr -k40nr -k41nr -k42nr | split -a 3 -d -l ${line}
mkdir -p ./dir
mv x[0-9][0-9][0-9] ./dir
この回答への補足
皆さん、なかなかやりたいことが伝えられず申し訳ありません。下のANo.3の方にはもっと補足を書こうと思ったのですが間違えてクリックし確定してしまいました。
補足日時:2012/06/19 12:29大変ありがとうございます。私には想像もつかない方法をご存じで、世の中にはすごい人がいるものだなぁとただただ感じいっております。
試してみます。これも解読するのには3日以上かかるかと思われますのでお礼を先にさせていただきます。
No.2
- 回答日時:
前回の回答で $count をクリアする文を入れるのを忘れていました。
すみませんが、修正願います。if ($count == $groupGyousuu + ($idx_amari < $amari ? 1 : 0)) {
close OUT;
$flag = next_flag($flag);
$idx_amari++;
$count = 0; # この文を追加
open OUT '>:encoding(cp932)', "$dir/out$flag.txt" or die "Can't open: $!";
}
ありがとうございます。大変参考になります。お礼が遅くなり申し訳ありません。試したり読んだりしているのですが初心者の私にはとても敷居が高くて、まだまだ時間がかかってしまいそうなのでお礼させていただきます。
No.1
- 回答日時:
out.txt を作成するまでは、よくできているいるように思います。
その後の細分化しながらのグループ分けは、あまり有効的なやり方ではありません。カラム37番目 (コード中も 37 になっているので、0 から数えた?) が整数であるとすると、最大 50 種類で平均しても1種類で 600 行にもなってしまうからです。ソート対象は37番目のカラムのみのようですので、ソート済みの out.txt の行を順に 11111, 11112, 11121, ..., 34232, 34241, 34242 に割り当てたほうが簡単に済むように思います。while (my $line = <$fh>) {
...
if (eof) { $groupGyousuu = int($. / 192); $amari = $. % 192; }
}
最初の while ループの最終行でグループ行数と余りの行を算出しておきます。次のコードでは、フラグをファイ名に含めてあります。行に入れる場合は、コメントアウトしてある行を参考にしてみてください。(なお、余りの行がない場合、余分な空ファイルが作られてしまいますが、繁雑になるので除外のコードを入れてありません。)
my ($count, $idx_amari, $flag) = (0, 0, '11111');
open IN, '<:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!";
open OUT, '>:encoding(cp932)', "$dir/out$flag.txt" or die "Can't open: $!";
while (my $line = <IN>) {
# substr($line, index($line, ','), 0) = ',' . join(',', split //, $flag);
print OUT $line;
$count++;
if ($count == $groupGyousuu + ($idx_amari < $amari ? 1 : 0)) {
close OUT;
$flag = next_flag($flag);
$idx_amari++;
open OUT '>:encoding(cp932)', "$dir/out$flag.txt" or die "Can't open: $!";
}
}
close OUT;
sub next_flag {
my @temp = split //, $_[0];
my @limit = (3, 4, 2, 4, 2);
foreach my $i (reverse 0 .. 4) {
if ($temp[$i] < $limit[$i]) {
$temp[$i]++;
return join('', @temp);
} else {
$temp[$i] = 1;
}
}
}
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- 英語 英語ができる方に質問です。 以下の文がネイティブの方に伝わるかどうかを確認していただけないでしょうか 7 2022/12/16 14:54
- CGI htmlからパラメータで、cgiに渡したい。 1 2023/02/06 16:15
- 英語 英文の添削お願いします。【長文です。】 マッチングアプリで相手を言い負かしている時のやつです。 色々 1 2023/07/01 02:12
- その他(プログラミング・Web制作) pythonのこのエラーがわかりません 3 2022/11/16 14:54
- 英語 文の構造をご教示ください 2 2023/01/01 18:03
- CGI perlで書いたcgiでsqliteの使い方を教えてください 2 2023/05/08 21:29
- Excel(エクセル) マクロでテキストファイルを読み込んだ際の最終セルにデータと改行が含まれる問題の改善方法 2 2022/03/25 16:50
- Excel(エクセル) Excelにて、フォルダ内のTextファイルをマクロで統合すると文字化けしてしまう時の解消コード 4 2023/01/01 07:32
- Ruby 教えてください 2 2023/01/04 17:50
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・「それ、メッセージ花火でわざわざ伝えること?」
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・【お題】甲子園での思い出の残し方
- ・【お題】動物のキャッチフレーズ
- ・人生で一番思い出に残ってる靴
- ・これ何て呼びますか Part2
- ・スタッフと宿泊客が全員斜め上を行くホテルのレビュー
- ・あなたが好きな本屋さんを教えてください
- ・かっこよく答えてください!!
- ・一回も披露したことのない豆知識
- ・ショボ短歌会
- ・いちばん失敗した人決定戦
- ・性格悪い人が優勝
- ・最速怪談選手権
- ・限定しりとり
- ・性格いい人が優勝
- ・これ何て呼びますか
- ・チョコミントアイス
- ・単二電池
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・ゴリラ向け動画サイト「ウホウホ動画」にありがちなこと
- ・泣きながら食べたご飯の思い出
- ・一番好きなみそ汁の具材は?
- ・人生で一番お金がなかったとき
- ・カラオケの鉄板ソング
- ・自分用のお土産
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Pythonでテキストを行数指定し...
-
一行だけ読込
-
あるファイルないから日付指定...
-
vba dir の相対パス
-
ExcelをCSV書き出す場合のシー...
-
VBAで巨大なファイルの途中から...
-
ReadLineでの読み出し行を指定する
-
MATLAB グローバル変数の宣言
-
エクセルVBA コードが同じでも...
-
VBAでCSVファイルを途中行まで...
-
window.open でのファイル指定方法
-
close()で例外が投げられる理由
-
Perlを改造してエラーチェック...
-
バッチファイルの作り方(CSV→...
-
正規表現で、特定のファイル名...
-
タブの色を変更する方法
-
CSVが可変長の場合の検索方法
-
空白文字 \\f と\\v の違いに...
-
C言語で特定の行を抽出する方法...
-
fgets で値が取得できない
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ファイル名を複数個配列で確保...
-
Pythonで非日本語のUnicode文字...
-
「パスが見つかりません」とい...
-
C++でのテキストファイル読み込...
-
Perl エラーログを指定の場所...
-
ファイル名に日付を入れてアッ...
-
Pythonでテキストを行数指定し...
-
ifstream を利用した1行分のテ...
-
perlでCSVをソートする方法につ...
-
Perlによるディレクトリ内の連...
-
あるファイルないから日付指定...
-
連番のファイルを何個も開きたい
-
テキストファイルの各行を配列...
-
perlで先頭の数値をみて昇順に...
-
Perl 重複カウント 上位3名
-
MATLABでカーブフィットしたデ...
-
Fortranで1行飛ばして読み込む方法
-
while文がうまく動かない
-
Pythonでegrep機能をつかいたい
-
配列を用いた文字置換
おすすめ情報