Go to the previous, next section.
In all these examples, we'll use the del1
macro discussed
above to keep commands off the history list.
Let's start with a Fourier series, to demonstrate SM's ability
to manipulate vectors. All keywords are capitalised for clarity.
Start SM, choose a plotting device (with
the dev
macro), and erase all the commands on the history (or
playback) buffer with DELETE 0 10000
. Then type the following
commands:
SET px=-PI/10,2*PI,PI/200 SET y=SIN(px) + SIN(3*px)/3 + SIN(5*px)/5 + SIN(7*px)/7 SET y=(y>0)?y:0 LIMITS -1 7 y BOX CONNECT px yThe vector
px
could just as well have been read from a file. You
should now have a part of a square-wave, truncated at 0.
Now consider a simpler way of doing the same thing. For the present, clear
the history buffer again (DELETE 0 10000
), and type:
SET px=-PI/10,2*PI,PI/200 SET y=SIN(px) DO i=1,3 { SET val = 2*$i + 1 SET y = y + SIN(val*px)/val } DELETE val LIMITS -1 7 y BOX CONNECT px yHere we use a vector
val
to save a value, an equivalent (but
slower) loop using SM variables would be
DO i=1,3 { DEFINE val (2*$i + 1) DEFINE y = y + SIN($val*px)/$val } DEFINE val DELETE
That is all very well if you only ever wanted to sum the first four terms of the series. Fortunately there is a way to change this, using the macro editor. First define a macro consisting of all the commands on the history list:
del1 MACRO all 0 10000will define the macro
all
to be history lines 0-10000.
(You need the del1
to avoid having the MACRO all 0 10000
in
your macro).
Then you can edit it using
del1 MACRO EDIT allwhen you have made the desired changes (e.g. changing
DO i=1,3
to DO i=1,20
) use ^X to leave the editor and return to the
command editor. Now you could type all
to run your new macro,
or put it back onto the history list. To do the latter, delete the
commands now on the history list (the now-familiar DELETE 0 10000
),
then del1 WRITE HISTORY all
to put the macro all
onto the
list. Now the
playback
command will run all those commands, and produce a better
squarewave. (As discussed in a moment, playback
is a macro so type it
in lowercase, unless you have defined your own PLAYBACK
macro.)
This ability to edit the history buffer is convenient, and there is a macro
provided called edit_hist
which goes through exactly the steps that we
took you through. The trick of including a del1
in macros is
pretty common, for example h
is defined as
del1 HELP
so that it won't appear on the history list.
The macro playback
is rather similar to edit_hist
, but instead
of editing and then writing all
, it executes it. We discussed
the possibility of just playing back a limited number of lines while
talking about hcopy
, just say playback n1 n2
.
Now that you have a set of commands which produce a Fourier plot, it
would be nice to define a macro to make plots, taking the number of
terms as an argument, and then free the history buffer for other things.
After a playback, the macro all
is defined, so let's change its
name to square
. There is a macro ed
defined more-or-less as
del1 MACRO EDIT
, so type ed all
to enter the macro editor.
Use
or ^P to get to line 0 and
change the number of arguments from 0 to 1, and the name of the macro
from all
to square
(the space between the name and the : is
required.) Then go to the DO i=1,20
line, and change 20
to
$1
. Exit with ^X, clear the screen with era
and
type square 10
. Now how do you
save your nice macro? WRITE MACRO square filename
will write it
to file `filename', and next time you run SM
MACRO READ filename
will define it. In fact there is a command SAVE
to save everything
which can be a mindless way of proceeding.
A macro similar to this Fourier macro called square
is in the file demos
in the default macro directory (and was used to
produce the top left box of the cover to this manual). To try it yourself, type
something like load demos square 20
. (20
is the number of
terms to sum.)
If your wondering why ed
is only `more-or-less' defined as del1 MACRO EDIT
, it's because the real ed
checks to see if you
have provided a macro name, and if you haven't it edits the same macro
as last time.
But enough of macros which fiddle with the history buffer. Here are four
sets of macros which do useful things, and may give some idea of the power
available.
First a macro to use the values of a third vector to mark points, then one
to do least-squares fits to data points, then a macro to join
pairs of points, and finally some macros to handle histograms and Gaussians.
These macros are given in the format that SM would
write them to disk (ready for a MACRO READ
), with the name, then the
number of arguments if greater than 0, then the body of the macro.
First the points.
alpha_poi 3 # alpha_poi x y z. Like poi x y, with z as labels for points DO i=0,DIMEN($1)-1 { DEFINE _x ($1[$i]) DEFINE _y ($2[$i]) RELOCATE $_x $_y DEFINE _z ($3[$i]) PUTLABEL 5 $_z } FOREACH v (_x _y _z) { DEFINE $v DELETE 2Here we use the temporary variables
_x _y _z
to get around
restrictions on expressions in RELOCATE
commands.
Note the DO
loop running from 0
to DIMEN($1)-1
, produced
by array indices starting at 0 not 1.
If you wanted to use character strings as labels, this could be done by
using the DEFINE READ
command, but this would be a good deal slower as
SM would have to rescan the file for each data-point. The top right
box of this manual's cover was made using this macro.
The use of alpha_poi
(and also the macro file called ascii
)
has been superceded by the introduction of string-valued vectors into
SM. Nowadays you'd simple read the column that you want to
label the point with as a string (e.g. READ lab 3.s
), set the
point type to that string (e.g. PTYPE lab
), and plot the points as usual
(e.g. POINTS x y
).
The least-squares macro makes heavy use of the SUM
operator.
It could be used to find the dimension of a
vector too, but only clumsily, and DIMEN
is provided anyway. The macro
is:
lsq 4 # Do a least squares fit to a set of vectors # Syntax: lsq x y x2 y2 Fit line y2=$a*x2+$b to x y DEFINE _n (DIMEN($1)) # number of points DEFINE _sx (SUM($1)) # Sigma x DEFINE _sy (SUM($2)) # Sigma y DEFINE _sxy (SUM($1*$2)) # Sigma xy DEFINE _sxx (SUM($1*$1)) # Sigma xx DEFINE a (($_n*$_sxy - $_sx*$_sy)/($_n*$_sxx - $_sx*$_sx)) DEFINE b (($_sy - $a*$_sx)/$_n) SET $4=$a*$3+$b FOREACH v ( _n _sx _sy _sxy _sxx ) {DEFINE $v DELETE 2This does a linear fit, leaving the coefficients in
$a
and $b
. It could
be easily generalised to deal with weights, fits constrained to pass
through the origin, quadratics...
Our third example connects pairs of points. This was written to deal with a set of data points before and after a certain correction had been applied.
pairs 4 # pairs x1 y1 x2 y2. Connect (x1,y1) to (x2,y2) DO i=0,DIMEN($1)-1 { DEFINE _x ($1[$i]) DEFINE _y ($2[$i]) RELOCATE $_x $_y DEFINE _x ($3[$i]) DEFINE _y ($4[$i]) DRAW $_x $_y } FOREACH v ( _x _y ) { DEFINE $v DELETE 2After the introduction of vectors for
ANGLE
and EXPAND
(in version 2.1) this macro can be rewritten to be much faster:
pairs 4 # pairs x1 y1 x2 y2. connect (x1,y1) to (x2,y2) DEFINE 6 { ptype angle expand aspect 2 FOREACH 5 { $!!6 2 { DEFINE $5 | 2 FOREACH 5 {fx1 fx2 fy1 fy2 gx1 gx2 gy1 gy22 { DEFINE $5 DELETE } ASPECT 1 SET _dx$0=($3 - $1)*($gx2 - $gx1)/($fx2 - $fx1) SET _dy$0=($4 - $2)*($gy2 - $gy1)/($fy2 - $fy1) PTYPE 2 0 SET _a$0=(_dx$0 == 0 ? (_dy$0 > 0 ? PI/2 : -PI/2) : \ (_dy$0 > 0 ? ATAN(_dy$0/_dx$0) : ATAN(_dy$0/_dx$0) + PI)) ANGLE 180/pi*_a$0 EXPAND SQRT(1e-5 + _dx$0**2 + _dy$0**2)/(2*128) POINTS (($1 + $3)/2) (($2 + $4)/2) FOREACH 5 { $!!6 2 { $5 $$5 DEFINE $5 DELETE 2 FOREACH 5 ( _a _dx _dy ) { DELETE $5$0 2Note how
DEFINE name |
is used to save things like the angle
and expansion, while DEFINE name DELETE
is used to ensure that the
up-to-date versions of things like fx1
are used (i.e. that they
haven't been DEFINE'd with a !
).
The name of the macro ($0
) is used to
make unique vector names, or at least names like _dxpairs
that
are very unlikely to be in use.
SM allows you to plot a pair of vectors as a histogram, but what if you have only got the raw data points, not yet binned together? Fortunately, SM can do this binning for you. Consider the following macro:
get_hist 6 # get_hist input output-x output-y base top width # given $1, get a histogram in $3, with the centres of the # bins in $2. Bins go from $4 to $5, with width $6. SET $2 = $4+$6/2,$5+$6/2,$6 SET HELP $2 X-vector for $3 SET $3=0*$2 SET HELP $3 Histogram from $1, base $4 width $5 DO i=0,DIMEN($1)-1 { DEFINE j ( ($1[$i] - $4)/$6 ) SET $3[$j] = $3[$j] + 1 } DEFINE j DELETESince this was written, a new feature was added to SM, the expression
HISTOGRAM(x:y)
, to make histograms. The macro we discussed
above can now be written much more efficiently as:
get_hist 6 # get_hist input output-x output-y base top width # given $1, get a histogram in $3, with the centres of the # bins in $2. Bins go from $4 to $5, with width $6. SET $2 = $4+$6/2,$5+$6/2,$6 SET HELP $2 X-vector for $3 SET $3=HISTOGRAM($1:$3) SET HELP $3 Histogram from $1, base $4 width $5
Suppose that your data is in vector x, for want of a better name, and
it has values between 0 and 20. Then the command@example
get_hist x xx yy 0 20 1
will produce a histogram in yy
, bin centres in xx
, running
from 0 to 20 with bins 1 unit wide. So you could plot it with
lim xx yy box hi xx yy
, and maybe it looks like a Gaussian. So what
is the mean and standard deviation? The command @example
stats x mean sig kurt echo $mean $sig $kurt
will answer that, and find the kurtosis too. (Macro stats
consists of lines
such as define $2 ( sum($1)/dimen($1) )
). Then we could use
the macro gauss
to plot the corresponding Gaussian,@example
set z=0,20,.1 set gg=gauss(z) set gg=gg*dimen(x) con z gg
The bottom left box of the cover was made this way. What if you don't
like the way that the histogram looks? Try the macro barhist
.
Now, if you wanted to plot a lognormal, you'd have to write your own
macro, and you could use SORT
to find medians and add another macro
to utils
, followed by one to find Wilcoxon statistics...
(Since this was written a wilcoxon
macro was donated to stats
).
Go to the previous, next section.