Next Previous Contents

## 2. Tutorial

Now that you have seen how to start Rlab, run a program, get help, and interpret error messages, you should be ready to try out some elementary operations. These simple examples are here to help you ``get your feet wet''. Please read this section in front of a computer, and try the examples as you read each one.

Since this is a tutorial, every detail and nuance of each example may not be fully explained. As you work through this section you may have to take some ideas ``on faith''. However, everything should be fully explained in subsequent sections of this manual. If you find something that is not explained, please bring it to the author's attention.

## 2.1 Fundamental Operations

Rlab does not require definition of variable types and size, Unlike more conventional languages such as: Fortran, Pascal, and C. This approach may seem daring at first, but practice has shown that it is most often a much more productive environment for rapid development of programs than strictly typed languages.

### Creating Matrices / Arrays

For starters we will introduce the reader to basic operations that are used in many applications. The first is to create a matrix or array of data so that operations can be demonstrated. The matrix elements are entered at the command line (or in a file). The commas separate the elements of a row, and the semi-colons separate one row from the next.

``` > a = [1,2,3; 4,5,6; 7,8,9] 1 2 3 4 5 6 7 8 9 ```

The commas are required so that there are no ambiguities when more complex expressions are used to create a matrix. For an example, lets create the Attitude matrix for a 3-2-3 Euler angle rotation. The variables `th`, `ph`, and `ps` represent the three Euler angles. To make the notation more concise, we will make the variables `c` and `s` copies of the builtin functions `sin` and `cos`. Next the matrix is entered, with the result displayed upon completion.

``` > th = pi/8; ph = pi/4; ps = pi/16; > c = cos; s = sin; > A = [ c(ps)*c(th)*c(ph)-s(ps)*s(ph), c(ps)*c(th)*s(ph)+s(ps)*c(ph), -c(ps)*s(th); > -s(ps)*c(th)*c(ph)-c(ps)*s(ph),-s(ps)*c(th)*s(ph)+c(ps)*c(ph), s(ps)*s(th); > s(th)*c(ph), s(th)*s(ph), c(th) ] 0.503 0.779 -0.375 -0.821 0.566 0.0747 0.271 0.271 0.924 ```

The matrices we have created thus far: `a`, and `A` are two-dimensional; they have row and column dimensions. Actually, all arrays are two-dimensional. Row and column vectors merely have one dimension equal to one, and scalar values have both dimensions equal to one.

The `show` function displays information about its argument. In the following example, we see that the entire array `a` is a 3-by-3 matrix, from the numeric class, data type real, and uses dense storage. Note that the scalar value `a` is also the same kind of object, just with different dimensions.

``` > show(a); nr : 3 nc : 3 n : 9 class : num type : real storage : dense > show(a); nr : 1 nc : 1 n : 1 class : num type : real storage : dense ```

### Reading Data From a File

Numeric data can also be easily read from a file with the buitin functions (see Section Data). For this example we will read a matrix stored in a text file. The `readm` function will read a text file that contains columns of numbers. In this instance the file looks like:

```        17        24         1         8        15
23         5         7        14        16
4         6        13        20        22
10        12        19        21         3
11        18        25         2         9
```

The matrix can be read with the following statement.

``` > m = readm("magic.dat"); ```

### Basic Math Operations

The basic mathematical operators: `+,-,*,/` work on numeric objects of any dimension. If the operands are scalars, then the operations are performed as expected:

``` > 2 + 3 5 > 2 - 3 -1 > 2 * 3 6 > 2 / 3 0.667 ```

If either of the operands have dimensions higher than one, then array or matrix operations are performed. Array operations act in an element-by-element sense. That is, the scalar value is used repeatedley to perform the operation on each element of the array. For example:

``` > a+2 3 4 5 6 7 8 9 10 11 > 2*a 2 4 6 8 10 12 14 16 18 ```

When both operands are matrices, then matrix operations are performed, provided the dimensions of the operands are appropriate. For example:

``` > a+a 2 4 6 8 10 12 14 16 18 > a*a 30 36 42 66 81 96 102 126 150 > [1,2,3] * a 30 36 42 > a * [1;2;3] 14 32 50 ```

### Basic Tools

In addition to the basic mathematical operators, there are many functions designed to operate efficiently on arrays. Most of the functionality these functions provide could be performed with fairly simple algorithms written in the Rlab language. However, these functions are written in the C-language (a compiled language), and are optimized for operations on arrays. Generally, using these functions will produce programs with good performance, and a minimum of effort. Generally, there are three types of functions: scalar, vector, and matrix.

Scalar Functions:

These functions operate on scalar values, and treat arrays (matrices) in an element-by-element fashion. For example, the function `abs` will return the absolute value of an object (provided it is a numeric object). If the object is scalar in size, the result is scalar. If the object is an array, either a vector or a matrix, then the result is an array of the same size, with each element representing the absolute value of the corresponding element of the input array.

Vector Functions:

These functions operate on either row (1-by-N), or column (N-by-1) vectors. If the argument is an array with dimensions N-by-M, then the operation is performed on the M columns of the input.

Matrix Functions:

These function operate on matrices as a single entity. These functions may return a scalar, a vector, another matrix, or any combination. For example, the function `det` returns a scalar value, while `eig` returns a matrix (the eigenvectors), and a vector (the eigenvalues).

Using the matrices created in the previous section we will demonstrate some of the most frequently used functions.

The matrix `m` has been termed a "magic square" matrix by some. The name is due to the properties of the matrix. First of all, its elements are integers from 1 to N squared (N is the dimension of the matrix). The sum of each row, the sum of each column, and the sum of the diagonal elements are all the same. These properties can be displayed very simply with the help of some functions.

``` > sum(m) 65 65 65 65 65 > sum(m') 65 65 65 65 65 > sum(diag(m)) 65 ```

### Linear Algebra

Rlab contains a high-level interfaces to the LAPACK (Linear Algebra PACKage), the FFTPACK (Fast Fourier Transform PACKage), and the RANLIB (RANdom number LIBrary) libraries. These interfaces can simplify many, otherwise difficult programming tasks. For example, we might be interested in solving a system of equations. Using the magic-square matrix once again

``` > 1/rcond(m) 6.7 > x = solve(m, ones(5,1)) 0.0154 0.0154 0.0154 0.0154 0.0154 > m*x - ones(5,1) 0 0 0 0 0 ```

The function `rcond` estimates the reciprocal of the matrix condition number. A value of 6.7 indicates that the magic-square matrix is reasonably well conditioned (full-rank). Next, we use the `solve` function, to get the solution to the system of equations with coefficients of the magic-square, and right-hand sides of unity. Lastly, we check the result by mulitplying the coefficient matrix by the solution vector (`m*x`) and subtracting the right-hand side. The result should be a zero-vector (and it is).

Note that there are other ways to solve a system of equations. The `\` operator (see Section Arithmetic Operations) could be used like:

``` > x = m\ones(5,1) ```

Or, the `inv` function could be used. However, using `inv` is usually a bad idea.

In addition to the linear-algebra functions supplied to solve systems of equations, there are numerous others such as `eig` for solving eigenvalue problems, and `qr` for performing QR decomposition, and `svd` for performing the singular value decomposition.

## 2.2 Computing the Mean

This example is fairly long, but it does cover allot of ground. For this example it is assumed that there exist data in a file, for which you want to know some statistics. In this case, the mean or average, and the standard deviation. The file looks like:

``` 1 90 2 86 3 55 4 92 5 73 6 30 ```

The students are identified with an integer (the first column). To read this data, and compute the mean or average test score is simple. The `readm` function is used to get the data from the file. The contents of the file are read, and assigned to the matrix `grades`.

``` > grades = readm("jnk"); > sum(grades) 21 426 > sum(grades[;2])/grades.nr 71 ```

The function `sum` sums the column of a matrix, and is used here to look at the sums of both columns. However, only the average of the second column is desired. The following statement singles out the second column of `grades`, uses it as an argument to `sum`, and divides the result by the number of rows in `grades`.

## 2.3 Computing the Mean Again

A more complicated version (only at first glance) of the problems is created when the professor wants to eliminate numeric identification of each student and use their names instead. This file consists of student's names in the first column, and grades in the second column.

``` Jeanne 90 John 86 Fred 55 David 92 Alice 73 Dork 30 ```

Although the file format is simple, many programs/languages would have difficulty handling the mixture of string and numeric data. Rlab has a list-object which allows for convenient association of numeric and string data. Lists are N-dimensional arrays that are indexed associatively. With the list we can create elements that are indexed with the student's name. Each element will then contain the student's grade. For example: `grade.Alice` will contain Alice's grade of 73. First the data must be read and the array `grade` created.

``` > while((length(line = getline("mean_std.ex"))) != 0) { grade.[line.] = line.; } ```

The previous four lines of code may look at little complex, but is really quite simple, taken one step at a time. Starting with the getline function call:

``` line = getline("mean_std.ex") ```

The `getline` function takes a filename as argument, reads one line, and splits it into numbers and strings. The data are returned as a list, with the first element containing the data in the first field, the second element containing the data in the second field and so on. When `getline` can't read anymore information from the file it returns a list with zero length. To read from a file, until the end we use:

``` length(line = getline("mean_std.ex")) != 0 ```

inside a `while` statement. The `while` statement executes until the condition is false (zero). Thus, when the end-of-file is reached, and `getline` returns a zero-length list, the while-loop will terminate. The statement inside the while-loop:

``` grade.[line.] = line.; ```

creates a list-variable named `grade`. Each element of grade is a student's grade. These elements are indexed with the student's name. Remember, `getline` returns a list containing the whitespace separated fields of each line, so: `line.` is the first field, or the student's name in each line, and `line.` is the second field, or the student's grade in each line. The result is a list. We can see the list indices by typing the list-variable's name at the prompt, and we can see the contents of a list element by using the appropriate list index.

``` > grade Alice David Dork Fred Jeanne John > grade.Alice 73 ```

To compute the mean value of the students grades, a simple for-loop is used to sum up the grades, prior to dividing the total by the number of students.

``` > total = 0; > for (i in members (grade)) { total = total + grade.[i]; } > mean = total / length(grade) 71 ```

## 2.4 Fitting a Curve

It is often necessary to fit a curve to some experimental data. This is a simple matter with a high-level language. We will start by generating our own "experimental" data.

First, set the random number generator to generate numbers from a uniform distribution, with a lower bound of -2, and an upper bound of 5.

``` > rand("uniform",-2, 5); ```

Next, generate random data with a linearly varying component. The linearly varying component is formed, and stored in `off`. The simulated, measured data is stored in `b`.

``` > off = 0:-20:-.2; > b = ((off + 22) + rand( size(off) )); ```

Next, generate the Data matrix, `A`.

``` > m = b.n; > t = (1:m)/m; > A = [ ones(m,1), t', (t.^2)' ]; ```

Now use left division (least squares) to solve for `x`.

``` > x = A\b'; ```

Now, create a simple function that uses the computed parameters to make predictions.

```ls = function(t)
{
global (x)
return x + x*t + x*t.^2;
}
```

Last, plot a comparison of the original data, and the computed values.

``` > plgrid (); > pltitle ( "RLaB Least Squares Example" ); > xlabel ( "Indeplendent Variable" ); > ylabel ( "Deplendent Variable" ); > plot( [ t; b; ls( t ) ]' ); ```

Figure XX shows the output from the previous plot commands. The plot command is very simple, but a little mystifying at first. For the time being, you can ignore the `plgrid`, `pltitle`, `xlabel`, and `ylabel` statements; they merely server to add window dressing to the displayed plot. The `plot` takes a matrix as an argument, and plots columns two through the last versus the first column. So, the first column is `t`, the independent variable, The second column is `b`, the experimental data, and the last column is the result of the least-squares fit of the data. The matrix is formed by stacking the three individual row-vectors on top of one another, then transposing the entire matrix so that in the end it is a three column matrix.

Next Previous Contents