Can't get Perl Chart (gnuplot) to label all tics on axis - perl

Using Chart::Gnuplot in perl.
The x axis is to be for date/time.
I'm specifying
timeaxis => "x" (seems to work)
I'm using
timefmt => '%Y-%m-%d_%H:%M:%S'
to read the elements in the array ref passed to xdata in the Chart::Gnuplot::DataSet->new. (that seems to work too)
Using
xtics => {labelfmt => "%m-%d %H", rotate => -90}
to display the labels the way I want them. (that all seems to work too)
And indeed, everything looks good except for the fact that it's only labeling a handful of tics on the x (date/time) axis. I want to label them all (or every other one, or have some control over this)
I found lots of examples of how to do this for numbers (note dates) using... start, incr, end, etc... . And I tried lots of experiments to get this to work. But I think I've exhausted all that I can find on this googling around and I'm still stuck :-(
And so, if there is any advise on how to get this to label all the tics with the date/time, I'd very much appreciate it.

You can use xtics => { labels=>[...] } but you need to respect that
In case of timeseries data, position values must be given as quoted dates or times according to the format timefmt.
from gnuplot documentation.
Assuming the array #x contains the data set's time values in timefmt, x-tick labels can be forced at each of those times like this.
xtics => {
labels=>[map { q(').$_.q(') } #x]
}
There are plenty of ways to add single quotes around each time, but I think the map above is cleanest.
You can of course provide your own labels, just make sure they are quoted properly and the same as timefmt. I think Perl's q() quoting operator is the way to go.
labels=>[ q('2005-6-7_07:04:53') , q('2005-6-7_07:05:10') ]
Full working example
Here is a full working example, modified from the gnuplot tick examples.
#!/usr/bin/perl -w
use strict;
use Chart::Gnuplot;
# Change the date time format of the tic labels
# - the solution is the same as change the number format
# Date array
my #x = qw(
2005-6-7_07:00:00
2005-6-7_07:05:00
2005-6-7_07:10:00
2005-6-7_07:15:00
);
my #y = qw(
3562279127
3710215571
3877469703
3876354871
);
# Create the chart object
my $chart = Chart::Gnuplot->new(
output => 'test.png',
xtics => {
rotate => -90,
labelfmt => "%m-%d %H",
labels=>[map { q(').$_.q(') } #x]
},
timeaxis => "x", # declare that x-axis uses time format
);
# Data set object
my $data = Chart::Gnuplot::DataSet->new(
xdata => \#x,
ydata => \#y,
style => 'linespoints',
timefmt => '%Y-%m-%d_%H:%M:%S',
);
# Plot the graph
$chart->plot2d($data);
Without the labels, you'll get something like this.
With the labels, you'll get something like this.

Related

How to move the x-axis position using dc.js?

I have this:
But I want the x axis to run along y=0, e.g.
And ideally I'd either have the tick labels on top i.e. in the blue bit, or where they were at the bottom of the chart.
EDIT: how the chart is created.
I'm using something like:
var
ndx = crossfilter(data),
dataDimension = ndx.dimension(d => d.period),
ordinals = data.map(d => d.period),
lossGroup = dataDimension.group().reduceSum(d => d.loss),
offsets = lossGroup.all().map(d => -d.value),
chart;
// The data is like {
// period: Date (start of period, e.g. month),
// start: Integer (number at start of period/end of last period)
// loss: Integer (number lost during period)
// gain: Integer (number gained during period)
// }
chart = dc.barChart(chartElement)
.dimension(dataDimension)
// The first group is the loss
.group(lossGroup)
.stack(dataDimension.group().reduceSum(d => d.start), 'carryforward')
.stack(dataDimension.group().reduceSum(d => d.gain), 'gain')
.stackLayout(d3.layout.stack().offset(layers => offsets))
.x(d3.scale.ordinal(ordinals).domain(ordinals))
.xUnits(dc.units.ordinal)
.elasticY(true)
.renderLabel(false)
// The first group is the loss
.title(item => 'Loss: ' + item.value)
.title('carryforward', item => 'Sustained: ' + item.value)
.title('gain', item => 'Gain: ' + item.value)
.renderTitle(true);
dc.renderAll();
Hmm, I guess you are using .stackLayout() to get the negative stacking. Since dc.js doesn't “look inside” this setting, I don't think there is anything built-in to offset the axis. You would probably need to use a pretransition handler to move it.
As for moving the tick labels, you could use .title() instead, like in this example. And then set the tick label to empty.
Best I can think of, not really an answer but more than a comment. :-)

Formatting GD::Graph x-axis in Perl

I'm producing a graph of prices over time. Because I have dates on the x-axis, I have converted them to days since the epoch. Days since the epoch is not a very clear value to display on the graph so I want to convert them back to human readable dates using the x_number_format option.
But...it doesn't appear to be called as the graph is rendered.
I've created the following test code to demonstrate the problem.
use strict;
use GD::Graph::points;
# Generate some random data!
my #x_data;
my #y_data;
for (1...20) {
push #x_data, $_;
push #y_data, rand(20) + 10;
}
# This is never called - possible bug!
sub x_format {
print "X Formatter!\n";
return " - $_[0] - ";
}
# This gets called for every Y-axis point
sub y_format {
print "Y Formatter!\n";
return " - $_[0] - ";
}
my $graph=GD::Graph::points->new(1000,450);
$graph->set(
y_label => 'Random numbers',
y_number_format => \&y_format,
x_number_format => \&x_format,
x_label => 'Sequential meaningless numbers',
x_labels_vertical => 1,
x_plot_values => 1,
);
my #data=(
[ #x_data ],
[ #y_data ],
);
open PNG, ">temp.png";
binmode PNG;
print PNG $graph->plot(\#data)->png;
close PNG;
system("temp.png");
This test code produces a graph as expected and prints Y Formatter! 6 times. One for each point on the y-axis. However, it doesn't print X Formatter! and doesn't format the x-axis.
I have tried formatting the x-axis values more directly with
x_number_format => sub { " - $_[0] - " },
That doesn't format the x-axis either.
Am I doing something glaring stupid or is this a bug in GD:Graph?
There are no bug reports for this issue in the GD::Graph bug page
By inspecting the source, I can see that you need to set
x_tick_number to a defined value for the x_number_format callback to be called.
So you can try something like this:
$graph->set(
y_label => 'Random numbers',
y_number_format => \&y_format,
x_number_format => \&x_format,
x_tick_number => 6,
x_label => 'Sequential meaningless numbers',
x_labels_vertical => 1,
x_plot_values => 1,
);
According to the documentation:
x_tick_number
If set to 'auto', GD::Graph will attempt to format the X
axis in a nice way, based on the actual X values. If set to a number,
that's the number of ticks you will get. If set to undef, GD::Graph
will treat X data as labels. Default: undef.

How to convert PDL image to GdkPixbuf

I'm trying to display a graph generated by PDL (using PLplot) inside a Gtk3 app. When I try the following code, I see two problems:
$pdlImg isn't a GdkPixbuf so new_from_pixbuf() doesn't work.
$pdlImg appears to be empty as because the error message prints out the 10x10x3 array as a string and they're all zeroes.
Code:
#!/usr/bin/perl -w
use strict;
use PDL;
use PDL::Graphics::PLplot;
use Gtk3 -init;
my $pdlImg = zeroes(byte, 10, 10, 3);
my $pl = PDL::Graphics::PLplot->new(DEV => 'mem', MEM => $pdlImg);
my $x = sequence(10);
my $y = $x**2;
$pl->xyplot($x, $y);
$pl->close;
my $win = Gtk3::Window->new;
my $img = Gtk3::Image->new_from_pixbuf($pdlImg);
$win->add($img);
$win->show_all;
Gtk3::main();
To answer your first question, you are having PLplot put the plot into a piddle that is 10 pixels wide and 10 pixels high. I'm not sure if you're just going to get one corner of the normal plot in that case, or if you're getting the whole plot sampled into those 10x10 pixels. But in either case it's no surprise that $pdlImg is entirely zeroes. Try passing in a piddle with larger size (perhaps 1000, 1000, 3), or perhaps even using MEM => $pdlImg=null when you create the PLplot plot object.
I can't help with your second question, I have no experience with Gtk3, sorry.

Cassandra: get_range_slices of TimeUUID super column?

I have a schema of Row Keys 1-n. In each row there are a variable number of supercolumns with a TimeUUID 'name'. Im hoping to be able to query this data by a time range.
Two issues have come up:
in KeyRange -> the values that I put in for 'start_key' and 'end_key' are getting misunderstood (for lack of a better term) by Thrift. Experimenting with different groups of values Im not seeing what I expect and often get back something completely unexpected.
Example: my row keys are running from 1-1000 with lots of random gaps. I put start_key = 50 and end_key = 20 .. and I get back rows with keys ranging from 99 to 414.
Example: I have a known row with key = 13. Putting this value into start_key and end_key gives me no results.
Second issue: even when I do get results the 'columns' portion of the 'keyslice' is always empty. I have checked via cassandra-cli and I know there is data.
Im using Perl as follows:
my $slice_range = new Cassandra::SliceRange();
$slice_range->{ start } = create_UUID( UUID::Tiny::UUID_TIME, "2010-12-24 00:00:00" );
$slice_range->{ finish } = create_UUID( UUID::Tiny::UUID_TIME, "2011-12-25 00:00:00" );
my $slice_predicate = new Cassandra::SlicePredicate();
$slice_predicate->{ slice_range } = $slice_range;
my $key_range = new Cassandra::KeyRange();
$key_range->{ start_key } = 13;
$key_range->{ end_key } = 13;
my $result = $client->get_range_slices( $column_parent, $slice_predicate, $key_range, $consistency_level );
print Dumper( $result );
Clearly Im misunderstanding some basic precept.
EDIT: It turns out that the Perl library Im using is not properly documented. The UUID creation was not working as advertised. I opened it up, fixed it, and now its all going a bit more as I was expecting. I can slice my supercolumns by date/time range. Still working on getting the key range portion to work.
http://wiki.apache.org/cassandra/FAQ#range_rp covers why you're not seeing what you expect with key ranges.
You need to specify a SlicePredicate that contains the actual range of what you're trying to select. The default of no column_names and no slice_range will result in the empty columns list that you see.

How can I write in scientific notation using Perl formats?

I've always used printf, and I've never used write/format. Is there any way to reproduce printf("%12.5e", $num) using a format? I'm having trouble digesting the perlform documentation, but I don't see a straightforward way of doing this.
EDIT: based on the answers I got, I'm just gonna keep on using printf.
Short answer, don't use formats.
Unresearched answer, sure, just use sprintf:
#!/usr/bin/perl
use strict;
use warnings;
our $num = .005;
write;
format STDOUT =
#>>>>>>>>>>>>>>>>>
sprintf("%12.5e", $num)
.
Seriously, if you need something like Perl 5 formats, take a look at Perl6::Form (note, this is a Perl 5 module, it just implements the proposed Perl 6 version of formats).
I totally agree with Chas. Owens on formats in general. Format was really slick 15 years ago, but format has not kept up with the advancements of the rest of Perl.
Here is a technique for line oriented output that I use time to time. You can use formline which is one of the public internal functions used by format. Format is page oriented. It is very hard to do things like span columns or change the format by line depending on the data. You can format a single line using the same text formatting logic used by format and then output that result yourself.
A (messy) example:
use strict; use warnings;
sub print_line {
my $pic=shift;
my #args=#_;
formline($pic,#args);
print "$^A\n";
$^A='';
}
my ($wlabel, $wlow, $whigh, $wavg)=(0,0,0,0);
my ($plabel,$plow,$phigh, $pavg);
my ($s_low,$s_high,$s_avg)=qw(%.2f %.2e %.2f);
my #results=( ["Label 1", 3.445, 0.00006678, .025],
["Label 2", 12.5555556, 55.112, 1.11],
["Wide Label 3", 1231.11, 1555.0, 66.66] );
foreach (#results) {
my $tmp;
$tmp=length($_->[0]);
$wlabel=$tmp if $tmp>$wlabel;
$tmp=length(sprintf($s_low,$_->[3]));
$wlow=$tmp if $tmp>$wlow;
$tmp=length(sprintf($s_high,$_->[2]));
$whigh=$tmp if $tmp>$whigh;
$tmp=length(sprintf($s_avg,$_->[1]));
$wavg=$tmp if $tmp>$wavg;
}
print "\n\n";
my #a1=("Label", "Rate - Operations / sec");
my #a2=("Text", "Average", "High", "Low");
my #a3=("----------", "-------", "----", "---");
my $l1fmt="#".'|' x $wlabel." #".'|'x($whigh+$wavg+$wlow+6);
my $l2fmt="#".'|' x $wlabel." #".'|' x $wavg." #".'|' x $whigh .
" #".'|' x $wlow;
print_line($l1fmt,#a1);
print_line($l2fmt,#a2);
print_line($l2fmt,#a3);
$plabel="#".'>' x $wlabel;
$phigh="#".'>' x $whigh;
$pavg="#".'>' x $wavg;
$plow="#".'<' x $wlow;
foreach (#results) {
my $pic="$plabel $pavg $phigh $plow";
my $mark=$_->[0];
my $avg=sprintf($s_avg,$_->[1]);
my $high=sprintf($s_high,$_->[2]);
my $low=sprintf($s_low,$_->[3]);
print_line($pic,$mark,$avg,$high,$low);
}
print "\n\n";
Outputs this:
Label Rate - Operations / sec
Text Average High Low
---------- ------- ---- ---
Label 1 3.44 6.68e-05 0.03
Label 2 12.56 5.51e+01 1.11
Wide Label 3 1231.11 1.56e+03 66.66
Notice that the width of the columns is set based on the width of the data as formatted by the sprintf format string. You can then left, center, right justify that result. The "Low" data column is left justified, the rest of the data are right justified. You can change this by the symbol used in the scalar $plow and it is the same as format syntax. The labels at the top are centered and the "Rate - Operations / sec" label spans 3 columns.
This is obviously not "production ready" code, but you get the drift I think. You would need to further check the total width of the columns against desired width, etc. You have to manually do some of the work that format does for you, but you have far more flexibility with this approach. It is very easy to use this method for several sections of a line with sprintf for example.
Cheers.