Next: , Previous: simplex, Up: Import


4.13.3 graph

This package implements two-dimensional linear and logarithmic graphs, including automatic scale and tick selection (with the ability to override manually). A graph is a guide (that can be drawn with the draw command, with an optional legend) constructed with one of the following routines:



An axis can be drawn on a picture with one of the following commands:

Here are some simple examples of two-dimensional graphs:

  1. This example draws a textbook-style graph of y= exp(x), with the y axis starting at y=0:
         import graph;
         size(150,0);
         
         real f(real x) {return exp(x);}
         pair F(real x) {return (x,f(x));}
         
         xaxis("$x$");
         yaxis("$y$",0);
         
         draw(graph(f,-4,2,operator ..),red);
         
         labely(1,E);
         label("$e^x$",F(1),SE);
         
    
    exp.png
  2. The next example draws a scientific-style graph with a legend. The position of the legend can be adjusted either explicitly or by using the graphical user interface xasy (see GUI). If an UnFill(real xmargin=0, real ymargin=xmargin) or Fill(pen) option is specified to add, the legend will obscure any underlying objects. Here we illustrate how to clip the portion of the picture covered by a label:

         import graph;
         
         size(400,200,IgnoreAspect);
         
         real Sin(real t) {return sin(2pi*t);}
         real Cos(real t) {return cos(2pi*t);}
         
         draw(graph(Sin,0,1),red,"$\sin(2\pi x)$");
         draw(graph(Cos,0,1),blue,"$\cos(2\pi x)$");
         
         xaxis("$x$",BottomTop,LeftTicks);
         yaxis("$y$",LeftRight,RightTicks);
         
         label("LABEL",point(0),UnFill(1mm));
         
         add(point(E),legend(20E),UnFill);
    
    lineargraph0.png

    To specify a fixed size for the graph proper, use attach.

         import graph;
         
         size(250,200,IgnoreAspect);
         
         real Sin(real t) {return sin(2pi*t);}
         real Cos(real t) {return cos(2pi*t);}
         
         draw(graph(Sin,0,1),red,"$\sin(2\pi x)$");
         draw(graph(Cos,0,1),blue,"$\cos(2\pi x)$");
         
         xaxis("$x$",BottomTop,LeftTicks);
         yaxis("$y$",LeftRight,RightTicks);
         
         label("LABEL",point(0),UnFill(1mm));
         
         attach(point(E),legend(20E),UnFill);
    
  3. This example draws a graph of one array versus another (both of the same size) using custom tick locations and a smaller font size for the tick labels on the y axis.
         import graph;
         
         size(200,150,IgnoreAspect);
         
         real[] x={0,1,2,3};
         real[] y=x^2;
         
         draw(graph(x,y),red,MarkFill[0]);
         
         xaxis("$x$",BottomTop,LeftTicks);
         yaxis("$y$",LeftRight,RightTicks(Label(fontsize(8)),new real[]{0,4,9}));
    
    datagraph.png

  4. The next example draws two graphs of an array of coordinate pairs, using frame alignment and data markers. In the left-hand graph, the markers, constructed with
         
         marker marker(path g, markroutine markroutine=marknodes, 
                       pen p=currentpen, filltype filltype=NoFill,
                       bool put=Above);
    
    using the path unitcircle (see filltype), are drawn below each node. Any frame can be converted to a marker, using
         
         marker marker(frame f, markroutine markroutine=marknodes,
                       bool put=Above);
    
    In the right-hand graph, the unit n-sided regular polygon polygon(int n) and the unit n-point cross cross(int n) are used to build a custom marker frame. Here markuniform(int n) adds this frame at n uniformly spaced points along the arclength of the path. This example also illustrates the errorbar routines:
         
         void errorbars(picture pic=currentpicture, pair[] z, pair[] dp,
                        pair[] dm={}, bool[] cond={}, pen p=currentpen,
                        real size=0);
         
         void errorbars(picture pic=currentpicture, real[] x, real[] y,
                        real[] dpx, real[] dpy, real[] dmx={}, real[] dmy={},
                        bool[] cond={}, pen p=currentpen, real size=0);
    

    Here, the positive and negative extents of the error are given by the absolute values of the elements of the pair array dp and the optional pair array dm. If dm is not specified, the positive and negative extents of the error are assumed to be equal.

         import graph;
         
         picture pic;
         real xsize=200, ysize=140;
         size(pic,xsize,ysize,IgnoreAspect);
         
         pair[] f={(5,5),(50,20),(90,90)};
         pair[] df={(0,0),(5,7),(0,5)};
         
         errorbars(pic,f,df,red);
         draw(pic,graph(pic,f),"legend",
              marker(scale(0.8mm)*unitcircle,blue,Fill,Below));
         
         xaxis(pic,"$x$",BottomTop,LeftTicks);
         yaxis(pic,"$y$",LeftRight,RightTicks);
         add(point(pic,NW),pic,legend(pic,20SE),UnFill);
         
         picture pic2;
         size(pic2,xsize,ysize,IgnoreAspect);
         
         frame mark;
         filldraw(mark,scale(0.8mm)*polygon(6),green);
         draw(mark,scale(0.8mm)*cross(6),blue);
         
         draw(pic2,graph(pic2,f),marker(mark,markuniform(5)));
         
         xaxis(pic2,"$x$",BottomTop,LeftTicks);
         yaxis(pic2,"$y$",LeftRight,RightTicks);
         
         yequals(pic2,55.0,red+Dotted);
         xequals(pic2,70.0,red+Dotted);
         
         // Fit pic to W of origin:
         add(pic.fit(),W);
         
         // Fit pic2 to E of (5mm,0):
         add((5mm,0),pic2.fit(),E);
    
    errorbars.png
  5. This example draws a graph of a parametrized curve. The calls to
         
         xlimits(picture pic=currentpicture, real min=-infinity,
                 real max=infinity, bool crop=Crop);
    

    and the analogous function ylimits can be uncommented to restrict the respective axes limits for picture pic to the specified min and max values (alternatively, the function limits(pair, pair) can be used to limit the axes to the box having opposite vertices at the given pairs). Existing objects in picture pic will be cropped to lie within the given limits unless crop=NoCrop. For example, if xlimits or ylimits are called with no arguments, existing objects in currentpicture will be cropped to the current graph limits. The function crop(picture pic) is equivalent to calling both xlimits() and ylimits().

         import graph;
         
         size(0,200);
         
         real x(real t) {return cos(2pi*t);}
         real y(real t) {return sin(2pi*t);}
         
         draw(graph(x,y,0,1));
         
         //xlimits(0,1);
         //ylimits(-1,0);
         
         xaxis("$x$",BottomTop,LeftTicks("$%#.1f$"));
         yaxis("$y$",LeftRight,RightTicks("$%#.1f$"));
         
         
    
    parametricgraph.png

    Axis scaling can be requested and/or automatic selection of the axis limits can be inhibited with the scale routine:

         
         void scale(picture pic=currentpicture, scaleT x, scaleT y);
    

    This sets the scalings for picture pic. The graph routines accept an optional picture argument for determining the appropriate scalings to use; if none is given, it uses those set for currentpicture. All path coordinates (and any call to limits, etc.) refer to scaled data. Two frequently used scaling routines Linear and Log are predefined in graph.

    Scaling routines can be given two optional boolean arguments: automin and automax. These default to true, but can be respectively set to false to disable automatic selection of "nice" axis minimum and maximum values. Linear can also take as an optional final argument a multiplicative scaling factor (e.g. for a depth axis, Linear(-1) requests axis reversal).

    For example, to draw a log graph of a function, use scale(Log,Log):

         import graph;
         
         size(200,200,IgnoreAspect);
         
         real f(real t) {return 1/t;}
         
         scale(Log,Log);
         
         draw(graph(f,0.1,10));
         
         //xlimits(1,10);
         //ylimits(0.1,1);
         
         xaxis("$x$",BottomTop,LeftTicks);
         yaxis("$y$",LeftRight,RightTicks);
    
    loggraph.png

    By extending the ticks, one can easily produce a logarithmic grid:

         import graph;
         size(200,200,IgnoreAspect);
         
         real f(real t) {return 1/t;}
         
         scale(Log,Log);
         draw(graph(f,0.1,10),red);
         pen thin=linewidth(0.5*linewidth());
         xaxis("$x$",BottomTop,LeftTicks(begin=false,end=false,extend=true,
         				ptick=thin));
         yaxis("$y$",LeftRight,RightTicks(begin=false,end=false,extend=true,
         				 ptick=thin));
    
    loggrid.png

    One can also specify custom tick locations and formats for logarithmic axes:

         import graph;
         
         size(300,175,IgnoreAspect);
         scale(Log,Log);
         draw(graph(identity,5,20));
         xlimits(5,20);
         ylimits(1,100);
         xaxis("$M/M_\odot$",BottomTop,LeftTicks(new real[] {6,10,12,14,16,18}));
         yaxis("$\nu_{\rm upp}$ [Hz]",LeftRight,
               RightTicks(new string(real x){return format(pow10(x));}));
         
    
    logticks.png

    Here is an example of a "broken" linear x axis that omits the segment [3,8]:

         import graph;
         
         size(200,150,IgnoreAspect);
         
         // Break the axis at 3; restart at 8.
         real a=3, b=8;
         
         scale(Broken(a,b),Linear);
         
         real[] x={1,2,10};
         real[] y=x^2;
         
         draw(graph(x,y),red,MarkFill[0]);
         
         xaxis("$x$",BottomTop,LeftTicks(new real[]{0,1,2,9,10}));
         yaxis("$y$",LeftRight,RightTicks);
         
         label(rotate(90)*Break,(a,currentpicture.userMin.y));
         label(rotate(90)*Break,(a,currentpicture.userMax.y));
         
    
    brokenaxis.png

  6. Asymptote can draw secondary axes with the routines
         
         picture secondaryX(picture primary=currentpicture, void f(picture));
         picture secondaryY(picture primary=currentpicture, void f(picture));
    

    In this example, secondaryY is used to draw a secondary linear y axis against a primary logarithmic y axis:

         import graph;
         texpreamble("\def\Arg{\mathop {\rm Arg}\nolimits}");
         
         size(10cm,5cm,IgnoreAspect);
         
         real ampl(real x) {return 2.5/(1+x^2);}
         real phas(real x) {return -atan(x)/pi;}
         
         scale(Log,Log);
         draw(graph(ampl,0.01,10));
         ylimits(.001,100);
         
         xaxis("$\omega\tau_0$",BottomTop,LeftTicks);
         yaxis("$|G(\omega\tau_0)|$",Left,RightTicks);
         
         picture q=secondaryY(new void(picture pic) {
         		       scale(pic,Log,Linear);
         		       draw(pic,graph(pic,phas,0.01,10),red);
         		       ylimits(pic,-1.0,1.5);
         		       yaxis(pic,"$\Arg G/\pi$",Right,red,
         			     LeftTicks("$% #.1f$",
         				       begin=false,end=false));
         		       yequals(pic,1,Dotted);
         		     });
         label(q,"(1,0)",Scale(q,(1,0)),red);
         add(q);
    
    Bode.png

    A secondary logarithmic y axis can be drawn like this:

         import graph;
         
         size(9cm,6cm,IgnoreAspect);
         string data="secondaryaxis.csv";
         
         file in=line(csv(input(data)));
         
         string[] titlelabel=in;
         string[] columnlabel=in;
         
         real[][] a=dimension(in,0,0);
         a=transpose(a);
         real[] t=a[0], susceptible=a[1], infectious=a[2], dead=a[3], larvae=a[4];
         real[] susceptibleM=a[5], exposed=a[6],infectiousM=a[7];
         
         draw(graph(t,susceptible,t >= 10 && t <= 15));
         draw(graph(t,dead,t >= 10 && t <= 15),dashed);
         
         xaxis("Time ($\tau$)",BottomTop,LeftTicks);
         yaxis(Left,RightTicks);
         
         picture secondary=secondaryY(new void(picture pic) {
           scale(pic,Linear,Log);
           draw(pic,graph(pic,t,infectious,t >= 10 && t <= 15),red);
           yaxis(pic,Right,red,LeftTicks(begin=false,end=false));
         });
         			     
         add(secondary);
         label(shift(5mm*N)*"Proportion of crows",point(NW),E);
         
    
    secondaryaxis.png
  7. Here is a histogram example, which uses the stats module.
         import graph;
         import stats;
         
         size(400,200,IgnoreAspect);
         
         int n=10000;
         real[] a=new real[n];
         for(int i=0; i < n; ++i) a[i]=Gaussrand();
         
         int nbins=100;
         real dx=(max(a)-min(a))/(nbins-1);
         real[] x=min(a)-dx/2+sequence(nbins+1)*dx;
         real[] freq=frequency(x,a);
         freq /= (dx*sum(freq));
         histogram(x,freq);
         
         draw(graph(Gaussian,min(a),max(a)),red);
         
         xaxis("$x$",BottomTop,LeftTicks);
         yaxis("$dP/dx$",LeftRight,RightTicks);
         
         
    
    histogram.png
  8. Here is an example of reading column data in from a file and a least-squares fit, using the stats module.
         size(400,200,IgnoreAspect);
         
         import graph;
         import stats;
         
         file fin=line(input("leastsquares.dat"));
         
         real[][] a=dimension(fin,0,0);
         a=transpose(a);
         
         real[] t=a[0], rho=a[1];
         
         // Read in parameters from the keyboard:
         //real first=getreal("first");
         //real step=getreal("step");
         //real last=getreal("last");
         
         real first=100;
         real step=50;
         real last=700;
         
         // Remove negative or zero values of rho:
         t=rho > 0 ? t : null;
         rho=rho > 0 ? rho : null;
             
         scale(Log,Linear);
         
         int n=step > 0 ? ceil((last-first)/step) : 0;
         
         real[] T,xi,dxi;
         
         for(int i=0; i <= n; ++i) {
           real first=first+i*step;
           real[] logrho=(t >= first && t <= last) ? log(rho) : null;
           real[] logt=(t >= first && t <= last) ? -log(t) : null;
           
           if(logt.length < 2) break;
           
           // Fit to the line logt=L.m*logrho+L.b:
           linefit L=leastsquares(logt,logrho);
             
           T.push(first);
           xi.push(L.m);
           dxi.push(L.dm);
         } 
             
         draw(graph(T,xi),blue);
         errorbars(T,xi,dxi,red);
         
         ylimits(0);
         
         xaxis("$T$",BottomTop,LeftTicks);
         yaxis("$\xi$",LeftRight,RightTicks);
    
    leastsquares.png
  9. Here is an example that illustrates the general axis routine.
         import graph;
         size(0,100);
         
         guide g=ellipse((0,0),1,2);
         axis(Label("C",align=10W),g,LeftTicks(endlabel=false,8,end=false),
              ticklocate(0,360,new real(real v) {
         		  path h=(0,0)--max(abs(max(g)),abs(min(g)))*dir(v);
         		  return intersect(g,h).x;}));
    
    generalaxis.png
  10. To draw a vector field along a path, first define a routine that returns a path as a function of a relative position parameter from [0,1] and use
         
         typedef path vector(real);
         
         void vectorfield(picture pic=currentpicture, path g, int n, 
                          vector vector, real arrowsize=0, real arrowlength=0,
                          pen p=currentpen);
    
    Here is a simple example of a flow field:
         import graph;
         defaultpen(1.0);
         
         size(0,150,IgnoreAspect);
         
         real arrowsize=4mm;
         real arrowlength=2arrowsize;
         
         // Return a vector interpolated linearly between a and b.
         vector vector(pair a, pair b) {
           return new path(real x) {
             return (0,0)--arrowlength*interp(a,b,x);
           };
         }
         
         real alpha=1;
         real f(real x) {return alpha/x;}
         
         real epsilon=0.5;
         path p=graph(f,epsilon,1/epsilon);
         
         int n=2;
         draw(p);
         xaxis("$x$");
         yaxis("$y$");
         
         vectorfield(p,n,vector(W,W),arrowsize);
         vectorfield((0,0)--(currentpicture.userMax.x,0),n,vector(NE,NW),
         	    arrowsize);
         vectorfield((0,0)--(0,currentpicture.userMax.y),n,vector(NE,NE),
         	    arrowsize);
         
    
    flow.png

  11. Asymptote can also generate color density images and palettes. The following palettes are predefined in palette.asy:
    pen[] Grayscale(int NColors=256)
    a grayscale palette;


    pen[] Rainbow(int NColors=65501)
    a rainbow spectrum;


    pen[] BWRainbow(int NColors=65485)
    a rainbow spectrum tapering off to black/white at the ends;


    pen[] BWRainbow2(int NColors=65485)
    a double rainbow palette tapering off to black/white at the ends, with a linearly scaled intensity.

    The function cmyk(pen[] Palette) may be used to convert any of these palettes to the CMYK colorspace. A color density plot can added to a picture pic by generating from a real[][] array data, using palette palette, an image spanning the rectangular region with opposite corners at coordinates initial and final:

         
         void image(picture pic=currentpicture, real[][] data, pen[] palette,
                    pair initial, pair final);
    

    An optionally labelled palette bar may be generated with the routine

         
         picture palette(real[][] data, real width=Ticksize,
                         pen[] palette, Label L, pen p=currentpen,
                         paletteticks ticks=PaletteTicks)
    

    The argument paletteticks is a special tick type (see ticks) that takes the following arguments:

         
         paletteticks PaletteTicks(int N=0, real Step=0,
                                   bool beginlabel=true, bool endlabel=true,
                                   Label format="", pen pTick=nullpen);
    

    The image and palette bar can be fit (and optionally aligned) to a frame and added to picture dest at the location origin using add(pair origin=0, picture dest=currentpicture, frame):

         import graph;
         import palette;
         
         int n=256;
         real ninv=2pi/n;
         real[][] v=new real[n][n];
         
         for(int i=0; i < n; ++i)
           for(int j=0; j < n; ++j)
             v[i][j]=sin(i*ninv)*cos(j*ninv);
         
         pen[] Palette=BWRainbow();
         
         picture plot;
         
         image(plot,v,Palette,(0,0),(1,1));
         picture bar=palette(v,5mm,Palette,"$A$",PaletteTicks("$%+#.1f$"));
         
         add(plot.fit(250,250),W);
         add((1cm,0),bar.fit(0,250),E);
         
    
    image.png
  12. The following scientific graphs, which illustrate many features of Asymptote's graphics routines, were generated from the examples diatom.asy and westnile.asy, using the comma-separated data in diatom.csv and westnile.csv.
    diatom.png
    westnile.png