From cb49a0997a4aabe814aef7bd7787219d58cc9b85 Mon Sep 17 00:00:00 2001 From: Ole Tange Date: Mon, 27 Dec 2021 19:34:00 +0100 Subject: [PATCH] plotpipe: Allow expressions. --- plotpipe/plotpipe | 122 +++++++++++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 33 deletions(-) diff --git a/plotpipe/plotpipe b/plotpipe/plotpipe index 5baeda0..ecd191b 100755 --- a/plotpipe/plotpipe +++ b/plotpipe/plotpipe @@ -9,7 +9,7 @@ plotpipe - Plot CSV data from a pipe =head1 SYNOPSIS -I | B [-n] [-H] [-0] [--log axis] [-C str] [-h] [-V] +I | B [-n] [-H] [-0] [--log axis] [-C str] [-h] [-V] [I] =head1 DESCRIPTION @@ -24,6 +24,16 @@ titles on the plot. =over 4 +=item I + +Evaluate I. I consists of parts separated by +,. Each part is evaluated as a Perl expression. You can use {I} as +the column value: + + seq 10 | plotpipe '{1},{1},{1}**2,sqrt({1})' + (echo x y;paste <(seq 10) <(seq 11 20)) | + plotpipe {1},{2},{2}/{1} + =item B<--colsep> I =item B<-C> I @@ -76,7 +86,7 @@ Use \0 (NUL) instead of newline (\n) as record separator. =item B<-s> I -Smooth values. Take the average of I y-values. +Smooth values. Take the median of I y-values. =item B<--version> @@ -91,15 +101,15 @@ Show version =head1 EXAMPLE -Plot the points (1,100) .. (100,1): +Plot (1,100) .. (100,1): paste <(seq 100 -1 1) | plotpipe -Plot the points (1,101) .. (100,200): +Plot (1,101) .. (100,200): paste <(seq 100) <(seq 101 200) | plotpipe -Plot the points (1,101) .. (100,200) and (1,300) .. (100,102): +Plot (1,101) .. (100,200) and (1,300) .. (100,102): paste <(seq 100) <(seq 101 200) <(seq 300 -2 102) | plotpipe @@ -146,11 +156,10 @@ input.csv: You are not limited to a simple graph, but can also do XY-line plots. seq 0 0.001 6.29 | - perl -nE 'say sin($_*100)*0.3+0.5*cos($_*2),",", - sin($_*2)-cos($_*100)*0.3,",", - sin($_)+cos($_*99),",", - sin($_*3)-cos($_*101)' | - plotpipe + plotpipe 'sin({1}*100)*0.3+0.5*cos({1}*2), + sin({1}*2)-cos({1}*100)*0.3, + sin({1})+cos({1}*99), + sin({1}*3)-cos({1}*101)' =head1 LIMITS @@ -189,7 +198,10 @@ B uses B and B. =head1 SEE ALSO -B, B +B, B, B +(http://ploticus.sourceforge.net/doc/welcome.html), B +(https://github.com/dkogan/feedgnuplot), B +(https://github.com/tenox7/ttyplot) =cut @@ -368,13 +380,15 @@ if(not GetOptions(options_hash())) { } $Global::progname = "plotpipe"; -$Global::version = 20210222; +$Global::version = 20210302; if($opt::version) { version(); exit 0; } if($opt::help) { help(); exit 0; } if($opt::null) { $/ = "\0"; } +my @eval_function = split/,/, join(" ",@ARGV); + # Read csv -my @csv = <>; +my @csv = ; # Title = lines starting with # my @title = map { s/^#//; s/"/''/g; $_ } map { "$_" } grep { /^#/ } @csv; @@ -408,22 +422,41 @@ if($opt::header) { } # Convert input to perl table -my $ncols = split /$opt::colsep/, $csv[0]; my @tbl; -if($ncols >= 2 and not $opt::nox) { - # Column 1 = x-axis - for(@csv) { - chomp; - my @row = split /$opt::colsep/, $_; - push @tbl,\@row; +for(@csv) { + chomp; + my @row = split /$opt::colsep/, $_; + push @tbl,\@row; +} + +# Eval +if(@eval_function) { + for(@eval_function) { + my $new_header = $_; + $new_header =~ s/\{(\d+)\}/$header[$1-1] || "\\\\{$1\\\\}"/ge; + push @new_header, $new_header; } + $opt::header = 1; + @header = @new_header; + for my $row (@tbl) { + my @newrow = map { + s/\{(\d+)\}/$row->[$1-1]/g; + eval "$_" + } map { $_ } @eval_function; + $row = \@newrow; + } +} + +# Add x-axis if needed +my $ncols = $#{$tbl[0]}+1; +if($ncols >= 2 and not $opt::nox) { + # Column 1 = x-axis => Data is fine } else { - # All data = y-axis, invent x-axis + # All data = y-axis => Invent x-axis my $x = 0; - for(@csv) { - chomp; - my @row = ($x++, split /$opt::colsep/, $_); - push @tbl,\@row; + for my $row (@tbl) { + my @newrow = ($x++, @$row); + $row = \@newrow; } # Prepend dummy header for x-axis unshift(@header,""); @@ -438,19 +471,42 @@ if($opt::smooth) { exit(255); } my $smooth = $opt::smooth-1; - for(my $x = 0; $x < $smooth; $x++) { - for (my $y = 0; $y <= $#{$tbl[$x]}; $y++) { - $sum[$y] += $tbl[$x][$y] / $opt::smooth; - } + + sub median { + return ((sort { $a <=> $b } @_)[$#_/2]); } - for(my $x = $smooth; $x <= $#tbl; $x++) { + sub avg { + my $s=0; + map { $s += $_ } @_; + return ($s / ($#_+1)); + } + + for(my $x = 0; $x < $#tbl-$smooth; $x++) { for (my $y = 0; $y <= $#{$tbl[$x]}; $y++) { - $sum[$y] += $tbl[$x][$y] / $opt::smooth; - $new[$x-$smooth][$y] = $sum[$y]; - $sum[$y] -= $tbl[$x-$smooth][$y] / $opt::smooth; + my @med; + for(my $m = $x; $m < $x+$smooth; $m++) { + push @med, $tbl[$m][$y]; + } + $new[$x+$smooth/2][$y] = median(@med); } } @tbl = @new; + + sub do_average() { + for(my $x = 0; $x < $smooth; $x++) { + for (my $y = 0; $y <= $#{$tbl[$x]}; $y++) { + $sum[$y] += $tbl[$x][$y] / $opt::smooth; + } + } + for(my $x = $smooth; $x <= $#tbl; $x++) { + for (my $y = 0; $y <= $#{$tbl[$x]}; $y++) { + $sum[$y] += $tbl[$x][$y] / $opt::smooth; + $new[$x-$smooth][$y] = $sum[$y]; + $sum[$y] -= $tbl[$x-$smooth][$y] / $opt::smooth; + } + } + @tbl = @new; + } } # Save data to tmpfile that will be read by Gnuplot