From d68b331af688e94c1e5e88ecc339cff8c59ffc99 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Thu, 11 Mar 2021 13:46:26 -0800 Subject: [PATCH] added --using, --usingall --- bin/feedgnuplot | 146 +++++++++++++++++++++++++++++------ completions/bash/feedgnuplot | 2 + completions/zsh/_feedgnuplot | 2 + t/plots.t | 17 +++- t/using-individual.ref | 39 ++++++++++ t/usingall.ref | 39 ++++++++++ 6 files changed, 219 insertions(+), 26 deletions(-) create mode 100644 t/using-individual.ref create mode 100644 t/usingall.ref diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 0a86475..003d6e3 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -91,6 +91,7 @@ sub interpretCommandline $options{curvestyle} = []; $options{style} = []; $options{every} = []; + $options{using} = []; $options{histogram} = []; $options{x1y2} = []; $options{x2y1} = []; @@ -121,6 +122,7 @@ sub interpretCommandline 'x2=s@', 'y2=s@', 'x1y2=s@', 'x2y1=s@', 'x2y2=s@', 'style=s{2}', 'curvestyle=s{2}', 'curvestyleall=s', 'styleall=s', 'with=s', 'extracmds=s@', 'set=s@', 'unset=s@', 'every=s{2}', 'everyall=s', + 'using=s{2}', 'usingall=s', 'square!', 'square_xy!', 'square-xy!', 'squarexy!', 'hardcopy=s', 'maxcurves=i', 'monotonic!', 'timefmt=s', 'equation=s@', 'equation-below=s@', 'equation-above=s@', 'image=s', @@ -185,7 +187,7 @@ sub interpretCommandline @{$options{$listkey}} = map split('\s*,\s*', $_), @{$options{$listkey}} if defined $options{$listkey}; } - for my $listkey (qw(curvestyle rangesize tuplesize every)) + for my $listkey (qw(curvestyle rangesize tuplesize every using)) { next unless defined $options{$listkey}; my @in = @{$options{$listkey}}; @@ -260,7 +262,7 @@ sub interpretCommandline # arrays in order to preserve the ordering. I parse both of these into hashes # because those are useful to have later. After this I can access individual # legends with $options{legend_hash}{curveid} - for my $listkey (qw(legend curvestyle rangesize every)) + for my $listkey (qw(legend curvestyle rangesize every using)) { $options{"${listkey}_hash"} = {}; @@ -810,6 +812,7 @@ sub mainThread for my $what_options_prefix_suffix ( ['curvestyle', 'extraoptions', '', ' ' ], ['every', 'everyoptions', 'every ', ' ' ], + ['using', 'usingoptions', 'using ', ' ' ], ['legend', 'title', '', '' ]) { my ($what, $options, $prefix, $suffix) = @$what_options_prefix_suffix; @@ -843,8 +846,16 @@ sub mainThread print PIPE "set boxwidth $options{binwidth}\n" . "histbin(x) = $options{binwidth} * floor(0.5 + x/$options{binwidth})\n"; - - setCurveAsHistogram( $_ ) foreach (@{$options{histogram}}); + foreach my $id (@{$options{histogram}}) + { + # With histograms I have 2d plots with rangesize=1. I thus give gnuplot two + # values for each point: a domain and a range. For histograms I ignore the + # domain, so I get the statistics of the 2nd column: $2 + addOption($id, + 'usingoptions', + 'using (histbin($2)):(1.0) smooth ' . $options{histstyle}, + 'do-not-override'); + } if(@{$options{x2y1}} || @{$options{x2y2}}) { @@ -1185,10 +1196,14 @@ sub updateCurveOptions { $title = $id; } my $titleoption = defined $title ? "title \"$title\"" : "notitle"; - my $histoptions = $curve->{histoptions} || ''; - my $usingoptions = ''; - if( $options{timefmt} && !$histoptions ) + my $usingoptions = $curve->{usingoptions}; + if( length($usingoptions) ) + { + # user specified a 'using' option. I just do that, and don't look at + # anything else + } + elsif( $options{timefmt} ) { # with --timefmt I need an explicit 'using' specification. I specify the # columns as 1:2:3..... I need the right number of columns (this is given @@ -1214,7 +1229,7 @@ sub updateCurveOptions } } - $curve->{options} = "$curve->{everyoptions} $histoptions $usingoptions $titleoption $curve->{extraoptions}"; + $curve->{options} = "$curve->{everyoptions} $usingoptions $titleoption $curve->{extraoptions}"; } sub getCurve @@ -1241,6 +1256,9 @@ sub getCurve everyoptions => (!exists $options{every_hash}{$id} && exists $options{everyall}) ? "every $options{everyall} " : ' ', + usingoptions => (!exists $options{using_hash}{$id} && + exists $options{usingall}) ? + "using $options{usingall} " : '', title => '', datastring => '', datastring_meta => [], @@ -1274,25 +1292,14 @@ sub getCurve sub addOption { - my ($id, $which, $str) = @_; + my ($id, $which, $str, $do_not_override) = @_; my $curve = getCurve($id); - $curve->{$which} .= $str; - updateCurveOptions($curve, $id); -} - -sub setCurveAsHistogram -{ - my ($id, $str) = @_; - - my $curve = getCurve($id); - - # With histograms I have 2d plots with rangesize=1. I thus give gnuplot two - # values for each point: a domain and a range. For histograms I ignore the - # domain, so I get the statistics of the 2nd column: $2 - $curve->{histoptions} = 'using (histbin($2)):(1.0) smooth ' . $options{histstyle}; - - updateCurveOptions($curve, $id); + if(!$do_not_override || length($curve->{$which})==0) + { + $curve->{$which} .= $str; + updateCurveOptions($curve, $id); + } } # remove all the curve data @@ -1612,6 +1619,79 @@ Note that while gnuplot supports the time/date on any axis, I currently supports it I as the x-axis domain. This may change in the future. +=head3 'using' expressions + +We just described how feedgnuplot parses its input data. When passing this data +to gnuplot, each curve is sent independently. The domain appears in the leading +columns followed by C<--rangesize> columns to complete each row. Without +C<--domain>, feedgnuplot explicitly writes out sequential integers. gnuplot then +knows how many values it has for each point, and it knows which style we're +using, so it's able to interpret the data appropriately, and to make the correct +plot. + +As an example, if gnuplot is passed 2 columns of data, and it is plotting C, it will use column 1 for the x coordinate and column 2 for the y +coordinate. This is the default behavior, but the meaning of each column can be +controlled via a C expression in gnuplot (not feedgnuplot; keep reading). +The default is sequential integers, so this example uses C by +default. We can flip the meaning of the columns by passing C. +Arbitrary expressions may be specified by enclosing each field in C<()>, and +using C<$> to denote each data column. So to use the 2nd column as the x +coordinate and the sum of the two columns as the y coordinate, C is passed. Furthermore, the number of columns can vary. For instance +gnuplot can read the same two columns of data, but produce a plot with the extra +column encoding the sum as the color: C. +Please see the gnuplot documentation for lots of detail. + +That's how I works. Most of the time, I doesn't pass any +C expressions at all, and gnuplot does the default thing. But if we want +to do something fancy, feedgnuplot supports C<--using curveID expression> and +C<--usingall expression>. So we can plot a parabola: + + seq 100 | feedgnuplot --lines --usingall '1:($2*$2)' + +This is powerful, but there are some things to keep in mind: + +=over + +=item + +C<--using> overrides whatever C expression feedgnuplot was going to pass. +feedgnuplot passes a C expression only if C<--histogram> or C<--timefmt> +or C<--xticlabels> are given. So if C<--using> is given together with any of +these, the user must take care to do the right thing (whatever that means at +that time). + +=item + +The C<--tuplesize> controls the data passed to feedgnuplot and the data then +passed to gnuplot. It does I directly control how gnuplot eventually +interprets the data: C<--using> does that. So for instance we can plot +color-coded points: + + seq 10 | feedgnuplot --with 'points pt 7 palette' --usingall '1:2:2' + +Here feedgnuplot read 1 column of data. It defauled to C<--tuplesize 2>, so it +passed 2 columns of data to gnuplot. gnuplot then produced 3 values for each +point, and plotted them as indicated with the C style. + +=item + +You I need a column of data to generate a curve. You might want to use a +C expression to plot a time series I its cumulative integral. The +C expression can compute the integral, but you I pass in the data +twice; once for each curve to plot: + + seq 100 | \ + awk '{print $1,$1}' | \ + feedgnuplot \ + --extracmds 'sum=0' \ + --extracmds 'accum(x) = (sum=sum+x)' \ + --using 1 '1:(accum($2))' \ + --lines --y2 1 + +=back + =head2 Real-time streaming data To plot real-time data, pass in the C<--stream [refreshperiod]> option. Data @@ -2044,6 +2124,22 @@ I the curves. =item +C<--using curveID expression> + +Specifies a C expression to micromanage the plot. This is a powerful +option that allows gnuplot to interpret the input data in arbitrary ways. A +C expression tells gnuplot how to map the input columns of data to tuples +expected by the plotting style. Please see the L section above for more detail. + +=item + +C<--usingall expression> + +Global "using" expressions. This works exactly like C<--using>, except it +applies to I the curves. + +=item + C<--extracmds xxx> Additional commands to pass on to gnuplot verbatim. These could contain extra diff --git a/completions/bash/feedgnuplot b/completions/bash/feedgnuplot index bbb3b5a..aa96fbe 100644 --- a/completions/bash/feedgnuplot +++ b/completions/bash/feedgnuplot @@ -67,4 +67,6 @@ complete -W \ --zmax \ --zmin \ --xticlabels \ + --using \ + --usingall \ --vnlog ' feedgnuplot diff --git a/completions/zsh/_feedgnuplot b/completions/zsh/_feedgnuplot index 53b40c7..4168ddb 100644 --- a/completions/zsh/_feedgnuplot +++ b/completions/zsh/_feedgnuplot @@ -58,6 +58,8 @@ _arguments -S '*--style[Additional styles for a curve]:curve id: :style:' \ '*--every[Decimation factor for a curve]:curve id: :decimation factor:' \ '--everyall[Decimation factor for ALL curves]:decimation factor' \ + '*--using[Column specification for a curve]:curve id: :column specification:' \ + '--usingall[Column specification ALL curves]:column specification' \ '(--3d)*--histogram:plot to treat as a histogram:' \ '--binwidth:Histogram bin width:' \ '--histstyle:Style of histogram:(frequency fnormal unique cumulative cnormal)' \ diff --git a/t/plots.t b/t/plots.t index 0874eb5..2c257ce 100644 --- a/t/plots.t +++ b/t/plots.t @@ -39,7 +39,7 @@ BEGIN { } } -use Test::More tests => 88; +use Test::More tests => 92; use File::Temp 'tempfile'; use IPC::Run 'run'; use String::ShellQuote; @@ -298,6 +298,21 @@ tryplot( testname => 'every-individual', options => [qw(--points --every 0 2 --every 1 3)], refplot => 'every-individual.ref' ); +tryplot( testname => 'usingall', + cmd => q{seq 12 | gawk '{print $1,$1+1}'}, + options => [qw(--style 0), 'with points pt variable', + qw(--style 1), 'with linespoints pt variable', + qw(--usingall 1:2:($2) --unset grid)], + refplot => 'usingall.ref' ); + +tryplot( testname => 'using-individual', + cmd => q{seq 12 | gawk '{print $1,$1+1}'}, + options => [qw(--style 0), 'with points pt variable', + qw(--using 0 1:2:($2)), + qw(--using 1 1:(12-$2)), + qw(--unset grid)], + refplot => 'using-individual.ref' ); + SKIP: { diff --git a/t/using-individual.ref b/t/using-individual.ref new file mode 100644 index 0000000..ed22419 --- /dev/null +++ b/t/using-individual.ref @@ -0,0 +1,39 @@ + 12 +------------------------------------------------------------------------------------------+ + | + + + + + | + | | + | K | + | | + 10 |-+ B J +-| + | | + | | + | B I | + | | + 8 |-+ B H +-| + | | + | | + | B G | + | | + 6 |-+ B F +-| + | | + | | + | E B | + | | + | | + 4 |-+ D B +-| + | | + | C B | + | | + | | + 2 |-+ B B +-| + | | + | A B | + | | + | | + 0 |-+ B +-| + | | + | | + | | + | + + + + + | + -2 +------------------------------------------------------------------------------------------+ + 0 2 4 6 8 10 12 + diff --git a/t/usingall.ref b/t/usingall.ref new file mode 100644 index 0000000..8b3579a --- /dev/null +++ b/t/usingall.ref @@ -0,0 +1,39 @@ + 14 +------------------------------------------------------------------------------------------+ + | + + + + + | + | | + | ##| + | #### | + 12 |-+ #L# +-| + | ## | + | ## | + | ##K# K | + | #### | + 10 |-+ #J# J +-| + | ## | + | ## | + | ##I# I | + | #### | + 8 |-+ #H# H +-| + | ## | + | ## | + | #G# G | + | ### | + | ## | + 6 |-+ ##F# F +-| + | #### | + | #E# E | + | ## | + | ## | + 4 |-+ ##D# D +-| + | #### | + | #C# C | + | ## | + | ## | + 2 |-+ B# B +-| + | | + | A | + | | + | + + + + + | + 0 +------------------------------------------------------------------------------------------+ + 0 2 4 6 8 10 12 +