From: Armin S. <li...@ar...> - 2005-09-09 11:12:25
|
Hello, first I want to thank you very much for developing PyX, it's really great. I have used PyX for generating the graphics when I recently wanted to write an article about Gaussian Random Functions [1]. The only thing, that bothered me, was the overhead when writing a script that generates a single graphic. Overhead means here e.g. - importing modules - creating a canvas - remembering long names as pyx.graph.axis.painter.ticklength - writing the graphic. One partial solution could be to write a template and copy it around when starting a new graphic, but I was not confident with that. I therefore wrote a layer (I call "Simply Draw") to PyX that copes with my problems. A valid file (circle.py) could just contain e.g. stroke(circle(0, 0, 1)) and the corresponding graphic is created via $ simplydraw circle.py Today I have uploaded a first version of Simply Draw to my homepage [2] and included some examples [3] to show how easy it can be to generate graphics using PyX. I would like to invite anyone to take at look at this and would be glad about any comment or suggestion. The module pyxmath.py included in Simply Draw contains additional functionality for PyX (two graph data sources and a graph style). Perhaps you may want to think about including similar functionality to PyX itself. [1] http://arminstraub.de/browse.php?page=math_gaussianrandomfunctions [2] http://arminstraub.de/browse.php?page=programs_simplydraw [3] http://arminstraub.de/browse.php?page=programs_simplydraw_samples Cheers -- Armin Straub -- Armin Straub |
From: Joerg L. <jo...@us...> - 2005-09-09 12:45:55
|
Hello Armin! On 09.09.05, Armin Straub wrote: > first I want to thank you very much for developing PyX, it's really great. Thanks! > I have used PyX for generating the graphics when I recently wanted to write an > article about Gaussian Random Functions [1]. > The only thing, that bothered me, was the overhead when writing a script that > generates a single graphic. Overhead means here e.g. > - importing modules > - creating a canvas > - remembering long names as pyx.graph.axis.painter.ticklength > - writing the graphic. > One partial solution could be to write a template and copy it around when > starting a new graphic, but I was not confident with that. > > I therefore wrote a layer (I call "Simply Draw") to PyX that copes with my > problems. A valid file (circle.py) could just contain e.g. > stroke(circle(0, 0, 1)) > and the corresponding graphic is created via > $ simplydraw circle.py Great! There have been several suggestions on the list for something like this, but nobody ever started an implemententation. I'm sure this is useful for many people. > Today I have uploaded a first version of Simply Draw to my homepage [2] and > included some examples [3] to show how easy it can be to generate graphics > using PyX. Unfortunately, I was not able to access all the examples - somehow I always end up at the "clip" page. > I would like to invite anyone to take at look at this and would be glad about > any comment or suggestion. > > The module pyxmath.py included in Simply Draw contains additional > functionality for PyX (two graph data sources and a graph style). Perhaps you > may want to think about including similar functionality to PyX itself. Concering the Python function stuff, I'm not sure whether this will be needed in PyX 0.9 anymore (but it could still be useful). The reason for this being that we removed our own expressions parser in favour of using the Python eval function. Commenting on the graph style, I'll happily leave to André... Jörg |
From: Armin S. <li...@ar...> - 2005-09-09 17:59:23
|
Hello J=F6rg, > > Today I have uploaded a first version of Simply Draw to my homepage [2] > > and included some examples [3] to show how easy it can be to generate > > graphics using PyX. > > Unfortunately, I was not able to access all the examples - somehow I > always end up at the "clip" page. I have tried it from here, but I was unable to reproduce this bug. What doe= s=20 happen if you request the url [1]? Do you still get the (default) Clipping= =20 example displayed? This really puzzles me... is anyone seeing a similar misbehaviour? [1]=20 http://arminstraub.de/browse.php?page=3Dprograms_simplydraw_samples&sample= =3Dstackinggraphs > > The module pyxmath.py included in Simply Draw contains additional > > functionality for PyX (two graph data sources and a graph style). Perha= ps > > you may want to think about including similar functionality to PyX > > itself. > > Concering the Python function stuff, I'm not sure whether this will be > needed in PyX 0.9 anymore (but it could still be useful). The reason for > this being that we removed our own expressions parser in favour of using > the Python eval function. Commenting on the graph style, I'll happily > leave to Andr=E9... I was aware of the removal of the expression parser. But still a string is= =20 used to carry the expression. This might be nice for simple expression as=20 "x**2", but unfortunately it is not usable in the general case. I was e.g. = to=20 plot typical graphs of a fractional Brownian motion (you can see the source= =20 at [1] if you are more lucky this time). The trajectories to plot are not=20 given by an expression that could be passed in a string. Nonetheless it is= =20 easy to define a Python function that represents this trajectory. This is were functionxy comes into play. Instead of taking a string that ho= lds=20 a Pythonic expression it just takes a real expression. At least this is what I thought when writing these data sources (where=20 mathtree was still in use). But you make me wonder if these are still usefu= l=20 in PyX 0.9 then. If I define a function say def h(x): ... can I then use something like function("y=3Dh(x)") in PyX (sorry if the syntax is wrong, but I hope you get what I mean)? If t= his=20 is possible then my derivation is technically useless. On the other hand I= =20 think it would be nice if one could write as well function(h) with the same result. Shouldn't this be possible by a simple check to see i= f=20 expr passed to function is callable? I think this would be more clean in th= is=20 case and eliminates the need to parse the string and call eval. I even think that it is more straightforward to write function(lambda x: x**2) instead of something like function("f(x)=3Dx**2") but I think that's personal taste and sometimes it may be more convenient t= o=20 pass a string. That said, I think I will remove functionxy() in favour of function() takin= g=20 either a string or an expression. Do you think something like that would be= =20 useful also to have in PyX 0.9? It would not break the traditional behaviou= r=20 and provide new possibilities. Thanks for your comments. Cheers =2D-=20 Armin Straub |
From: Joerg L. <jo...@us...> - 2005-09-12 07:50:08
|
Hello Armin! On 09.09.05, Armin Straub wrote: > Hello Jörg, > > > > Today I have uploaded a first version of Simply Draw to my homepage [2] > > > and included some examples [3] to show how easy it can be to generate > > > graphics using PyX. > > > > Unfortunately, I was not able to access all the examples - somehow I > > always end up at the "clip" page. > > I have tried it from here, but I was unable to reproduce this bug. What does > happen if you request the url [1]? Do you still get the (default) Clipping > example displayed? > This really puzzles me... is anyone seeing a similar misbehaviour? I cannot reproduce it anymore - don't know what happened before. At first, I disallowed cookies. When it didn't work, I allowed them again but this didn't help. I need to add that this was with Firefox 1.5 beta1... Jörg |
From: Andre W. <wo...@us...> - 2005-09-09 13:17:36
|
Hi, On 09.09.05, Armin Straub wrote: > I therefore wrote a layer (I call "Simply Draw") to PyX that copes with my > problems. A valid file (circle.py) could just contain e.g. > stroke(circle(0, 0, 1)) > and the corresponding graphic is created via > $ simplydraw circle.py Great. I like it too. > Today I have uploaded a first version of Simply Draw to my homepage [2] and > included some examples [3] to show how easy it can be to generate graphics > using PyX. > I would like to invite anyone to take at look at this and would be glad about > any comment or suggestion. > > The module pyxmath.py included in Simply Draw contains additional > functionality for PyX (two graph data sources and a graph style). Perhaps you > may want to think about including similar functionality to PyX itself. Have you seen the histogram style added in 0.8? It should be able to do the steps. It can't leave out the vertical elements (it can just switch between boxes and steps), but this would be easy to add as an option. Beside that, is this style appropriate for the things you have in mind. (You may want to grab the test_histogram.py from test/functional to see some of the features this style offers.) It would be great to discuss this issue! Could you give a short intro to functionxy and paramfunctiont? I want to understand what's missing or wether we should add some data source to the PyX core. I'm open to any of those discussions. (As Jörg mentioned, I've already remove the mathtree in favour of python expressions for functions. I'm not sure whether this is related to your ideas.) Beside that -- again -- its great stuff. We had some discussions before regarding a simplification layer. I remember a Mail from Magnus Hetland discussing notebox (notebox.net). You may use this pointer to search the mailing list archive ... I should also tell you, that PyX originally started with a much simpler model in mind (when we started PyX Jörg and I were both GLE users (glx.sf.net) for several years and our major concerns were not the simple functional interface). Later we found that objects are handy at least for the internal organization and we dropped the idea of a function-like interface. Still, there have been various discussions about that and such an interface surely is great for many peoples. André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |
From: Armin S. <li...@ar...> - 2005-09-09 17:59:22
|
Hi Andr=E9, > Have you seen the histogram style added in 0.8? It should be able to > do the steps. It can't leave out the vertical elements (it can just > switch between boxes and steps), but this would be easy to add as an > option. Beside that, is this style appropriate for the things you have > in mind. (You may want to grab the test_histogram.py from > test/functional to see some of the features this style offers.) It > would be great to discuss this issue! No, I was not aware of the histogram style. Thanks for the pointer. I will= =20 look into this. > Could you give a short intro to functionxy and paramfunctiont? I want > to understand what's missing or wether we should add some data source > to the PyX core. I'm open to any of those discussions. > > (As J=F6rg mentioned, I've already remove the mathtree in favour of > python expressions for functions. I'm not sure whether this is related > to your ideas.) See my reply to J=F6rg's mail. > Beside that -- again -- its great stuff. We had some discussions > before regarding a simplification layer. I remember a Mail from Magnus > Hetland discussing notebox (notebox.net). You may use this pointer to > search the mailing list archive ... I will do that. > I should also tell you, that PyX originally started with a much > simpler model in mind (when we started PyX J=F6rg and I were both GLE > users (glx.sf.net) for several years and our major concerns were not > the simple functional interface). Later we found that objects are > handy at least for the internal organization and we dropped the idea > of a function-like interface. Still, there have been various > discussions about that and such an interface surely is great for many > peoples. In fact, I'm very happy that you didn't choose in favour of a functional=20 interface. I like your oo interface very much (although I had to get used t= o=20 it). You might have got the impression (from my introductory example) that Simpl= y=20 Draw uses a more functional approach, but that's not completely true. What = I=20 have changed in this respect is that you don't need a global canvas object= =20 (say c). Using Simply Draw you can access all functions (at least this shou= ld=20 be the case) of the global canvas c at a global level. Instead of c.stroke(...) you can just write stroke(...) now. This should just be considered an abbreviation. When the global canvas= =20 does not suffice, you have to create new ones and use them as usually. But you are right. Some functions like intersections() might be more easy t= o=20 use at a functional level (because both paths having equal rights). Thanks for your mail. Have a nice weekend =2D-=20 Armin Straub |
From: Armin S. <li...@ar...> - 2005-09-10 13:12:43
|
Hallo, > Have you seen the histogram style added in 0.8? It should be able to > do the steps. It can't leave out the vertical elements (it can just > switch between boxes and steps), but this would be easy to add as an > option. Beside that, is this style appropriate for the things you have > in mind. (You may want to grab the test_histogram.py from > test/functional to see some of the features this style offers.) It > would be great to discuss this issue! I have tried this style and I think it can be used for the purpose of step= =20 functions, too, if the vertical elements could be left out. But I think it might be even nicer to be able to set a different line style= =20 for the vertical lines. Sometimes I draw e.g. gray dotted vertical lines so= =20 that the step function is easier to grasp. > Could you give a short intro to functionxy and paramfunctiont? I want > to understand what's missing or wether we should add some data source > to the PyX core. I'm open to any of those discussions. > > (As J=F6rg mentioned, I've already remove the mathtree in favour of > python expressions for functions. I'm not sure whether this is related > to your ideas.) I was able to download the CVS version of PyX. So I tried function() after= =20 removal of mathtree. data.functionxy(a, b, f) was somewhat equivalent to data.function("y(x)=3Df(x)", min=3Da, max=3Db, context=3Dlocals()). I have replaced data.functionxy() in favour of a new data.function() accept= ing=20 both strings and expressions. Do you think this would be nice for PyX core = as=20 well? I have also uploaded a new example ("Brownian Motion") that shows a sample= =20 usage of this new data.function(). I did the same for data.paramfunction(), but have yet to include a nice=20 example. There is just a minor issue: data.paramfunction() takes varname as= =20 the first parameter which has no counterpart when using a real Python=20 expression instead of a string. data.paramfunction(a, b, f) is translated to data.paramfunction("t", a, b, "x,y=3Df(t)", context=3Dlocals()). If a similar feature could be build into PyX core, one could take advantage= of=20 the real Python expression instead of translating it into a string and=20 reevaluating it... (but it works also this way and I don't see a real=20 performance hit, for data.function() is usually used a only few times per=20 script). Cheers =2D-=20 Armin Straub |
From: Andre W. <wo...@us...> - 2005-09-12 06:22:01
|
Hi, On 10.09.05, Armin Straub wrote: > I have tried this style and I think it can be used for the purpose of step > functions, too, if the vertical elements could be left out. > But I think it might be even nicer to be able to set a different line style > for the vertical lines. Sometimes I draw e.g. gray dotted vertical lines so > that the step function is easier to grasp. That's a great idea and I'm happy to implement this. I have to think about the argument list. Currently we have "lineattrs" for both, horizontal and vertical lines. We might just add a hlineattrs and a vlineattrs and merge those ... that's at least one option, which is symmetric and compatible to what we have. > I was able to download the CVS version of PyX. So I tried function() after > removal of mathtree. > data.functionxy(a, b, f) > was somewhat equivalent to > data.function("y(x)=f(x)", min=a, max=b, context=locals()). I see. But the context feature as available for ages. Beside that the idea of the whole graph system was to make it completely symetric in x and y, and this is broken by functionxy, but I don't bother. Instead I very much like your idea and would like to add a functionxy to the PyX core. Compared to your version I would suggest to make the function the first parameter, i.e. use the signature functionxy(f, min, max, ...). The idea is to allow the omission of the range as it's allowed for a function in PyX. > I have replaced data.functionxy() in favour of a new data.function() accepting > both strings and expressions. Do you think this would be nice for PyX core as > well? I think we should not mix it up. We have a data.function, which works on strings and a data.functionxy along the lines you introduced and which is certainly very useful to many people. > I did the same for data.paramfunction(), but have yet to include a nice > example. There is just a minor issue: data.paramfunction() takes varname as > the first parameter which has no counterpart when using a real Python > expression instead of a string. > data.paramfunction(a, b, f) > is translated to > data.paramfunction("t", a, b, "x,y=f(t)", context=locals()). Hence it is again a paramfunctionxy. And you're right, that we do not need the name of the parameter anymore, since we just call the function with this parameter argument. Very nice. I like it ... and it becomes similar to the functionxy. > If a similar feature could be build into PyX core, one could take advantage of > the real Python expression instead of translating it into a string and > reevaluating it... (but it works also this way and I don't see a real > performance hit, for data.function() is usually used a only few times per > script). Well, the basic question is whether it saves you function calls. Wrapping a function call into another function call just needs time and function calls are quite expensive in Python. But overall this effect is small, since usually you calculate something like 100 or maybe 1000 points. It doesn't really matter for such a number. It's much more interesting that it's probably a feature which is easy to understand and to use. André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |
From: Armin S. <li...@ar...> - 2005-09-12 14:48:10
|
Hi Andr=E9, > > I have tried this style and I think it can be used for the purpose of > > step functions, too, if the vertical elements could be left out. > > But I think it might be even nicer to be able to set a different line > > style for the vertical lines. Sometimes I draw e.g. gray dotted vertical > > lines so that the step function is easier to grasp. > > That's a great idea and I'm happy to implement this. I have to think > about the argument list. Currently we have "lineattrs" for both, > horizontal and vertical lines. We might just add a hlineattrs and a > vlineattrs and merge those ... that's at least one option, which is > symmetric and compatible to what we have. Sounds nice to me. > > I was able to download the CVS version of PyX. So I tried function() > > after removal of mathtree. > > data.functionxy(a, b, f) > > was somewhat equivalent to > > data.function("y(x)=3Df(x)", min=3Da, max=3Db, context=3Dlocals()). > > I see. But the context feature as available for ages. Beside that the > idea of the whole graph system was to make it completely symetric in x > and y, and this is broken by functionxy, but I don't bother. Instead I I fear that I don't really understand what symmetry of x and y means here. > very much like your idea and would like to add a functionxy to the PyX > core. Compared to your version I would suggest to make the function > the first parameter, i.e. use the signature functionxy(f, min, max, > ...). The idea is to allow the omission of the range as it's allowed > for a function in PyX. It should have been defined your way for sure. > > I have replaced data.functionxy() in favour of a new data.function() > > accepting both strings and expressions. Do you think this would be nice > > for PyX core as well? > > I think we should not mix it up. We have a data.function, which works > on strings and a data.functionxy along the lines you introduced and > which is certainly very useful to many people. > [...] > It's much more interesting that it's probably a feature which is easy > to understand and to use. Perhaps it's because I don't know about what you called symmetry, but I thi= nk=20 it's easier to reuse function() and paramfunction(). When the user wants to plot a function, the first that probably comes to hi= s=20 mind is to use data.function(). Why shouldn't he be free to supply either a= =20 string or a Python function (in case that he uses the ladder and wants to d= o=20 advanced stuff, he should be aware that this is just a shortcut (if althoug= h=20 the most obvious from my point of view)). Additionally when using a new functionxy() this would probably be implement= ed=20 just as it is now - a shortcut to function(). I don't think it would be wor= th=20 the effort to double the functionality of function() like automatic handlin= g=20 of min and max. Nonetheless if this contradicts the concept of symmetry, I think it could b= e=20 added to the PyX core as functionxy() and paramfunctionxy() but still be us= ed=20 as function() and paramfunction() by simplydraw alternatively. Thanks for your ideas. Cheers =2D-=20 Armin Straub |
From: Andre W. <wo...@us...> - 2005-09-12 15:36:11
|
Hi, On 12.09.05, Armin Straub wrote: > > > data.function("y(x)=f(x)", min=a, max=b, context=locals()). > > > > I see. But the context feature as available for ages. Beside that the > > idea of the whole graph system was to make it completely symetric in x > > and y, and this is broken by functionxy, but I don't bother. Instead I > > I fear that I don't really understand what symmetry of x and y means here. You may not have noticed (?!), that you can also do a data.function("x(y)=...", ...) ... it works equally well. There's just noting special regarding the x axis and/or x direction compared to the y direction. (There is a small asymmetry in the sense that all x axes are in the first graph direction and all y axes in the second and in several graph methods working on the graph coordinates this order is to be used, but beside that there's no asymmetry in the whole system.) > > I think we should not mix it up. We have a data.function, which works > > on strings and a data.functionxy along the lines you introduced and > > which is certainly very useful to many people. > > [...] > > It's much more interesting that it's probably a feature which is easy > > to understand and to use. > > Perhaps it's because I don't know about what you called symmetry, but I think > it's easier to reuse function() and paramfunction(). I kindly object: your function and your paramfunction fix the order and the name of the axes involved. In that sense they're quite a restriction. They're a common usecase and thus they are certainly very helpful. > Additionally when using a new functionxy() this would probably be implemented > just as it is now - a shortcut to function(). I don't think it would be worth > the effort to double the functionality of function() like automatic handling > of min and max. Sure. Nothing to tell against that! > Nonetheless if this contradicts the concept of symmetry, I think it could be > added to the PyX core as functionxy() and paramfunctionxy() but still be used > as function() and paramfunction() by simplydraw alternatively. That's fine, but you may spend some time whether you really want to introduce such a different meaning. If you really want to do so I'll not complain. You're free to do so, but I ask you to at least spend a few seconds before doing you final decision ... ;-) > Thanks for your ideas. I always like to join into discussions about ideas and design. Not to mention that your ideas are of great value for PyX as a whole! Thanks as well ... André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |
From: Armin S. <li...@ar...> - 2005-09-13 09:08:47
|
Hallo Andr=E9, > > I fear that I don't really understand what symmetry of x and y means > > here. > > You may not have noticed (?!), that you can also do a > data.function("x(y)=3D...", ...) ... it works equally well. There's just > noting special regarding the x axis and/or x direction compared to the > y direction. Ok, I think, I'm a bit nearer to understanding the issue. Nice for plotting= =20 inverse functions by the way. Once more I have to see that I'm very new to= =20 PyX. There's so much... :-) > I kindly object: your function and your paramfunction fix the order > and the name of the axes involved. In that sense they're quite a > restriction. They're a common usecase and thus they are certainly very > helpful. > [...] > > Nonetheless if this contradicts the concept of symmetry, I think it cou= ld > > be added to the PyX core as functionxy() and paramfunctionxy() but still > > be used as function() and paramfunction() by simplydraw alternatively. > > That's fine, but you may spend some time whether you really want to > introduce such a different meaning. If you really want to do so I'll > not complain. You're free to do so, but I ask you to at least spend a > few seconds before doing you final decision ... ;-) I'm thinking about it... If I now understand the problem correctly, the=20 current implementation is bad mainly because =2D it fixes the order =2D and the name of the axes involved. I have solved (at least I hope so) the issue of fixed order using the=20 following code snippet if expression.func_code.co_varnames =3D=3D ('y',): string =3D "x(y)=3Dexpression(y)" else: string =3D "y(x)=3Dexpression(x)" which should yield some more symmetry in the sense that it does matter whet= her=20 a function is defined as f =3D lambda x: sin(x) versus f =3D lambda y: sin(y). This issue does not really appear for paramfunction since all you have to d= o=20 for swapped axes is to return the values (x,y) swapped. So there is at leas= t=20 from a functional point of view little to gain (although I might be wrong=20 here...). Just a note to the named axes. If this would be wanted one could take the=20 function name into account (not useful for lambda style functions though) t= o=20 allow for some settings, but I can't think of a really useful and obvious w= ay=20 (and that's what it should be in my opinion). Thanks for pointing me to this issue! Cheers =2D-=20 Armin Straub |
From: Andre W. <wo...@us...> - 2005-09-16 13:36:58
|
On 13.09.05, Armin Straub wrote: > > That's fine, but you may spend some time whether you really want to > > introduce such a different meaning. If you really want to do so I'll > > not complain. You're free to do so, but I ask you to at least spend a > > few seconds before doing you final decision ... ;-) > > I'm thinking about it... If I now understand the problem correctly, the > current implementation is bad mainly because > - it fixes the order > - and the name of the axes involved. Right. > I have solved (at least I hope so) the issue of fixed order using the > following code snippet > if expression.func_code.co_varnames == ('y',): > string = "x(y)=expression(y)" > else: > string = "y(x)=expression(x)" > which should yield some more symmetry in the sense that it does matter whether > a function is defined as > f = lambda x: sin(x) > versus > f = lambda y: sin(y). This works, yes, but I don't really like it. It seems too implicit to me. We had such an implicit definition by parsing the string and getting the axis name out of it in earlier versions (like "y=sin(x)"), but now we have it I much more like the very explicit form "y(x)=...". Still even in our old version we had the axis name explicitly given in the string expression. However, as long as we use a string we'll always need the context to access extern functions, right. So my currently preferred solution still is to have the function as it is for the common case with arbitrary names and a functionxy for the most frequent use-case with predefined names for a an external function with signature "y(x)=...". I would think of it just as an handy shortcut. Beside that I thought about a dynamism for functionXY, where XY could be anything. But the problem is, that we (1) dont't know where to split it and (2) we can't do a __getattr__ on module-level to implement something like that. While one could try to simulate that, I would not like such a solution, which is too fancy. > This issue does not really appear for paramfunction since all you have to do > for swapped axes is to return the values (x,y) swapped. So there is at least > from a functional point of view little to gain (although I might be wrong > here...). Right. I don't see any use-case either where the order is that problematic. > Just a note to the named axes. If this would be wanted one could take the > function name into account (not useful for lambda style functions though) to > allow for some settings, but I can't think of a really useful and obvious way > (and that's what it should be in my opinion). This is even more implicit and break for lambda functions. I don't like it either. Hmm. André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |