take linewidth into account when stroking only
[PyX.git] / faq / plotting_graphs.rst
blobde92487f50333f608e625dcb7cb598d9e0a6226c
1 ==================
2 Plotting of graphs
3 ==================
5 General aspects
6 ===============
8 .. _mingraphdata:
10 How do I generate a graph from data as simply as possible?
11 ----------------------------------------------------------
13 Suppose that you have a data file ``x.dat`` containing values for ``x`` and
14 ``y`` in two columns. Then the following code will do the job::
16    from pyx import *
18    g = graph.graphxy(width=10)
19    g.plot(graph.data.file("x.dat", x=1, y=2))
20    g.writeEPSfile("x")
22 ``graphxy`` creates a canvas (called ``g`` in this example) onto which the
23 graph will be drawn and it sets the default behavior including the axis. There
24 is, however, no default value for the width of the graph. In ``plot`` you have
25 to specify the name of the data file and the columns from which the data should
26 be taken. Finally, ``writeEPSfile`` will generate the postscript file ``x.eps``
27 which you can view or print.
29 A minimal example is also provided in the PyX distribution as
30 ``examples/graphs/minimal.py``.
32 .. _mingraphfunc:
34 How do I generate a graph of a function as simply as possible?
35 --------------------------------------------------------------
37 The following example will draw a parabola::
39    from pyx import *
41    g = graph.graphxy(width=10,
42                      x=graph.axis.linear(min=-2, max=2)
43                      )
45    g.plot(graph.data.function("y(x)=x**2"))
47    g.writeEPSfile("x")
49 Most of the code has been explained in :ref:`mingraphdata`. The main
50 difference is that here you need to specify minimum and maximum for the
51 ``x``-axis so that PyX knows in which range to evaluate the function.
53 Another, slightly more complex, example is also provided in the PyX
54 distribution as ``examples/graphs/piaxis.py``.
56 How can I stack graphs?
57 -----------------------
59 PyX always needs a canvas to draw on. One possibility therefore consists in
60 creating a new canvas with ::
62    c = canvas.canvas()
64 and inserting the graphs into this canvas with ``c.insert(…)``. Here, ``…`` has
65 to be replaced by the name of the graph. Alternatively, the canvas created with
66 ``graph.graphxy`` for one of the graphs can be used to insert the other graphs
67 even if they will be positioned outside the first graph.
69 The second issue to address is positioning of the graphs. By specifying
70 ``xpos`` and ``ypos`` when calling ``graphxy`` you can define the position of a
71 graph. Later on, the position and size of a graph ``g`` can be referred to as
72 ``g.xpos`` ``g.ypos`` ``g.width`` and ``g.height`` even if for example the
73 height has never been specified explicitly but is only defined by a PyX
74 default. 
76 The following example shows how to put graph ``gupper`` above graph ``glower``
77 on a canvas ``c``::
79    from pyx import *
80    from graph import graphxy
82    c = canvas.canvas()
84    glower = graphxy(width=10)
85    glower.plot(...)
86    c.insert(glower)
88    gupper = graphxy(width=10, ypos=glower.ypos+glower.height+2)
89    gupper.plot(...)
91    c.insert(gupper)
92    c.writeEPSfile(...)
94 where ``…`` has to be replaced by the appropriate information like data and
95 symbol specifications and the name of the output file. Here, ``c.insert`` is
96 used to actually insert the subcanvasses for the graphs into the main canvas
97 ``c`` and ``c.writeEPSfile`` in the last line requests to write the contents of
98 this canvas to a file.
101 How can I plot grid data?
102 -------------------------
104 PyX offers support for plotting three-dimensional data as two-dimensional color
105 plots or grey-scale plots and of vector fields by providing ways to plot
106 rectangles and arrows in graphs. 
108 We start by considering the task of creating a two-dimensional color plot by
109 plotting a number of filled rectangles. One first needs to create a data set
110 which consists of five entries per data point. These are the lower left corner
111 (*x*\ :sub:`min`, *y*\ :sub:`min`) and the upper right corner (*x*\ :sub:`max`,
112 *y*\ :sub:`max`) of the triangle and a value between 0 and 1 determining the
113 color via a PyX color palette. The following code gives an idea of how to
114 proceed::
116    g.plot(graph.data.file("datafile.dat", xmin=1, xmax=2, ymin=3, ymax=4, color=5),
117                  [graph.style.rect(color.palette.ReverseRainbow)]
118           )
119    g.dodata()
121 Here, we assume that the data are stored in ``datafile.dat`` and the columns
122 contain *x*\ :sub:`min`, *x* :sub:`max`, *y*\ :sub:`min`, *y*\ :sub:`max`, and
123 the color value in this order. The columns are numbered from 1, since the 0th
124 column contains the line number. To determine the color, we use the
125 ``ReverseRainbow`` palette. The last line instructs PyX to plot the rectangles
126 before plotting the axes. Otherwise, the axes might be covered partially by the
127 rectangles and, in particular, the ticks might not be visible. Gray-scale plots
128 can easily be generated by specifying the palette ``Gray`` or ``ReverseGray``
129 (cf. appendix C of the manual for a list of predefined palettes).
131 At first sight, it seems surprising that plotting of grid data requires the
132 specification of four coordinates for the rectangle. The reason is that this
133 allows to draw rectangles of varying sizes which may help to reduce the size of
134 the postscript file by combining rectangles of the same color in horizontal or
135 vertical direction. For example, it may be sufficient to plot a grey-scale
136 image in a small number of grey shades and then combining rectangles may be
137 appropriate. Note, though, that this step is part of the data creation and not
138 preformed by PyX. Another advantage of fully specifying each rectangle is that
139 it is straightforward to leave parts of the graph blank.
141 The same ideas as for the color plot can be applied to plot vector fields where
142 each data point is represented by an arrow. In this case a data point is
143 specified by the position of the arrow, its size and its direction as indicated
144 in the following code snippet::
146    g.plot(graph.data.file("datafile.dat"), x=1, y=2, size=3, angle=4),
147               [graph.style.arrow()]
148           )
150 Complete code examples can be found in ``examples/graphs/mandel.py`` and
151 ``examples/graphs/arrows.py`` .
153 .. _problemcoord:
155 How can I access points in problem coordinates of a graph?
156 ----------------------------------------------------------
158 Sometimes it may be necessary to add graphical elements to a graph in addition
159 to the data or function(s) which have been plotted as described in 
160 :ref:`mingraphdata` and :ref:`mingraphfunc`. For a graph instance 
161 ``g`` the positioning can easily be done in canvas coordinates by making
162 use of the origin (``g.xpos``, ``g.ypos``) and the width 
163 (``g.width``) and height (``g.height``) of the graph. 
165 Occasionally, it may be more convenient to specify the position of the
166 additional material in terms of problem coordinates. However, this requires
167 that the mapping from problem coordinates to canvas coordinates is known.  By
168 default this is not the case before the content of the canvas is written to the
169 output which is too late for our purpose. One therefore needs to explicitly
170 instruct PyX to determine this mapping. One possibility is to ask PyX to finish
171 the graph by means of ``g.finish()``. Now, problem coordinates can be used to
172 insert additional material which will end up in front of the graph. If this is
173 not desired, one should only fix the layout of the graph by means of
174 ``g.dolayout()``. Then, the additional material can be put onto the canvas
175 before the graph is drawn and it will therefore appear behind the graph.
177 The conversion of problem coordinates (``px``, ``py``) to canvas coordinates
178 (``x``, ``y``) is performed as follows::
180    x, y = g.pos(px, py)
182 By default, the problem coordinates will refer to the ranges of the *x* and *y*
183 axes. If several axes with different ranges exist, the instances of the desired
184 axes should be passed to the ``pos`` method by means of the keyword arguments
185 ``xaxis`` and ``yaxis``.
187 We remark that the drawing of lines parallel to one of the axes at specific
188 problem coordinates can also be done by adapting the method described in
189 :ref:`zeroline`.
191 I would like a key for only some of my data sets. How do I do that?
192 -------------------------------------------------------------------
194 .. todo::
196    This still needs to be answered.
198 Axis properties
199 ===============
201 How do I specify the tick increment?
202 ------------------------------------
204 In the partition of a linear axis, the increments associated with ticks,
205 subticks etc. can be specified as argument of ``parter.linear``. In the
206 following example, ticks will be drawn at even values while subticks will be
207 drawn at all integers::
209    from pyx.graph import axis
211    tg = graph.graphxy(width=10,
212                       x=axis.linear(min=1, max=10,
213                                     parter=axis.parter.linear(tickdists=[2,1]))
214                       )
216 .. _zeroline:
218 How do I plot the zero line?
219 ----------------------------
221 PyX releases before 0.6 offered the possibility to stroke a zero line by 
222 specifying ``zeropathattrs`` in the painter constructor. In more recent 
223 releases, one proceeds as follows. First one has to fix the layout information 
224 of the graph by means of the ``finish`` or ``dolayout`` method (see 
225 :ref:`problemcoord` for a more detailed explanation). Then, the 
226 ``xgridpath`` or ``ygridpath`` method of a graph will return a grid 
227 path parallel to the *y* or *x* axis, respectively, at the specified *y* value. 
228 As an example, a zero line in *x* direction can be drawn as follows::
230    g.finish()
231    g.stroke(g.ygridpath(0))
233 How can I add grid lines to a graph?
234 ------------------------------------
236 Specifying ``gridattrs`` for the painter of an axis will generate grid lines
237 orthogonal to this axis. At least an empty list is needed like in ::
239    g = graph.graphxy(width=10,
240                      x=graph.axis.linear(painter=graph.axis.painter.regular(gridattrs=[])),
241                      y=graph.axis.linear()
242                      )
244 where grid lines in vertical direction are drawn in default style.
246 Occassionally, one might want to draw grid lines corresponding to ticks and
247 subticks in a different style. This can be achieved by specifiying changeable
248 attributes using ``changelist``. The following code ::
250    my_xpainter = graph.axis.painter.regular(gridattrs=
251                        [attr.changelist([style.linestyle.solid, style.linestyle.dashed])]
252                                             )
253    my_ypainter = graph.axis.painter.regular(gridattrs=
254                        [attr.changelist([color.rgb.red, color.rgb.blue])]
255                                             )
257    g = graph.graphxy(width=10,
258                      x=graph.axis.linear(painter=my_xpainter),
259                      y=graph.axis.linear(painter=my_ypainter)
260                      )
262 will create vertical solid and dashed grid lines for ticks and subticks,
263 respectively. The horizontal grid lines will be red for ticks and blue for
264 subticks. The changeable attributes are applied in a cyclic manner. Therefore,
265 in this example grid lines at subsubticks would be plotted in the same style as
266 for ticks. If this is not desired, the list of attributes should be extended by
267 an appropriate third style. The keyword ``None`` will switch off the respective
268 level of grid lines in case you want to draw them only e.g.  for ticks but not
269 subticks.
271 Data properties
272 ===============
274 How do I choose the symbol and its attributes?
275 ----------------------------------------------
277 Suppose a graph called ``g`` has been initialized, e.g. by using
278 ``graph.graphxy``. Then, data and the style of their representation in the
279 graph are defined by calling ``g.plot`` like in the following example in which
280 filled circles are requested::
282    g.plot(graph.data.file("test.dat"),
283           [graph.style.symbol(graph.style.symbol.circle, symbolattrs=[deco.filled])]
284           )
286 As another example, if the linewidth of the symbol is too thin for your
287 purposes, you could use something like::
289    [graph.style.symbol(graph.style.symbol.plus, symbolattrs=[style.linewidth.Thick])]
291 How do I choose the color of the symbols?
292 -----------------------------------------
294 Colors are not properties of the symbol as such and can therefore not be
295 specified in ``symbolattrs`` directly. The color is rather related to the
296 plotting of the symbol as defined by ``deco.stroked`` or ``deco.filled``. 
297 With ::
299    graph.style.symbol(graph.style.symbol.circle,
300                       symbolattrs=[deco.stroked([color.rgb.red]),
301                                    deco.filled([color.rgb.green])]
302                       )
304 you will obtain a circle filled in green with a red borderline.
306 How do I choose the line style?
307 -------------------------------
309 If you do not want to use symbols, you can set the line style as in this
310 example ::
312    g.plot(graph.data.file("test.dat"),
313           [graph.style.line([style.linewidth.Thin])]
314           )
316 where the linewidth is set.
318 If you also want to use symbols, you can combine the symbol and the
319 line style as in ::
321    g.plot(graph.data.file("test.dat"),
322           [graph.style.line(lineattrs=[style.linewidth.Thin,
323                                        style.linestyle.dashed]),
324            graph.style.symbol(graph.style.symbolline.circle,
325                               symbolattrs=[deco.filled])
326            ]
327           )
329 to plot the symbols on top of a thin, dashed line. You may alter the
330 order of the styles to plot the line on top of the symbols.
332 How can I change the color of symbols or lines according to a palette?
333 ----------------------------------------------------------------------
335 If several data sets should be plotted in different colors, one can specify in
336 ``symbolattrs`` and/or ``lineattrs`` a palette like ``color.palette.Rainbow``.
337 Equidistant colors are chosen spanning the palette from one end to the other.
338 For example, for three data sets the colors are chosen from the palette at 0,
339 0.5, and 1. For the rainbow palette, this would correspond to red, green, and
340 blue, respectively.
342 In the following example, symbols vary in form and change their color according 
343 to the rainbow palette at the same time as the connecting lines::
345    mystyle = [graph.style.symbol(graph.style.symbol.changecircle,
346                                  symbolattrs=[color.palette.Rainbow]),
347               graph.style.line(lineattrs=[color.palette.Rainbow])]
349 See question :ref:`changelist` for a more complete example demonstrating how to
350 use this style definition and for a comment on the necessity of defining
351 ``mystyle`` (you are of course free to choose a different name).
353 .. _changelist:
355 How can I specify changing colors (or other attributes) for symbols or lines?
356 -----------------------------------------------------------------------------
358 In ``symbolattrs`` and/or ``lineattrs`` so-called changelist can be used. As an
359 example ::
361    mystyle = graph.style.symbol(symbolattrs=
362                                 [attr.changelist([color.rgb.red, color.rgb.green])])
363    g.plot(graph.data.file("x.dat", x=1, y=2), [mystyle])
364    g.plot(graph.data.file("y.dat", x=1, y=2), [mystyle])
365    g.plot(graph.data.file("z.dat", x=1, y=2), [mystyle])
367 will switch between red and green symbols each time a new data set is plotted.
368 Several changelists can be specified. They are cycled independently and need
369 not be of the same length. It should be noted that the definition of
370 ``mystyle`` in this example ensures that there is only one instance of the
371 definition of ``symbolattrs``. Putting an explicit definition of
372 ``symbolattrs`` in each call to ``plot`` would not lead to the desired result
373 because each time a new instance would be created which then starts with the
374 first item in the changelist.
376 It may be necessary to repeat attributes in order that several changelists
377 cooperate to produce the desired result.  A common situation is that one would
378 like to cycle through a list of symbols which should be used in alternating
379 colors. This can be achieved with the following code::
381    mystyle = graph.style.symbol(
382                    graph.style.symbol.changetriangletwice,
383                    symbolattrs=[attr.changelist([color.rgb.red, color.rgb.green])])
385 which will produce a red triangle, a green triangle, a red circle, a green
386 circle and so on for diamond and square because ``changetriangletwice`` lists
387 each symbol twice. If instead of changing between colors one would like to
388 change between filled and open symbols, one can make use of a predefined
389 changelist ::
391    mystyle = graph.style.symbol(
392                    graph.style.symbol.changetriangletwice,
393                    symbolattrs=[graph.style.symbol.changefilledstroked])