Advanced Graphing

From Scribus Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

This is an article about a script to create axes with tick marks, then plot data points to the scale of the graph.

What I want to do is explain the workflow that led to the script, and I imagine will continue to suggest improvements. What I wanted to do was to accumulate information about when orders were coming in according to time of day, then see if as data accumulated whether patterns became apparent.

Beginnings

Like many such projects it began quite manually, making a graph structure by hand, trying out and not liking a grid, and in essence making my own grid to lay out data points. I was getting the data from a spreadsheet, collating manually since there wasn't so much at first, then making a polyline, again manually. This gave me an opportunity to fiddle with various parameters to get the look I wanted. This ugly self-created grid would not be printed or exported to the PDF.

Earlygraph.png

The data collection began in mid-April, and continues. Quite early, and you can easily see it in this graph from late April, there was this interesting peak at 4pm, so this led if nothing else to continuing the process to see what would happen with larger numbers.

As I began updating the graph, I realized I needed some help from the manual drudgery. The first thing I did was to enter the information I wanted into a postgresql database, so I could dissect in various ways, such as by time of day. Later, as the number of data rose, I wanted to be able to look at this by day of week also. This also was helped by having postgresql, where I could for example transform all instances of 23May to M23May, thus making selecting for day of week easier.

This was still quite manual, since I would export an ordered list to a text file, edit it a bit to making counting how many orders came in per hour, then use Scribus – Properties > Shape > Edit to manually change the nodes of the graph line as needed. Doable but tedious. what especially became annoying was counting lines of a text file, but of course postgresql can use an external script if you write one.

select * from orders order by time;
select * from orders where time < '01:00' order by time;
select * from orders where time > '00:59' and time < '02:00' order by time;
select * from orders where time > '01:59' and time < '03:00' order by time;
select * from orders where time > '02:59' and time < '04:00' order by time;
select * from orders where time > '03:59' and time < '05:00' order by time;
select * from orders where time > '04:59' and time < '06:00' order by time;
select * from orders where time > '05:59' and time < '07:00' order by time;
select * from orders where time > '06:59' and time < '08:00' order by time;
select * from orders where time > '07:59' and time < '09:00' order by time;
select * from orders where time > '08:59' and time < '10:00' order by time;
select * from orders where time > '09:59' and time < '11:00' order by time;
select * from orders where time > '10:59' and time < '12:00' order by time;
select * from orders where time > '11:59' and time < '13:00' order by time;
select * from orders where time > '12:59' and time < '14:00' order by time;
select * from orders where time > '13:59' and time < '15:00' order by time;
select * from orders where time > '14:59' and time < '16:00' order by time;
select * from orders where time > '15:59' and time < '17:00' order by time;
select * from orders where time > '16:59' and time < '18:00' order by time;
select * from orders where time > '17:59' and time < '19:00' order by time;
select * from orders where time > '18:59' and time < '20:00' order by time;
select * from orders where time > '19:59' and time < '21:00' order by time;
select * from orders where time > '20:59' and time < '22:00' order by time;
select * from orders where time > '21:59' and time < '23:00' order by time;
select * from orders where time > '22:59' order by time;

So what does this get you? Here is a fragment of the output near the end of the list from the first command into the next few lines:

 A26Jun | 23:29:00
 T20Jul | 23:44:00
 F28May | 23:48:00
 A10Jul | 23:50:00
 F21May | 23:55:00
(603 rows)

  date  |   time   
--------+----------
 M19Jul | 00:00:00
 A22May | 00:07:00
 H22Jul | 00:09:00
 S27Jun | 00:17:00
 A26Jun | 00:17:00
 W21Jul | 00:19:00
 H3Jun  | 00:21:00
 M19Apr | 00:25:00
 W12May | 00:38:00
 F23Jul | 00:38:00
 T20Jul | 00:40:00
 H24Jun | 00:43:00
 W28Apr | 00:43:00
 T13Jul | 00:47:00
 W21Jul | 00:50:00
 T29Jun | 00:51:00
 H10Jun | 00:59:00
(17 rows)

  date  |   time   
--------+----------
 M19Jul | 01:09:00
 T6Jul  | 01:16:00
 S18Jul | 01:19:00
 H15Apr | 01:20:00
 H3Jun  | 01:20:00
 T29Jun | 01:21:00
 W23Jun | 01:25:00
 H22Jul | 01:38:00
 H20May | 01:42:00
 H24Jun | 01:43:00
 F2Jul  | 01:55:00
(11 rows)

Now I get the list broken down, and can even use these indicators of the number of items that are in each group, but really that is all that I want. I could edit this list, but much easier is to use grep:

grep row all_orders.txt

(603 rows)
(17 rows)
(11 rows)
(13 rows)
(10 rows)
(17 rows)
(14 rows)
(22 rows)
(17 rows)
(23 rows)
(37 rows)
(36 rows)
(37 rows)
(35 rows)
(29 rows)
(31 rows)
(37 rows)
(42 rows)
(32 rows)
(36 rows)
(35 rows)
(25 rows)
(19 rows)
(17 rows)
(11 rows)

Now this is a real timesaver. I just have to remember that the top item is the total number for a 24 hour period, then comes midnight–1am, 1am–2am, and so on.

Finally, On to the Script

The Current Working Script (subject to change)

#!/usr/bin/env python

# File: axes_graphing.py
# originally 2006.05.17 as graph2.py on Scribus wiki
# this version 2010.07.25
# creates graph with axes and plots Y values
# X values are fixed

"""
This script as written assumes a US Letter page in landscape orientation.
The various position and dimension values you see are appropriate for page units
of points. If you prefer other units, there are commands to get your units type, switch
to points, and then at the end back to your preferred units.
No headers are created and even the axis labels are not part of this script.
It was written to plot events over a 24 hour day, so the X axis denotes hours
(i.e., 24 tick points), and the Y axis number of events.

The only user input when the script runs is to specify the range of the Y axis,
and indicate the value of individual tick marks. The reason for not making this 
automatic is that you might wish to have a family of graphs with a range of values
from graph to graph, yet keep the same scale.

This having been said, nothing will plot unless the script has values for the yvalue list. 
Some arbitrary values are shown below.
With a bit more work, you might load these values from a file or value dialog(s).
If all you wanted to do was to update a graph, then you could add control commands to 
skip making axes, but note the importance of creating the X values, which happens
when the X axis ticks are created.
There is also virtually no error detection, or at least no messages other than
the traceback that will show up.

"""

import scribus

xorigin = 64      # x-origin of graph
yorigin = 536     # y-origin of graph
xaxis=637         # x axis length
yaxis=408         # y axis length
color="Black"
a = 1.5           # width of lines
l = 2.0           # width of the plotted line
t = 0.8           # width of scale markers
xvalue = []
yvalue = [1,1,4,2,1,0,1,1,4,1,7,2,3,4,4,2,7,5,5,5,5,4,3,2] # graph points
polyvalue = []
if scribus.haveDoc():
    xtick = xorigin
    xscale = xaxis/24     # this gives you typographic points per X data unit
    yrange = int(scribus.valueDialog('Maximum Y Scale', 'Enter Top Y Value\n(Scale)'))
    ymark = int(scribus.valueDialog('Tick Mark Interval', 'Enter Y Interval for Tick Marks'))
    ytick = yorigin
    yscale = yaxis/yrange # this gives you typographic points per Y data unit
    scribus.setRedraw(1)
    scribus.setUnit(0)
    d = scribus.createLine(xorigin,yorigin,xorigin+xaxis,yorigin) # create X axis
    scribus.setLineWidth(a, d)
    scribus.setLineColor(color, d)
    scribus.setFillColor(color, d)
    e = scribus.createLine(xorigin,yorigin,xorigin,yorigin-yaxis) # create Y axis
    scribus.setLineWidth(a, e)
    scribus.setLineColor(color, e)
    scribus.setFillColor(color, e)
    ytick = ytick - ymark * yscale
    nticks = 0
    item = 0
    while (nticks < 24):
        xtick = xtick + xscale
        xvalue.append(xtick)
        xt = scribus.createLine(xtick, yorigin, xtick, yorigin + 5) # creating X axis tick lines
        scribus.setLineWidth(t, xt)
        scribus.setLineColor(color, xt)
        scribus.setFillColor(color, xt)
        nticks += 1
    while (ytick > (yorigin - yaxis)):
        yt = scribus.createLine(xorigin, ytick, xorigin - 5, ytick) # creating Y axis tick lines
        scribus.setLineWidth(t, yt)
        scribus.setLineColor(color, yt)
        scribus.setFillColor(color, yt)
        ytick = ytick - ymark * yscale
    while (item < 24): # this interleaves the X and Y data for createPolyLine
        polyvalue.append(xvalue[item])
        polyvalue.append((yorigin - yvalue[item] * yscale)) 
        item += 1
    poly = scribus.createPolyLine(polyvalue) # this is the actual plotting
    scribus.setLineWidth(l, poly)
    scribus.setLineColor(color, poly)
    scribus.setFillColor("None",poly)

scribus.redrawAll()