Chapter 2: R Tutorial
=====================

"I think it is important for software to avoiding imposing a cognitive style on workers and their work." --- Edward Tufte

```{r date, message=FALSE}
date()
```

Packages
--------
A package is a set of functions for doing specific things.  There are thousands of them. Indeed this is one of the main attractions; the availability of literally hundreds of packages for performing statistical analysis and modeling.  And the range of packages is enormous.  The **BiodiversityR** offers a graphical interface for calculations of environmental trends, the package **Emu** analyzes speech patterns, and the package **GenABEL** is used to study the human genome.

To install and load the package called **UsingR** type
```{r installpackage, eval=FALSE}
install.packages("UsingR")
```
Syntax is case sensitive.  

After the package is downloaded to your computer, you make it available to your R session by typing
```{r madepackageavailable}
require(UsingR)
```

If a package is not available from a particular CRAN site you can try another.  To change your CRAN site, type
```{r chooseCRANmirror, eval=FALSE}
chooseCRANmirror()
```

Calculations
------------
R evaluates commands typed at the prompt.  It returns the result of the computation on the screen or saves it to an object.  For example, to find the sum of the square root of 25 and 2, type
```{r compute1}
sqrt(25) + 2
```

The [1] says `first requested element will follow.'  Here there is only one element, the answer 7.
```{r compute2}
12/3 - 5
```

How would you calculate the 5th power of 2?
```{r compute3}
2^5
```

Functions
---------
There are many math and statistical functions available in R. A function has a name, which is typed, followed by a pair of parentheses (required).  Arguments are added inside this pair of parentheses as needed.  For example, the square root of two is given as
```{r squareroot2}
sqrt(2)    # the square root
```
The pound symbol (#) is the comment character.  Any text in the line following this character is treated as a comment and is not evaluated by R.

```{r sinelogfunctions}
sin(pi)    # the sine function
log(42)   # log of 42 base e
```

Many functions have arguments that allow you to change the default behavior.  For example, to use base 10 for the logarithm, you can use either of the following
```{r, logfunction}
log(42, 10)
log(42, base=10)
```
To understand the first function, log(42, 10), you need to know that R expects the base to be the second argument (after the first comma) of the function.  The second example uses a named argument of the type base= to explicitly set the base value.  The first style contains less typing, but the second style is easier to remember and is good coding practice.

When R doesn't understand your function it responds with an error message.  For example
```{r error}
srt(2)
sqrt(-2)
```
The output NaN is used to indicate the value is not a number.

Assignments
-----------
It is convenient to name an object so you can use it later.  Doing so is called an assignment. You put a name on the left-hand side of the equals sign and a value, function, object, etc on the right.  Assignments generally do not produce output.
```{r myfirstassignment}
x = 2
x + 3
```
Here you assign 2 to x and then add 3 to x.

You can make names out of letters, numbers, and the dot or underline characters.  A name starts with a letter or a dot (a leading dot may not be followed by a number).  You are not allowed to use math operators, such as + and -.  The help page for *make.names* describes this in more detail (*?make.names*).

Case is also important in names; x is different than X.   It is good coding practice to use conventional names for certain types of data.  For instance, n is used for the length of a data record or the length of a vector, x and y are used for spatial coordinates, and i and j are for integers and indices for vectors and matrices.  

These conventions are not forced, but consistently using them makes it easier for you (and others) to look back and understand what you've done.

Variables that begin with the dot character are usually reserved for advanced programmers.  Unlike many programming languages, the period in R is only used as punctuation and can be included in an object name (e.g., my.object).

Getting help
------------
Using R requires knowing many functions.  R has a built in help for information about what is returned by each function, for details on additional arguments, and for examples.  If you know the name of a function for example you type
```{r getHelp}
help(var)
```
This brings up a help page dedicated to the variance function. ?var works the same way. The name of the function and the associate package is given as the preamble followed by a brief description of the function and how it is used.  Arguments are explained along with function and argument details.  Examples given toward the bottom of the page.  A good strategy for understanding what a function does is to copy and paste the examples into your R session.

The examples help you understand what the function does.  You can try the examples individually by copying and pasting them into your R session.  You can also try them all at once by using the example() function.  For instance, type
```{r exampleMean}
example(mean)
```

The help facility works great if you know the name of the function.  If not, there are other ways to search.  For example, the function help.search("mean") searches each entry in the help system and returning matches (often many) of functions that mention the word "mean".  The function apropos searches through function names and variables for matches.
```{r apropos}
apropos("mean")
```

Time to quit
------------
To end your R session type
```{r quit, eval=FALSE}
q(save="no")
```
Like most R functions q needs an open (left) and close (right) parentheses.   The argument save="no" says do not save the workspace.  Otherwise the workspace and session history are saved to a file in your working directory.  By default the file is called .RData.  The workspace is your current R working environment and includes any user-defined objects (vectors, matrices, data frames, lists, functions).

Small amounts of data
---------------------
To begin your analysis you will need to get your data into R.  For small amounts you use the concatenate function.  It combines items like a set of numbers.  Consider a set of hypothetical hurricane counts, where in the first year there were two landfalls in the second three and so on.  To enter these values type
```{r concatenateFunction}
h = c(2, 3, 0, 3, 1, 0, 0, 1, 2, 1)
class(h)
h
```
The ten values are stored in a vector object of class numeric called h.

The assignment operator is an equal sign.   Another assignment operator used frequently is the left pointing arrow that consists of two keystrokes (the less-than sign and the hyphen <-).  This is the more common and it reserves the equal sign for use in declaring argument values.

With most assignments only the prompt is returned to the screen with nothing printed.  The object to the left of the assignment operator is given the values of whatever is to the right of the operator.  They can be printed by typing the object name as you just did.  Finally, the values when printed are prefaced with a [1].  This indicates that the first entry in the object has a value of 2.

Arrow keys
----------
The arrow keys on your computer keyboard can be used to retrieve previous functions.  This saves typing.  Each command is stored in a history file and the up arrow key moves backward through the history file and the down arrow moves forward.  The left and right arrow keys work as expected.  Changes can be made to a mistyped function followed by a return without the need to go to the end of the line.

Functions
---------
Once the data are stored as an object you apply functions to do various things.   For example, you find the total number of land falls occurring over the set of years by typing:
```{r summation}
sum(h)
```
The number of years is found by typing
```{r length}
length(h)
```
The average number of hurricanes over this ten year period is found by typing:
```{r average}
sum(h)/length(h)
mean(h)
```

Other useful functions include sort, min, max, range, diff, and cumsum.  Try them on the object h of land fall counts.  For example, what does the function diff do?

Most functions have a name followed by a left parenthesis, then a set of arguments separated by commas followed by a right parenthesis.   Arguments have names.  Some are required, but many are optional with R providing default values.

In summary consider the code
```{r log42base10}
x = log(42, base=10)
```
Here x is the object name, = is the assignment operator, log is the function, 42 is the value for which the logarithm is being computed, and 10 is the argument corresponding to the logarithm base.  Note here the equal sign is used in two different ways; as an assignment operator and to specify a value for an argument.

Vectors
-------
Your data object h is stored as a vector.  This means that R keeps track of the order the data were entered.  The vector contains a first element, a second element, and so on.  This is convenient.

Your data object of land fall counts has a natural order--year 1, year 2, etc, so you want to keep this order.  You would like to be able to make changes to the data item by item instead of reentering the data.  R lets you do this.  Also, vectors are math objects so math operations can be performed on them.

Suppose h contain the annual land fall count from the first decade of a longer record.  You want to keep track of counts over a second decade.
```{r annualCountsbyDecade}
d1 = h 
d2 = c(0, 5, 4, 2, 3, 0, 3, 3, 2, 1)
```

Most functions operate the vector components all at once.
```{r vectorMath}
d1 + 2
d1 + d2
d1 - d2
d1 - mean(d1)
```
In the second two cases, the first year count of the first decade is added (and subtracted) from the first year count of the second decade and so on.   In the third case a constant (the average of the first decade) is subtracted from each count of the first decade.  This is an example of recycling.  R repeats values from one vector so as to match the length of the other vector.  Here the mean value is computed then repeated 10 times.  Subtraction then follows on each component one at a time.

Suppose you are interested in the variability of hurricane counts from one year to the next.  An estimate of this variability is the variance given by
\[
s^2 = \frac{1}{n-1} \sum_{i=1}^n (h_i - \bar h)^2,
\]
where $n$ is the sample size and $\bar h$ is the sample mean
\[
\bar h = \frac{1}{n}\sum_{i=1}^n h_i
\]

The function var will compute the sample variance but to see how vectorization works here you write a script.
```{r computeVariance}
dbar = mean(d1)
dif = d1 - dbar
ss = sum(dif^2)
n = length(d1)
ss/(n - 1)
```

Note how the different parts of the equation for the variance match what you type in R.  To verify your answer, type
```{r varianceFunction}
var(d1)
```
Similarly the standard deviation, which is the square root of the variance, is obtained by typing
```{r standardDeviationFunction}
sd(d1)
```

To change the number of significant digits printed to the screen from the default of 7, type
```{r optionsFunction}
options(digits=3)
var(d1)
```

A restriction on vectors is that all the components have to have the same type.  You can't create a vector with the first component a numeric value and the second component a character text.  A character vector can be a set of text strings as in
```{r ElsnerFamilyCharacters}
Elsners = c("Jim", "Svetla", "Ian", "Diana")
Elsners
```
Note that character strings are made by matching quotes on both sides of the string, either double or single.  Caution: The quotes must not be directional.  This can cause problems if you type your code in a word processor (like MS Word) as the program will insert directional quotes.  It is better to use a text editor like Notepad.

You add another component to the vector Elsners by using the concatenate function.
```{r coerceToCharacter}
c(Elsners, 1.5)
```
The component 1.5 gets coerced to a character string.  Coercion occurs for mixed types where the components get changed to the lowest common type which is usually a character.  This will prevent you from using the vector in arithmetic operations.

Vector elements can have names.  The names will appear when the vector is printed.  You use the names() function to retrieve and set names as character strings.  For instance you type
```{r namesFunction}
names(Elsners) = c("Dad", "Mom", "Son", "Daughter")
Elsners
```
The names() function appears on the left side of the assignment operator.  The function adds the names attribute to the vector.  Names can be used on vectors of any type.

Returning to your hurricane example, suppose the National Hurricane Center (NHC) finds a previously undocumented hurricane in the 6th year of the 2nd decade.  In this case you type
```{r changeVectorElement}
d2[6] = 1
d2
```
This changes the 6th element (component) of vector d2 to 1 leaving the other components alone.  Note the use of square brackets.  Square brackets are used to subset components of vectors (and arrays, lists, etc), whereas parentheses are used with functions to enclose the set of arguments.

To print the number of hurricanes during the 3rd year only, type
```{r print3rdElement}
d2[3]
```
To print all the hurricane counts except from the 4th year, type
```{r printAllbut4thElement}
d2[-4]
```
To print the hurricane counts for the odd years only, type
```{r printOddYears}
d2[c(1, 3, 5, 7, 9)]
```
Here you use the c() function inside the subset operator [.

Since here you want a regular sequence of years, the expression *c(1, 3, 5, 7, 9)* can be simplified using structured data.

Structured data
---------------
Creating structured data can save you time. For example the colon function is used to create sequences of numbers.
```{r integerSequence}
1:10
10:1
rev(1:10)
```

The seq() function is more general.  It allows for start and end values, but also a step size or sequence length.
```{r theSequenceFunction}
seq(1, 9, by=2)
seq(1, 10, by=2)
seq(1, 9, length=5)
```

Use the rep() function to create a vector with elements having repeat values.   The simplest usage of the function is to replicate the value of the first argument the number of times specified by the value of the second argument.
```{r repeatFunction}
rep(1, times=10)
rep(1:3, times=3)
```
You create more complicated patterns by specifying pairs of equal-sized vectors.  In this case, each component of the first vector is replicated the corresponding number of times as specified in the second vector.
```{r repeatFunction2}
rep(c("cold", "warm"), c(1, 2))
```
Here the vectors are implicitly defined using the c() function and the name of the second argument times= is left off.  It's good coding practice to name the arguments so the order they appear in the function is not important.

Suppose you want to repeat the sequence of cold, warm, warm three times.  You nest the above sequence generator in another repeat function as follows.
```{r repeatFunction3}
rep(rep(c("cold", "warm"), c(1, 2)), 3)
```
Function nesting provides flexibility.

Logic
-----
Functions like mean() and var() when applied to a vector of data output a statistic.  max() is another.  To find the greatest number of hurricanes in a single year during the first decade type
```{r maxFunction}
max(d1)
```
To determine which years had the greatest number type
```{r equalequal}
d1 == 3
```
A single equal sign is an assignment statement, but a double equal sign is a logical operator.  Each element of the vector is matched in value with 3 and a true or false returned. The length of the output will match the length of the vector.

To get the years corresponding to the truth of the logical operature type
```{r whichFunction}
which(d1 == 3)
```

How would you get the number of years in each decade without a hurricane?
```{r sumLogic}
sum(d1 == 0); sum(d2 == 0)
```
Note you can place two commands on a single line by separating them with a semicolon.

The ratio of the mean number of hurricanes over the two decades is
```{r ratioMeans}
mean(d2)/mean(d1)
```
So there is `r round((mean(d2)/mean(d1)-1)*100,digits=0)`\% more landfalls during the second decade.  Is this difference *statistically* significant?

You should remove objects from your workspace that are no longer needed.  This helps you to recycle names.  First, to see what objects reside in your workspace, type
```{r whatObjects}
objects()
```
To remove objects, type
```{r removeFunction}
rm(d1, d2, Elsners)
```
To remove all objects type
```{r removeAll}
rm(list=objects())
```
This will clean your workspace completely.  To avoid name conflicts it is good practice is to start a session with a clean workspace.  Caution: don't include this command in code you share with others.

Getting data into R
-------------------
First check your working directory.
```{r getwdFunction}
getwd()
```
This is the directory where R will look for data.  To change your working directory you use the setwd() function and specifying the path name within quotes.  Alternatively you should be able to use one of the menu options in the R interface.  To list the files in your working directory you type dir().

You also need to know the file type of your data.  This will determine the read function.  For example, the data set *US.txt* contains a list of tropical cyclone counts by year making land fall in the United States (excluding Hawaii) at hurricane intensity.  The file is a space-delimited text file.  In this case you use the read.table() function to import the data.

Does your data file have column names?  These are given in the first line of your file, usually as a series of character strings.  The line is called a `header' and if your data has one, you need to specify header=TRUE.

Make sure the text file *US.txt* is in your working directory and type
```{r readtableFunction}
H = read.table("US.txt", header=TRUE)
```
If you get a prompt without an error message, the data has been imported.

At this stage the most common mistake is that your data file is not in your working directory.  This will result in an error message along the lines of 'cannot open the connection' or 'cannot open file.'

If your file has comma's between columns then use the argument sep="," in the function.  No changes are made to your file.

You can also change the missing value character.  By default it is NA.  If the missing value character in your file is coded as 99, specify na.strings="99" and it will be changed to NA in your data object.

Several variants of read.table() differ only in the default argument settings. read.csv() has settings that are suitable for comma delimited (csv) files that have been exported from a spreadsheet.  Your work flow is to export your data from a spreadsheet to a csv file then import it to R using the read.csv() function.

You can also import data directly from the web by specifying the URL instead of the local file name.
```{r importfromWeb}
loc = "http://www.hurricaneclimate.com/storage/chapter-2/US.txt"
H = read.table(loc, header=TRUE)
```

Data frames
-----------
The object H is a data frame and the function read.table() and its variants return data frames.  Data frames are similar to a spread sheet.  The data are arranged in rows and columns.  The rows are the cases and the columns are the variables.  To see how big your data frame is type
```{r dimFunction}
dim(H)
```
This tells you there are `r I(dim(H)[1])` rows and `r I(dim(H)[2])` columns in your data frame.

To list the first six lines of the data object, type
```{r headFunction}
head(H)
```
The columns include year, number of hurricanes, number of major hurricanes, number of Gulf coast hurricanes, number of Florida hurricanes, and number of East coast hurricanes in order.  Note the column names are given as well.  The last six lines of your data frame are listed similarly using the tail() function.  The number of lines listed is changed with using the argument n.

If your data reside in a directory other than your working directory, you can use the file.choose() function.  This will open a dialog box allowing you to scroll and choose the file.

To make the individual columns available by column name, type
```{r attachFunction}
attach(H)
```

The number of years in the record is obtained and saved in n and the average number of U.S. hurricanes is saved in rate using the following two lines of code.
```{r numberRate}
n = length(All)
rate = mean(All)
```
By typing the names of the saved objects, the values are printed.
```{r printValues}
n
rate
```
Thus over the `r I(n)` years of data the average number of hurricanes per year is `r I(round(rate,digits=2))`.

If you want to change the names of the columns in the data frame, type
```{r changeColNames}
names(H)[4] = "GC"
names(H)
```
This changes the 4th column name from G to GC.  Note that this is done to the data frame in R and not to your data file.

While attaching a data frame is convenient, it is not a good strategy when writing lots of code as name conflicts can easily arise.  If you do attach your data frame, make sure you use the function detach() after finishing.

Tables and summaries
--------------------
Vectors and matrices must have a single class.  For example, the vectors A, B, and C below are constructed as numeric and logical, respectively.
```{r vectorClasses}
A = c(1, 2.2, 3.6, -2.8) #numeric vector
B = c(TRUE, TRUE, FALSE, TRUE)  #logical vector
C = c("Cat 1", "Cat 2", "Cat 3") #character vector
class(A); class(B); class(C)
```

Let the vector wx denote the weather conditions for five forecast periods as character data.
```{r weather}
wx = c("sunny", "clear", "cloudy", "cloudy", "rain")
class(wx)
table(wx)
```
The output from the table() function is a list of the unique character strings and the corresponding number of occurrences of each string.

As another example, let the object ss denote the Saffir-Simpson category for a set of five hurricanes.
```{r SaffirSimpson}
ss = c("Cat 3", "Cat 2", "Cat 1", "Cat 3", "Cat 3")
table(ss)
```
Here the character strings correspond to different intensity levels as ordered categories with Cat 1 < Cat 2 < Cat 3.  In this case convert the character vector to an ordered factor with levels.  This is done using the factor() function.
```{r factorFunction}
ss = factor(ss, order=TRUE)
class(ss)
ss
```
The class gets changed to an ordered factor.  A print of the object results in a list of the elements in the vector and a list of the levels in order.  Note, if you do the same for the wx object, the order is alphabetical by default.  Try it.

You can also use the table() function on discrete numeric data.  For example,
```{r tableFunction}
table(All)
```

The summary() method provides a summary description of your object.  What gets returned depends on the class of the object being summarized.  If your object is a data frame the summary consisting of numeric data and the output includes six statistics (mean, median, minimum, maximum, first quartile, and third quartile) for each column.
```{r summaryFunction}
summary(H)
```

Quantiles
---------

The quartiles output from the summary() method are examples of quantiles.  Sample quantiles cut a set of ordered data into equal-sized data bins.  The ordering comes from rearranging the data from lowest to highest.  The first, or lower, quartile corresponding to the .25 quantile (25th percentile) indicates that 25\% of the data have a value less than this quartile value.  The third, or upper, quartile corresponding to the .75 quantile (75th percentile) indicates that 75\% of the data have a smaller value than this quartile value.

The quantile() function calculates sample quantiles on a vector of data.  For example, consider the set of North Atlantic Oscillation (NAO) index values for the month of June from the period 1851--2010.  The NAO is a variation in the climate over the North Atlantic Ocean featuring fluctuations in the difference of atmospheric pressure at sea level between the Iceland and the Azores.  The index is computed as the difference in standardized sea-level pressures.  The standardization is done by subtracting the mean and dividing by the standard deviation. The units on the index is standard deviation.

First read the data consisting of monthly NAO values, then apply the quantile() function to the June values.
```{r readNAO}
loc = "http://www.hurricaneclimate.com/storage/chapter-2/NAO.txt"
NAO = read.table(loc, header=TRUE)
quantile(NAO$Jun, probs=c(.25, .5))
```
Note the use of the dollar sign to point to a particular column in the data frame.  Recall that to list the column names of the data frame object called NAO type names(NAO).

Of the `r I(length(NAO$Jun))` values, 25% of them are less than `r I(round(quantile(NAO$Jun,probs=.25),2))` standard deviations (sd), 50% are less than `r I(round(quantile(NAO$Jun,probs=.5),2))` sd.  Thus there are an equal number of years with June NAO values between `r I(round(quantile(NAO$Jun,probs=.25),2))` and `r I(round(quantile(NAO$Jun,probs=.5),2))` sd.

The third quartile value corresponding to the .75 quantile (75th percentile) indicates that 75% of the data have a value less than this.  The difference between the first and third quartile values is called the interquartile range (IQR).  Fifty percent of all values lie within the IQR.  The IQR can be found directly by using the IQR() function.

Bar plots
---------
R has a wide range of plotting capabilities.  It takes time to master, but a little effort goes a long way.

The bar plot (or bar chart) is a way to easily compare categorical or discrete data.  Levels of the variable are arranged in some order along the horizontal axis and the frequency of values in each group is plotted as a bar with the bar height proportional to the frequency.

To make a bar plot of your U.S. hurricane counts, type
```{r barplotUShur, fig.width=6, fig.height=4}
barplot(table(All), ylab="Number of Years", xlab="Number of Hurricanes")
```
The plot is a concise summary of the number of hurricanes.  Bar heights are proportional to the number years with that many hurricanes.  The plot conveys the same information as the table.  The purpose of the bar plot is to illustrate the difference between data values.  Readers expect the plot to start at zero so draw it that way.  Usually there is little scientific reason to make the bars appear three dimensional.

Note that the axis labels are set using the ylab and xlab arguments with the actual label as a character string in quotation.  You must be careful to use bi-directional quotes, not the directional quotes that appear in word-processing type.

Although many of the plotting commands are simple and somewhat intuitive, to get a publication quality figure requires tweaking the default settings.  You will see some of these tweaks as you work through the book.

By default the plot is sent to the graphics device for your computer screen.  This might be windows, quartz, or X11.  There are also devices for creating postscript, pdf, png, and jpeg output and sending them to a file in your working directory.  For publication-quality graphics, the postscript and pdf devices are preferred because they produce scalable images.  Use bitmap devices for drafts.

The sequence is to first specify a graphics device, then call your graphics functions, and finally close the device.  For example, to create an encapsulated postscript file (eps) of your bar plot placed in your working directory, type
```{r barplotUShurPS}
postscript(file="MyFirstRPlot.eps")
barplot(table(All), ylab="Number of Years", xlab="Number of Hurricanes")
dev.off()
```
The file containing the bar plot is placed in your working directory.  Note that the postscript() function opens the device and dev.off() closes it.  Make sure you remember to close the device.  To list the files in your working directory type dir().

The pie chart is an alternative to the bar chart for displaying relative frequencies or proportions (?pie).  It represents this information with wedges of a circle or pie.  Since your eye has some difficulty judging relative areas, another alternative to the bar chart is the dot chart.  To find out more about this graph function, type ?dotchart.

Scatter plots
-------------
Perhaps the most useful graph of all is the scatter plot.  You use it to represent the relationship between two continuous variables.  It is a graph of the values of one variable against the values of the other as points $(x_i, y_i)$ in a Cartesian plane.

You use the plot() method to make a scatter plot.  The syntax is plot(x, y) where x and y are vectors containing the paired data.  Values of the variable named in the first argument (here x) are plotted along the horizontal axis.

For example, to graph the relationship between the February and March values of the NAO type
```{r NAOscatterPlot, fig.width=3.5, fig.height=4}
plot(NAO$Feb, NAO$Mar, xlab="February NAO", ylab="March NAO")
```

The plot shows the relationship between the NAO values during February and March.  Low values of the index during February tend to be followed by low values in March and high values in February tend to be followed by high values in March.  You observe there is a direct (or positive) relationship between the two variables although the points are scattered widely indicating the relationship is not tight.

The relationship between two variables can be easily visualized with a scatter plot. You can change the point symbol with the argument pch.  If your goal is to model the relationship, you should plot the dependent variable (the variable you are interested in modeling) on the vertical axis.  Here it might make the most sense to put the March values on the vertical axis since a predictive model would use February values to forecast March values.

The plot produces points as a default. This is changed using the argument type where the letter ell is placed in quotation.  For example, to plot the February NAO values as a time series type
```{r NAOscatterwithLine, fig.width=6, fig.height=4}
plot(NAO$Year, NAO$Feb, ylab="February NAO", xlab="Year", type="l")
```
The values fluctuate about zero and do not appear to have a long-term trend.  With time series data it is better to connect the values with lines rather than use points unless values are missing.

This concludes your introduction to R.  We showed you where to get R, how to install it, and how to install packages.  We showed you how use R as a calculator, how to work with functions, make assignments, and get help.  We also showed you how to work with small amounts of data and how to import data from a file.  We concluded with how to tabulate, summarize, and make some simple plots.