added --using, --usingall

This commit is contained in:
Dima Kogan 2021-03-11 13:46:26 -08:00
parent 83139d7ad0
commit d68b331af6
6 changed files with 219 additions and 26 deletions

View File

@ -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);
if(!$do_not_override || length($curve->{$which})==0)
{
$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);
}
# remove all the curve data
@ -1612,6 +1619,79 @@ Note that while gnuplot supports the time/date on any axis, I<feedgnuplot>
currently supports it I<only> 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<with
points>, 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<using> expression in gnuplot (not feedgnuplot; keep reading).
The default is sequential integers, so this example uses C<using 1:2> by
default. We can flip the meaning of the columns by passing C<using 2:1>.
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<using
2:($1+$2)> 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<using 1:2:($1+$2) with points palette>.
Please see the gnuplot documentation for lots of detail.
That's how I<gnuplot> works. Most of the time, I<feedgnuplot> doesn't pass any
C<using> 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<using> expression feedgnuplot was going to pass.
feedgnuplot passes a C<using> 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<not> 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<points palette> style.
=item
You I<always> need a column of data to generate a curve. You might want to use a
C<using> expression to plot a time series I<and> its cumulative integral. The
C<using> expression can compute the integral, but you I<must> 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<all> the curves.
=item
C<--using curveID expression>
Specifies a C<using> expression to micromanage the plot. This is a powerful
option that allows gnuplot to interpret the input data in arbitrary ways. A
C<using> expression tells gnuplot how to map the input columns of data to tuples
expected by the plotting style. Please see the L</"'using' expressions"> section above for more detail.
=item
C<--usingall expression>
Global "using" expressions. This works exactly like C<--using>, except it
applies to I<all> the curves.
=item
C<--extracmds xxx>
Additional commands to pass on to gnuplot verbatim. These could contain extra

View File

@ -67,4 +67,6 @@ complete -W \
--zmax \
--zmin \
--xticlabels \
--using \
--usingall \
--vnlog ' feedgnuplot

View File

@ -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)' \

View File

@ -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:
{

39
t/using-individual.ref Normal file
View File

@ -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

39
t/usingall.ref Normal file
View File

@ -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