プロが教えるわが家の防犯対策術!

perlで一定の範囲ごとにカウントさせる方法について教えてください。

例えば、data.txtに、-10≦n≦10の範囲で様々な数が入ったファイルがあるとして、

-10.0 ≦ x < -9.9
-9.9 ≦ x < -9.8
....
9.8 ≦ x < 9.9
9.9 ≦ x ≦ 10.0 (←最後は≦のほうがいいですが、<でもOK)

と0.1ごとに区切って、
その範囲に入る数をカウントしたいとき、

ifや+=を使って範囲を指定して、カウントアップさせる方法もありますが、
指定したい範囲の数が多いと、ifばかりをプログラム内に羅列するのは面倒です。

できれば以下のような結果が表示されるとうれしいのですが…

中間値 カウント数
-9.95 3
-9.85 6
...

すっきりした書き方はありませんか?よろしくお願いします。

A 回答 (5件)

たとえば


sub check_inclusive_exclusive {
my ($left, $right) = @_;
sub { $left <= $_[0] && $_[0] < $right; };
}

sub check_inclusive_inclusive {
my ($left, $right) = @_;
sub { $left <= $_[0] && $_[0] <= $right; };
}

my @counts;
for (-100 .. 98) {
my $left = $_ / 10.0;
my $right = ($_+1) / 10.0;
my $mid = ($_+0.5) / 10.0;

push @counts, +{ left => $left, right => $right, mid => $mid, check => check_inclusive_exclusive($left, $right) };
}
push @counts, +{ left => 9.9, right => 10.0, mid => 9.95, check => check_inclusive_inclusive(9.9, 10.0) };

for my $count (@counts) {
$count->{count} = 0;
}

open my $fh, '<', 'data.txt';
while (<$fh>) {
for my $count (@counts) {
++$count->{count}, last if $count->{check}($_);
}
}
とする手はあると思うけど, これでうれしいかどうかは知らん.
    • good
    • 0

一定範囲ごとにカウントしたい場合、


一般的には大きい数字から比較していけばよいと思うのだけどそれじゃだめですか?

実験は面倒なので理論だけ。(未テストです)
イメージ的には
my @floor = ( -9.9 -9.8 -9.7 -9.6 ); # 比較したい数字を降順にしたもの
my @count = () # カウントの結果
my $value = -9.75; # txtから読み取った数字
count_loop: for( my $N=0; $N<$#floor; $N++ )
{
if( $floor[$N] <= $value )
{
$count[$N] ++;
last count_loop;
}
}

テキストの中身が範囲内に収まっているのならこれで十分かなーとおもうけど
後は表示するときに、 増分の半分を $floorに足してあげれば中央値になるかなー?
    • good
    • 0

今回の条件(上限下限が決まっている。

定間隔)だったら

$val=( 読み込んだ値 )
だとして、

$minval = -10 ; # 最小値
$maxval = 10 ; # 最大値
$stepval = 10 ; # 0.1の逆数

if ( $val < $min ) {
$level = 0 ;
} elsif ( $val >= $maxval ) {
$level= 199; # ≦10のための処理
}else{
$level =int(($val - $minval) * $stepval) ; #区間番号0~199
}
$count[ $level ] += 1 ;

表示は
for($i=0;$I<200;++$i){
$start = $minval + $i / $stepval ;
$end = $minval + ($i+1) / $stepval ;
$mid = ($start + $end)/2 ;
printf "%5.2f %d\n", $mid, $count[$i];
}
    • good
    • 0

テストは行っていませんので、バグがあるかもしれませんが…。


参考までにどうぞ。
インデントは全角スペースになっています。


#!/usr/bin/perl
use strict;
use POSIX;

open(IN, "data.txt") or die;

my %hash;
while(<IN>){
 # 2桁目以降の端数を切り上げ
 my $n = &ceil($_*10)/10;

 # インクリメント
 $hash{$n}++;
}

print "中間値\tカウント数\n";

for(sort{ $a <=> $b } keys %hash){
 # -10.0から-9.9(keyは-9.90)を、-9.95と表示
 printf("%.2f\t%d\n", $_-0.05, $hash{$_});
}
    • good
    • 0

他の人の回答見てませんけども。



何に使うデータで、そのあとどのように、どの程度使うのかは分かりませんが。
テストはしてません。正規表現部分は都合に合う様に好きにして下さい。


-----------------------------------------------
@data; #全ての数値が入っている物とする。
foreach (@data){
{ m/([0-9]+\.[0-9]?)/ } $count{"$1"}++;
}
-------------------------------------------------

中間値とカウント数のの意味は分かりませんでしたが、
ハッシュのデータをごにょごにょしてカウント数から導き出せる数値は全部出せます。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!