From: Joel VanderWerf Date: 2006-12-02T17:27:39+09:00 Subject: Re: [OT] calculations on lists of numbers Paul Lutus wrote: > ara.t.howard@noaa.gov wrote: > >> for years i've felt that i should be able to pipe numerical output into >> some unix command like so >> >> cat list | mean >> cat list | sum >> cat list | minmax >> >> etc. >> >> and have never found one. right now i'm building a ruby version - before >> i continue, does anyone know a standard unix or ruby version of this? > > It is so easy to create in Ruby, a matter of minutes, that it is not > terribly important to do the search you are suggesting. Disagree. I would like to know if a unix version exists, since it will certainly be faster than ruby, run in less memory, and probably exist in environments where ruby doesn't. So I think the search is worth while. Also, it _is_ terribly important to scrutinize code one finds in a newsgroup, so here we go... > #!/usr/bin/ruby -w > > array = [] > > STDIN.read.split(/\s+/).each do |item| > if(v = item.to_f) > array << v > end > end - Use $stdin instead of STDIN, to play well with reassignment of $stdin, in case this snippet ever becomes part of a library, and someone wants to capture output. - The code above can be simplified and improved so that files can be named on the command line: array = ARGF.read.split.map {|s| Float(s)} (Is #Float better than #to_f? It depends. If you want "3foobar" to be treated as 3.0 and you want "foobar3" to be treated as 0.0, stick with #to_f (and keep the nil values out of the array). If you want the program to die noisily on bad input, use #Float. As a bonus, you don't have to deal with nil values.) > if(array.size > 0) > sum = 0 > array.each { |v| sum += v } > mean = sum / array.size > puts sum.to_s + " " + > mean.to_s + " " + > array.min.to_s + " " + > array.max.to_s > end More idiomatically ruby, IMO, is the following: unless array.empty? sum = array.inject {|s,x| s+x} mean = sum / array.size puts "#{sum} #{mean} #{array.min} #{array.max}" end Also, you might want an empty array have a sum of 0, just so that the nice algebraic properties hold: [1, 2, 3, 4].sum + [].sum == [1, 2].sum + [3, 4].sum (And, it's fairly standard: http://wiki.r-project.org/rwiki/doku.php?id=tips:surprises:emptysetfuncs) That only makes sense for the >> cat list | sum invocation, of course. Here's the implementation so far: $ cat agr.rb #!/usr/bin/env ruby array = ARGF.read.split.map {|s| Float(s)} sum = array.inject(0) {|s,x| s+x} print sum unless array.empty? mean = sum / array.size print " #{mean} #{array.min} #{array.max}" end puts $ echo "1 2 3" | ./agr.rb 6.0 2.0 1.0 3.0 $ echo "1 2 3foo" | ./agr.rb ./agr.rb:3:in `Float': invalid value for Float(): "3foo" (ArgumentError) from ./agr.rb:3 from ./agr.rb:3 $ echo "1 2 3" >data $ ./agr.rb data data 12.0 2.0 1.0 3.0 [~/tmp] echo "" >empty_data [~/tmp] ./agr.rb empty_data 0 > > -------------------------------- > > $ echo 1 2 3 4 5 | (script_name) > > Output: 15.0 3.0 1.0 5.0 And then what do you do if you are piping this output somewhere else? Use cut to get the mean or whatever it was you wanted? The OP wanted three separate functions. It might be better to use an argument to the script to select which aggregate value is to be output. There's not much point computing the min and max if only the mean was requested. -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407