April 25, 2014
アドホックに書いたルーチン。数値計算の精度を全く考慮しない。 モジュールとか読み込まなくてもよい。
引数に、数値の配列を渡す。
sub mean
{
local($s) = 0;
local($n) = 0;
foreach $_ (@_) { if ($_ =~ /[0-9]/) { $s += $_; $n++; } }
if ($n == 0) { ""; }
else { $s /= $n; }
}
sub dist
{
local($i, $m, $n, $s);
($m, $n, $s) = (0,0,0);
if (@_ < 2) { return 0; }
$m = &mean(@_);
for ($i = 0; $i < @_; $i++) {
if ($_[$i] =~ /[0-9]/) { $s += ($_[$i] - $m) * ($_[$i] - $m); $n++; }
}
if ($n < 1) { ""; }
elsif ($n < 2) { $s; }
else { $s /= ($n - 1); }
}
sub stdev { sqrt(&dist); }
sub median{
my $n = @_; # 元データ配列には空 (null) や "NA" などが入っててもよい
my @data; # 整理したデータを格納する
my $i; # だたのカウンタ
my $k = 0; # だたのカウンタ
my @ties; # 中央値のタイがあれば格納される
my $ties; # タイの数
my $x; # 中央の場所
my $median; # 返り値
if ($n < 0) { return "" } # ここには来ないはず
elsif ($n < 1) { return "" } # 空の配列が渡されたとき
elsif ($n < 2) { return $_[0] } # 引数が一つの値だったとき
for ($i = 0; $i < $n; $i++) { # 引数が複数の値だったとき
if ($_[$i] eq "") { next } # 空 (null) は無視する
elsif ($_ =~ /[A-DF-Za-df-z]/) { next } # e/E 以外の英文字を含むと無視
$data[$k++] = $_[$i]; # 条件に合うものだけ拾い出す
}
@data = sort { <=> } @data; # 昇順にソート
$n = @data; # データ数
# 暫定中央値を決める データ数が偶数なら中央をはさむ値の平均値
$x = $n/2;
if(($n % 2) < 1) { $median = ($data[$x - 1] + $data[$x]) / 2.0 }
else { $median = $data[$x] } # x の小数部は切り捨てて使われる
@ties = grep(/$median/, @data); # 暫定中央値がタイか
$ties = @ties;
if ($ties < 1) { return $median } # タイじゃない場合
else { # タイがある場合
for($i = 0; $i < $n; $i++) { if($_[$i] >= $median) { last } }
return ($median - 0.5) + ($n/2 - $i) / $ties;
}
}
sub max
{
local ($i,$max);
for ($i = 0; $i < @_; $i++) {
if ($_[$i] =~ /[0-9]/) { $max = $_[$i]; last; }
}
foreach $_ (@_) { if (($_ =~ /[0-9]/) && ($max < $_)) { $max = $_; }}
if (!($max =~ /[0-9]/)) { $max = ""; }
$max;
}
sub min
{
local ($i,$min);
for ($i = 0; $i < @_; $i++) {
if ($_[$i] =~ /[0-9]/) { $min = $_[$i]; last; }
}
foreach $_ (@_) { if (($_ =~ /[0-9]/) && ($min > $_)) { $min = $_; }}
if (!($min =~ /[0-9]/)) { $min = ""; }
$min;
}