Plotting of graphs¶
General aspects¶
How do I generate a graph from data as simply as possible?¶
Suppose that you have a data file x.dat
containing values for x
and
y
in two columns. Then the following code will do the job:
from pyx import *
g = graph.graphxy(width=10)
g.plot(graph.data.file("x.dat", x=1, y=2))
g.writeEPSfile("x")
graphxy
creates a canvas (called g
in this example) onto which the
graph will be drawn and it sets the default behavior including the axis. There
is, however, no default value for the width of the graph. In plot
you have
to specify the name of the data file and the columns from which the data should
be taken. Finally, writeEPSfile
will generate the postscript file x.eps
which you can view or print.
A minimal example is also provided in the PyX distribution as
examples/graphs/minimal.py
.
How do I generate a graph of a function as simply as possible?¶
The following example will draw a parabola:
from pyx import *
g = graph.graphxy(width=10,
x=graph.axis.linear(min=-2, max=2)
)
g.plot(graph.data.function("y(x)=x**2"))
g.writeEPSfile("x")
Most of the code has been explained in How do I generate a graph from data as simply as possible?. The main
difference is that here you need to specify minimum and maximum for the
x
-axis so that PyX knows in which range to evaluate the function.
Another, slightly more complex, example is also provided in the PyX
distribution as examples/graphs/piaxis.py
.
How can I stack graphs?¶
PyX always needs a canvas to draw on. One possibility therefore consists in creating a new canvas with
c = canvas.canvas()
and inserting the graphs into this canvas with c.insert(…)
. Here, …
has
to be replaced by the name of the graph. Alternatively, the canvas created with
graph.graphxy
for one of the graphs can be used to insert the other graphs
even if they will be positioned outside the first graph.
The second issue to address is positioning of the graphs. By specifying
xpos
and ypos
when calling graphxy
you can define the position of a
graph. Later on, the position and size of a graph g
can be referred to as
g.xpos
g.ypos
g.width
and g.height
even if for example the
height has never been specified explicitly but is only defined by a PyX
default.
The following example shows how to put graph gupper
above graph glower
on a canvas c
:
from pyx import *
from graph import graphxy
c = canvas.canvas()
glower = graphxy(width=10)
glower.plot(...)
c.insert(glower)
gupper = graphxy(width=10, ypos=glower.ypos+glower.height+2)
gupper.plot(...)
c.insert(gupper)
c.writeEPSfile(...)
where …
has to be replaced by the appropriate information like data and
symbol specifications and the name of the output file. Here, c.insert
is
used to actually insert the subcanvasses for the graphs into the main canvas
c
and c.writeEPSfile
in the last line requests to write the contents of
this canvas to a file.
How can I plot grid data?¶
PyX offers support for plotting three-dimensional data as two-dimensional color plots or grey-scale plots and of vector fields by providing ways to plot rectangles and arrows in graphs.
We start by considering the task of creating a two-dimensional color plot by plotting a number of filled rectangles. One first needs to create a data set which consists of five entries per data point. These are the lower left corner (xmin, ymin) and the upper right corner (xmax, ymax) of the triangle and a value between 0 and 1 determining the color via a PyX color palette. The following code gives an idea of how to proceed:
g.plot(graph.data.file("datafile.dat", xmin=1, xmax=2, ymin=3, ymax=4, color=5),
[graph.style.rect(color.palette.ReverseRainbow)]
)
g.dodata()
Here, we assume that the data are stored in datafile.dat
and the columns
contain xmin, x max, ymin, ymax, and
the color value in this order. The columns are numbered from 1, since the 0th
column contains the line number. To determine the color, we use the
ReverseRainbow
palette. The last line instructs PyX to plot the rectangles
before plotting the axes. Otherwise, the axes might be covered partially by the
rectangles and, in particular, the ticks might not be visible. Gray-scale plots
can easily be generated by specifying the palette Gray
or ReverseGray
(cf. appendix C of the manual for a list of predefined palettes).
At first sight, it seems surprising that plotting of grid data requires the specification of four coordinates for the rectangle. The reason is that this allows to draw rectangles of varying sizes which may help to reduce the size of the postscript file by combining rectangles of the same color in horizontal or vertical direction. For example, it may be sufficient to plot a grey-scale image in a small number of grey shades and then combining rectangles may be appropriate. Note, though, that this step is part of the data creation and not preformed by PyX. Another advantage of fully specifying each rectangle is that it is straightforward to leave parts of the graph blank.
The same ideas as for the color plot can be applied to plot vector fields where each data point is represented by an arrow. In this case a data point is specified by the position of the arrow, its size and its direction as indicated in the following code snippet:
g.plot(graph.data.file("datafile.dat"), x=1, y=2, size=3, angle=4),
[graph.style.arrow()]
)
Complete code examples can be found in examples/graphs/mandel.py
and
examples/graphs/arrows.py
.
How can I access points in problem coordinates of a graph?¶
Sometimes it may be necessary to add graphical elements to a graph in addition
to the data or function(s) which have been plotted as described in
How do I generate a graph from data as simply as possible? and How do I generate a graph of a function as simply as possible?. For a graph instance
g
the positioning can easily be done in canvas coordinates by making
use of the origin (g.xpos
, g.ypos
) and the width
(g.width
) and height (g.height
) of the graph.
Occasionally, it may be more convenient to specify the position of the
additional material in terms of problem coordinates. However, this requires
that the mapping from problem coordinates to canvas coordinates is known. By
default this is not the case before the content of the canvas is written to the
output which is too late for our purpose. One therefore needs to explicitly
instruct PyX to determine this mapping. One possibility is to ask PyX to finish
the graph by means of g.finish()
. Now, problem coordinates can be used to
insert additional material which will end up in front of the graph. If this is
not desired, one should only fix the layout of the graph by means of
g.dolayout()
. Then, the additional material can be put onto the canvas
before the graph is drawn and it will therefore appear behind the graph.
The conversion of problem coordinates (px
, py
) to canvas coordinates
(x
, y
) is performed as follows:
x, y = g.pos(px, py)
By default, the problem coordinates will refer to the ranges of the x and y
axes. If several axes with different ranges exist, the instances of the desired
axes should be passed to the pos
method by means of the keyword arguments
xaxis
and yaxis
.
We remark that the drawing of lines parallel to one of the axes at specific problem coordinates can also be done by adapting the method described in How do I plot the zero line?.
I would like a key for only some of my data sets. How do I do that?¶
Todo
This still needs to be answered.
Axis properties¶
How do I specify the tick increment?¶
In the partition of a linear axis, the increments associated with ticks,
subticks etc. can be specified as argument of parter.linear
. In the
following example, ticks will be drawn at even values while subticks will be
drawn at all integers:
from pyx.graph import axis
tg = graph.graphxy(width=10,
x=axis.linear(min=1, max=10,
parter=axis.parter.linear(tickdists=[2,1]))
)
How do I plot the zero line?¶
PyX releases before 0.6 offered the possibility to stroke a zero line by
specifying zeropathattrs
in the painter constructor. In more recent
releases, one proceeds as follows. First one has to fix the layout information
of the graph by means of the finish
or dolayout
method (see
How can I access points in problem coordinates of a graph? for a more detailed explanation). Then, the
xgridpath
or ygridpath
method of a graph will return a grid
path parallel to the y or x axis, respectively, at the specified y value.
As an example, a zero line in x direction can be drawn as follows:
g.finish()
g.stroke(g.ygridpath(0))
How can I add grid lines to a graph?¶
Specifying gridattrs
for the painter of an axis will generate grid lines
orthogonal to this axis. At least an empty list is needed like in
g = graph.graphxy(width=10,
x=graph.axis.linear(painter=graph.axis.painter.regular(gridattrs=[])),
y=graph.axis.linear()
)
where grid lines in vertical direction are drawn in default style.
Occassionally, one might want to draw grid lines corresponding to ticks and
subticks in a different style. This can be achieved by specifiying changeable
attributes using changelist
. The following code
my_xpainter = graph.axis.painter.regular(gridattrs=
[attr.changelist([style.linestyle.solid, style.linestyle.dashed])]
)
my_ypainter = graph.axis.painter.regular(gridattrs=
[attr.changelist([color.rgb.red, color.rgb.blue])]
)
g = graph.graphxy(width=10,
x=graph.axis.linear(painter=my_xpainter),
y=graph.axis.linear(painter=my_ypainter)
)
will create vertical solid and dashed grid lines for ticks and subticks,
respectively. The horizontal grid lines will be red for ticks and blue for
subticks. The changeable attributes are applied in a cyclic manner. Therefore,
in this example grid lines at subsubticks would be plotted in the same style as
for ticks. If this is not desired, the list of attributes should be extended by
an appropriate third style. The keyword None
will switch off the respective
level of grid lines in case you want to draw them only e.g. for ticks but not
subticks.
Data properties¶
How do I choose the symbol and its attributes?¶
Suppose a graph called g
has been initialized, e.g. by using
graph.graphxy
. Then, data and the style of their representation in the
graph are defined by calling g.plot
like in the following example in which
filled circles are requested:
g.plot(graph.data.file("test.dat"),
[graph.style.symbol(graph.style.symbol.circle, symbolattrs=[deco.filled])]
)
As another example, if the linewidth of the symbol is too thin for your purposes, you could use something like:
[graph.style.symbol(graph.style.symbol.plus, symbolattrs=[style.linewidth.Thick])]
How do I choose the color of the symbols?¶
Colors are not properties of the symbol as such and can therefore not be
specified in symbolattrs
directly. The color is rather related to the
plotting of the symbol as defined by deco.stroked
or deco.filled
.
With
graph.style.symbol(graph.style.symbol.circle,
symbolattrs=[deco.stroked([color.rgb.red]),
deco.filled([color.rgb.green])]
)
you will obtain a circle filled in green with a red borderline.
How do I choose the line style?¶
If you do not want to use symbols, you can set the line style as in this example
g.plot(graph.data.file("test.dat"),
[graph.style.line([style.linewidth.Thin])]
)
where the linewidth is set.
If you also want to use symbols, you can combine the symbol and the line style as in
g.plot(graph.data.file("test.dat"),
[graph.style.line(lineattrs=[style.linewidth.Thin,
style.linestyle.dashed]),
graph.style.symbol(graph.style.symbolline.circle,
symbolattrs=[deco.filled])
]
)
to plot the symbols on top of a thin, dashed line. You may alter the order of the styles to plot the line on top of the symbols.
How can I change the color of symbols or lines according to a palette?¶
If several data sets should be plotted in different colors, one can specify in
symbolattrs
and/or lineattrs
a palette like color.palette.Rainbow
.
Equidistant colors are chosen spanning the palette from one end to the other.
For example, for three data sets the colors are chosen from the palette at 0,
0.5, and 1. For the rainbow palette, this would correspond to red, green, and
blue, respectively.
In the following example, symbols vary in form and change their color according to the rainbow palette at the same time as the connecting lines:
mystyle = [graph.style.symbol(graph.style.symbol.changecircle,
symbolattrs=[color.palette.Rainbow]),
graph.style.line(lineattrs=[color.palette.Rainbow])]
See question How can I specify changing colors (or other attributes) for symbols or lines? for a more complete example demonstrating how to
use this style definition and for a comment on the necessity of defining
mystyle
(you are of course free to choose a different name).
How can I specify changing colors (or other attributes) for symbols or lines?¶
In symbolattrs
and/or lineattrs
so-called changelist can be used. As an
example
mystyle = graph.style.symbol(symbolattrs=
[attr.changelist([color.rgb.red, color.rgb.green])])
g.plot(graph.data.file("x.dat", x=1, y=2), [mystyle])
g.plot(graph.data.file("y.dat", x=1, y=2), [mystyle])
g.plot(graph.data.file("z.dat", x=1, y=2), [mystyle])
will switch between red and green symbols each time a new data set is plotted.
Several changelists can be specified. They are cycled independently and need
not be of the same length. It should be noted that the definition of
mystyle
in this example ensures that there is only one instance of the
definition of symbolattrs
. Putting an explicit definition of
symbolattrs
in each call to plot
would not lead to the desired result
because each time a new instance would be created which then starts with the
first item in the changelist.
It may be necessary to repeat attributes in order that several changelists cooperate to produce the desired result. A common situation is that one would like to cycle through a list of symbols which should be used in alternating colors. This can be achieved with the following code:
mystyle = graph.style.symbol(
graph.style.symbol.changetriangletwice,
symbolattrs=[attr.changelist([color.rgb.red, color.rgb.green])])
which will produce a red triangle, a green triangle, a red circle, a green
circle and so on for diamond and square because changetriangletwice
lists
each symbol twice. If instead of changing between colors one would like to
change between filled and open symbols, one can make use of a predefined
changelist
mystyle = graph.style.symbol(
graph.style.symbol.changetriangletwice,
symbolattrs=[graph.style.symbol.changefilledstroked])