#!/usr/local/bin/perl $version = "ave v0.3"; $Usage = <<"EOQ"; ave: Try to form averages and error estimates of (equal length) Monte Carlo (or whatever) runs from output files. The only requirement is each file matches every other file''s "word pattern". That is to say, if n-th line of file1 has "m" words, then the n-th line of every other file must have "m" words. Put briefly, this program averages files together: 1) Fixed strings average to the string value, and 2) Numbers average like numbers average. 3) Error estimates are also printed out. $version Copyright (C) 1994 Karl J. Runge Usage: ave [-yeso -yesa -yese -noo -noa -noe] 'fstring' [outfile] where 'fstring' is a glob string for which desired filenames match. Quotes '' are required to protect it from the shell. To simply list the files, or have your shell do the expansion use "file_1 ... file_n" 'o' = original, 'a' = averages, 'e' = errors. ('o' is verbatim from 1st file) 'yes' -> force showing of that data line (can abbr to 'y') 'no' -> prohibit showing of that data line (can abbr to 'n') 'o' as the first character of a line indicates it is simply from the 1st file 'a' as the first character of a line indicates the "average line" for all files 'e' as the first character of a line indicates the "errors" over all files If output file is not specified, it writes to 'ave.out' More options: -noprefix: do not put 'o' 'a' 'e' lables in column 1 of each line. -guessfmt: try to guess format of averages from first output file. -incl0: print out error entry even if '0' -iwc ignor "wc -w" test for word equality (dangerous). -q: be quiet. -note Place notes in output file n=1: (default) place date and number of files at top. n=2: as 1 but add per file info at the end. -err " .. ": are "+/-" like strings. For each occurrence, replace the average of the succeeding value with the error of the preceding value. These values may be slightly shifted out of format. -flag place flag " str" after each "inserted error bar" from the -err option. -flag by itself indicates no flag should be inserted. Default is to insert flag " [e1]". -both In addition to -err type error, write out /sqrt(N) Default flag is " [e2]". Use -NS1 and -NS2 to turn off the space being printed. -NS2 => " [e2]" -> "[e2]". Any line in an input file containing '#NO_AVE' will be ignored. Examples: careful person: ave -yeso -yesa -yese -incl0 'data*' data.ave bold person: ave -noo -yesa -noe -guessfmt -noprefix 'data*' suggested 1: ave -yeso -yesa -yese -guessfmt -incl0 'data*' suggested 2: ave -guessfmt 'data*' data.ave bold person 2: ave -no -ya -ye -guessfmt -flag -err '+/-' 'data*' VERY bold person: ave -no -ya -ne -guessfmt -noprefix -flag -err '+/-' 'data*' where we, say, have a bunch of files that match data* EOQ #todo: # worry about x=1.23 (i.e. '=' -> '= ') LOOP: # process args while (@ARGV) { $_ = shift @ARGV; CASE: { (/^-yeso$/ || /^-yo$/) && ( $yeso = 'True', last CASE); (/^-yesa$/ || /^-ya$/) && ( $yesa = 'True', last CASE); (/^-yese$/ || /^-ye$/) && ( $yese = 'True', last CASE); (/^-noo$/ || /^-no$/) && ( $noo = 'True', last CASE); (/^-noa$/ || /^-na$/) && ( $noa = 'True', last CASE); (/^-noe$/ || /^-ne$/) && ( $noe = 'True', last CASE); /^-noprefix$/ && ( $noprefix = 'True', last CASE); /^-err/ && ( $error_replace = shift, last CASE); /^-both(\S*)/ && ( $both_flag = " $1", last CASE); /^-flag(\S*)/ && ( $flag = " $1", last CASE); /^-NS1$/i && ( $NS1 = 'True', last CASE); /^-NS2$/i && ( $NS2 = 'True', last CASE); /^-NSB$/i && ( $NS1 = 'True', $NS2 = 'True', last CASE); /^-q$/ && ( $quiet = 'True', last CASE); /^-incl0$/ && ( $incl0 = 'True', last CASE); /^-iwc$/ && ( $ignore_wc = 'True', last CASE); /^-guessfmt$/ && ( $guessfmt = 'True', last CASE); /^-note(\S*)/ && ( $note = "X$1", last CASE); /^-h/ && ( &do_help, exit 0, last CASE); /^-/ && ( (print "$_ is not an option, Bye!\n"), exit 0, last CASE); unshift(ARGV,$_); # put back if done with flags last LOOP; } } $no_ave = '#NO *_*AVE'; #open(LIST,"eval ls $ARGV[0] | egrep -v '.*ave$' |"); open(LIST,"ls $ARGV[0] | egrep -v \'.*ave\$\' |") || die "can''t open ls pipe"; $nfile = 0; while () { # Get filenames to average chop; if ( -s $_) { $list[$nfile++] = $_; } } print "Got these: \n" unless $quiet; foreach $file (@list) { print "$file\n" unless $quiet; } $got = 0; if ( $note =~ /X(\S*)/ ) { if ( "$1" eq "" ) { $note = "1"; } else { $note = "$1"; } } else { $note = "0"; } if( $ARGV[1] ) { # this is output filename. $out = $ARGV[1]; } else { # $out = "$ARGV[0]".'.ave'; $out = "ave.out"; } $note_top .= "#NOAVE Created by \"$version\" on ".`date`; chop($note_top); $note_top .= " by ".`whoami`; chop($note_top); $note_top .= "@".`hostname`; $note_bot .= "$note_top"; $note_top .= "#NOAVE in directory ".`pwd`; $not_ok = ''; print "Testing number of words the same in each...\n" unless $quiet; foreach $file (@list) { # Test to see if all have same # of words # chop($wc = `wc -w $file | awk '{print \$1}'`); # so sue me Larry! chop($wc = `grep -iv '$no_ave' $file | wc -w | awk '{print \$1}'`); # so sue me Larry! $note_bot .= "#NOAVE ".`ls -l $file`; $note_bot .= "#NOAVE ${file}: $wc words\n"; if( ! $got++ ) { $count = $wc; } elsif ( $wc != $count ) { print "Not all files have same word count:\n" unless $not_ok; $files = join(' ',@list); system "wc -w $files" unless $not_ok; print "\nI can''t deal with this...Bye!\n" unless $not_ok; $not_ok = 'True' unless $ignore_wc; exit(1) unless $note; } } if ( $not_ok && $note ) { $note_top .= "#NOAVE Number of averaged files: 0\n"; open(OUT, ">$out"); print OUT "$note_top"; print OUT "#NOAVE NULL output\n"; print OUT "$note_bot"; close(OUT); exit(1); } # Now the real fun begins: print "Starting to process $list[0]\n" unless $quiet; foreach $file (@list) { open(FILE,"$file") || die "can''t open input $file"; # For each file... $lines = 0; $wtot = 0; while () { # For each line... next if /#NO\s*_*AVE/i; @words = split(/\s+/, $_); $wnum = 0; foreach $word (@words) { # For each word... $tally1{$lines,$wnum} += $word; #ave if numeric $tally2{$lines,$wnum} += ($word*$word); #dev if numeric $wnum++; } $lines++; $wtot += ($wnum + 1); } print "Did file $file: lines= $lines, words= $wtot\n" unless $quiet; } print "OK, let''s write it all out to: $out \n" unless $quiet; open(OUT,">$out") || die "can''t open output $out"; open(FILE,"$list[0]") || die "can''t open $list[0]"; # Use first file as template if ( $note > 0 ) { $note_top .= "#NOAVE Number of averaged files: $nfile\n"; print OUT "$note_top\n"; } if ( ! $flag ) { $flag = " [e1]"; } elsif ( $flag eq ' ' ) { $flag = ''; } $flag =~ s/^\s// if $NS1; if ( $both_flag eq ' ' ) { $both_flag = " [e2]"; } $both_flag =~ s/^\s// if $NS2; $lines = 0; while () { # For each line... next if /#NO\s*_*AVE/i; $plus_minus = ''; $next_ave = ''; @words = split(/\s+/, $_); $wnum = 0; $rec1 = $_; $rec2 = $_; $aveval = 0; foreach $word (@words) { # For each word... foreach $error_flag (split(/\s+/, $error_replace)) { $error_flag =~ s/(\W)/\\\1/g; # print "error_flag2 $error_flag\n"; if ( $word =~ /^$error_flag/ ) { $plus_minus = "$error_old"."$flag"; last; } else { $plus_minus = ''; } } if($tally1{$lines,$wnum}) { $tally1{$lines,$wnum} /= $nfile; $tally2{$lines,$wnum} /= $nfile; $t1 = $tally1{$lines,$wnum}; $tally2{$lines,$wnum} -= $t1*$t1; $tally2{$lines,$wnum} /= $nfile; # indep runs if($tally2{$lines,$wnum} > 0) { $tally2{$lines,$wnum} = sqrt($tally2{$lines,$wnum}); $aveval++; } else { $tally2{$lines,$wnum} = 0.0000; } $t2 = $tally2{$lines,$wnum}; # got ave and dev # now try to print out $w = $word; $fmt = '%g'; if( $guessfmt ) { $fmt = &guessfmt($w); } # go guess $t1 = sprintf("$fmt",$t1); $t2 = sprintf("$fmt",$t2); if( $guessfmt && $PRE ) { # yank off leading 0 $t1 =~ s/^0//; $t2 =~ s/^0//; } if( $guessfmt && $NEG ) { # yank off 0 in -0.xxx if($t1 < 0) { $t1 =~ s/0\./\./; } if($t2 < 0) { $t2 =~ s/0\./\./; } } if( "$t2" eq '0' ) { $t2 = ' ' unless $incl0;} if ( $next_ave ne '' ) { $ave_use = "$next_ave"; if ( $both_flag ne "" ) { $ave_error = $t1/sqrt($nfile); $ave_error = sprintf("$fmt",$ave_error); $ave_use .= " $ave_error$both_flag"; } } else { $ave_use = "$t1"; } $w =~ s/(\W)/\\\1/g; # protect non-alpha $rec1 =~ s/$w/$ave_use/; # Now actually replace with ave $rec2 =~ s/$w/$t2/; # Now actually replace with dev $error_old = "$t2"; } $wnum++; if ( $plus_minus ne '' ) { $next_ave = "$plus_minus"; } else { $next_ave = ''; } } if( $rec1 !~ /\n/ ) { $rec1 .= "\n"; } # check if lost newline if( $rec2 !~ /\n/ ) { $rec2 .= "\n"; } # if( ! $noprefix ) { # add prefix labels $_ = 'o '."$_"; $rec1 = 'a '."$rec1"; $rec2 = 'e '."$rec2"; } # print OUT "$_" unless $noo; # finally, print out line(s) if($yesa) { print OUT "$rec1"; } elsif (! $noa) { print OUT "$rec1" if $aveval; } if($yese) { print OUT "$rec2"; } elsif (! $noe) { print OUT "$rec2" if $aveval; } $lines++; } if ( $note > 1 ) { print OUT "\n$note_bot"; } sub guessfmt { # try to guess format from form of $str local($str) = @_; local($fmt, $expo, $nofront, $pre, $post); $PRE = '0'; $NEG = '0'; # PRE => .1234, NEG => -.1234 kinda thing if ( $str eq '0' ) { return "%g"; exit 0; } # if ( $str == int($str) && $str != 0 ) { return "%g"; exit 0; } if ( $str !~ /\./ ) { return "%g"; exit 0; } if ($str =~ /^-\./ ) { $NEG = 'True';} # check for -.1234 $str =~ s/-/1/g; $str =~ s/0/1/g; # print "stg= $str\n"; if ( $str =~ /^\./ ) {$nofront = 'True';} # check for .1234 if ( $str =~ /([eEdD])/ ) {$expo = "$1";} # check for 1.123e-09 if(! $nofront) { if($str =~ /(\d*)\.(\d*)/) { $pre = length($1); $post = length($2); } } else { if($str =~ /^\.(\d*)/) { $pre = '0'; $post = length($1); } } # right now, of form $pre.$post if ( $pre == 0 ) { $PRE = 'ZERO';} $pre = $pre + 1 + $post; if($expo) { $pre += 4; } if ( ! $post ) { return "%g"; exit 0; } elsif ( $expo ) { $fmt = '%'."$pre".'.'."$post".'e'; } else { $fmt = '%'."$pre".'.'."$post".'f'; } return "$fmt"; } sub do_help { # Page thru $Usage local($tfile) = "/tmp/ave_help$$"; if ( open(TFILE, ">$tfile") ) { print TFILE "$Usage"; close(TFILE); system "more $tfile; rm -f $tfile"; } elsif ( open(MORE, "|more") ) { # open pipeline to "more" print MORE "$Usage"; close(MORE); } else { # wot no more? print $LOG "$Usage"; } }