日頃、ちょっとした結果とかデータを集めていたりしませんか? それが、条件を変更したときなどにどんな傾向を示すか興味がありませんか? こういうときにちょっとだけ手助けになるかもしれないクラスです。 私は仕事でベンチマークとかをしていたりするので、レスポンスタイムのようなものの傾向をみるのに使ったりします。 「平均」だけだと、大きくハズレた値に引っ張られることもあります。 こういうとき、「件数」「合計値」「最大値」「最小値」「平均値」「標準偏差」「中央値」などを手軽に確認できると便利だと思いませんか? もちろん、ちゃんとレポートを書くときには、それなりのツールをあとから使えばいいと思うのです。 でも、取り急ぎ次の手を打つためにデータの傾向をしりたいときに役立つツールがあれば! ここで紹介するものは、そういう手軽なツールです。 出力データに合わせてちょっとした修正で使えるはずのものです。
結果を出力する「プログラム」って、後処理することを考えていないものが多いですよね。 そういうとき、スクリプト言語の柔軟性が役立ちます。 また、簡単に使えるクラスを用意していますので、処理したい項目事にオブジェクトを用意すれば項目がたくさんあっても扱いは簡単です。 ああ、Ruby のおかげ。
クラスの使い方ということで...
オブジェクトの初期化を行います。
引数 arg をデータとして追加していきます。 確認したいデータを、このメソッドでどんどん追加していきます。
項目数を返します。
最大値を返します。
最小値を返します。
合計値を返します。
平均値を返します。
標準偏差を返します。
中央値を返します。
統計的情報を print します。
表示フォーマットを返します。 これは、任意に設定できた方がいいかもしれませんね。
簡単なサンプルとして、乱数を使用してその結果を確認してみます。
これは、10000 回ループして、出力される乱数で確認したものです。#! /usr/local/bin/ruby # /home/tetsu/src/ruby/junk/ss1.rb # Created: August 08,1999 Sunday 01:23:23 # Author: tetsu(WATANABE Tetsuya) # $Id$ # usage: require 'ss' ss = Ss.new 1.upto(10000) do ss.additem(rand(0)) end ss.info exit $ ruby ss1.rb count: 10000 sum: 4988.30 max: 1.00 min: 0.00 ave: 0.50 s1 ave: 0.50 median: 0.50 s: 0.29 s2 s: 0.29
次は私のログイン時間を確認するものです。 あんまり意味がありませんが...
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/junk/ss2.rb
# Created: August 08,1999 Sunday 01:36:07
# Author: tetsu(WATANABE Tetsuya)
# $Id$
# usage:
def timestr2sec(arg)
sec = 0
w = arg.split('+')
w.each do |v|
if v =~ /:/
h_str, m_str = v.split(':')
sec += h_str.to_i * 3600
sec += m_str.to_i * 60
else
sec += v.to_i * 24 * 3600
end
end
sec.to_f / 3600.0
end
require 'ss'
ss = Ss.new
last_cmd = File.popen('last')
while last_cmd.gets
if /\((.*?)\)/
time_str = $1
next if time_str == '00:00'
ss.additem(timestr2sec(time_str))
end
end
last_cmd.close
ss.info
exit
$ ruby ss2.rb
count: 143
sum: 840.85
max: 63.83
min: 0.02
ave: 5.88
s1 ave: 5.88
median: 0.48
s: 12.75
s2 s: 12.75
コマンド last の出力から、時間の部分を切りだして、それを「時間」にしています。
最初「秒」でやったんですが、感じがつかめないので時間にしました。
Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/class/ss.rb
# Created: August 05,1999 Thursday 10:19:48
# Author: tetsu(WATANABE Tetsuya)
# $Id: ss.rb,v 1.3 1999/10/18 06:42:38 tetsu Exp tetsu $
# usage:
# 参考文献
# 奥村晴彦著
# C 言語による最新アルゴリズム辞典
# 技術評論社 ISBN4-87408-414-1
class Ss
def initialize
@max = nil
@min = nil
@sum = 0.0
@count = 0
@data = []
@s1 = 0.0
@s2 = 0.0
end
attr_reader :count, :max, :min, :sum
def additem(arg)
f = arg.to_f
@sum += f
if @max == nil
@max = f
elsif f > @max
@max = f
end
if @min == nil
@min = f
elsif f < @min
@min = f
end
@count += 1
@data.push(f)
f -= @s1
@s1 += f / @count.to_f
@s2 += (@count - 1).to_f * f * f / @count.to_f
end
def ave
@sum / @count
end
def ave90
n = (@count - (@count * 0.9)).to_i
sum = @sum
data = @data.sort
while n > 0
sum -= data[-n]
n -= 1
break if n == 0
sum -= data[n - 1]
n -= 1
end
sum / (@count - n)
end
def s
ave = self.ave
s = 0.0
@data.each do |v|
x = v - ave
s += x * x
end
Math.sqrt(s / @count)
end
def m
@data.sort[@count / 2]
end
def fmt
len = self.sum.to_s.sub(/\.\d+$/, '').length + 3
sprintf("%%%d.3f", len)
end
def info
ave = self.ave
fmt = self.fmt + "\n"
print 'count: ', self.count, "\n"
print 'sum: '
printf fmt, self.sum
print 'max: '
printf fmt, self.max
print 'min: '
printf fmt, self.min
print 'ave: '
printf fmt, ave
# print 's1 ave:'
# printf fmt, @s1
print 'median:'
printf fmt, self.m
print 's: '
printf fmt, self.s
# print 's2 s: '
# printf fmt, Math.sqrt(@s2 / (@count - 1).to_f)
end
end
if $0 == __FILE__
ss = Ss.new
while gets
if ~/([\d.]+)/
ss.additem($1)
end
end
ss.info
exit
end
アルゴリズムについては、参考文献によるものです。
奥村晴彦著
C 言語による最新アルゴリズム辞典
技術評論社
ISBN4-87408-414-1
現状では、データの入力項目を一つだけ扱っています。 ですが、複数あった場合は、必要な数だけオブジェクトを作成すれば OK です。 入力項目を自動判断して、必要な数の入力項目を作成し結果を表示するようなことも可能です。
現状では、結果の出力部分は「とりあえず」という形になっています。 必要に合わせて修正してください。