Repository: jtablesaw/tablesaw
Pull request #631 (2019-08-24 15:04:09)

Added missing value handling and getNumber() method  to Row.
Thanks for contributing.

- [ ] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

What was changed

add isMissing and setMissing methods to Row.

added double: getNumber() method to row. Returns a double for any numeric column, with requiring the user to specify the column type. 

## Testing

Did you add a unit test? yes



Patch:
diff --git a/core/src/main/java/tech/tablesaw/api/Row.java b/core/src/main/java/tech/tablesaw/api/Row.java
index 1d5fc507..edee4c32 100644
--- a/core/src/main/java/tech/tablesaw/api/Row.java
+++ b/core/src/main/java/tech/tablesaw/api/Row.java
@@ -52,17 +52,17 @@ public class Row implements Iterator<Row> {
           String actualType = tableSlice.getTable().columns().get(i).type().name();
           String proposedType = columnTypeName();
           throw new IllegalArgumentException(
-              String.format(
-                  "Column %s is of type %s and cannot be cast to %s. Use the method for %s.",
-                  columnName, actualType, proposedType, actualType));
+                  String.format(
+                          "Column %s is of type %s and cannot be cast to %s. Use the method for %s.",
+                          columnName, actualType, proposedType, actualType));
         }
       }
     }
 
     private void throwColumnNotPresentError(String columnName) {
       throw new IllegalStateException(
-          String.format(
-              "Column %s is not present in table %s", columnName, tableSlice.getTable().name()));
+              String.format(
+                      "Column %s is not present in table %s", columnName, tableSlice.getTable().name()));
     }
 
     private String columnTypeName() {
@@ -84,10 +84,11 @@ public class Row implements Iterator<Row> {
   private final ColumnMap<Column<String>> stringColumnMap = new ColumnMap<>(ColumnType.STRING);
   private final ColumnMap<BooleanColumn> booleanColumnMap = new ColumnMap<>(ColumnType.BOOLEAN);
   private final ColumnMap<DateTimeColumn> dateTimeColumnMap =
-      new ColumnMap<>(ColumnType.LOCAL_DATE_TIME);
+          new ColumnMap<>(ColumnType.LOCAL_DATE_TIME);
   private final ColumnMap<InstantColumn> instantColumnMap = new ColumnMap<>(ColumnType.INSTANT);
   private final ColumnMap<TimeColumn> timeColumnMap = new ColumnMap<>(ColumnType.LOCAL_TIME);
   private final ColumnMap<Column<?>> columnMap = new ColumnMap<>();
+  private final ColumnMap<NumericColumn<? extends Number>> numericColumnMap = new ColumnMap<>();
   private int rowNumber;
 
   public Row(Table table) {
@@ -109,18 +110,23 @@ public class Row implements Iterator<Row> {
     for (Column<?> column : tableSlice.getTable().columns()) {
       if (column instanceof DoubleColumn) {
         doubleColumnMap.put(column.name(), (DoubleColumn) column);
+        numericColumnMap.put(column.name(), (DoubleColumn) column);
       }
       if (column instanceof IntColumn) {
         intColumnMap.put(column.name(), (IntColumn) column);
+        numericColumnMap.put(column.name(), (IntColumn) column);
       }
       if (column instanceof ShortColumn) {
         shortColumnMap.put(column.name(), (ShortColumn) column);
+        numericColumnMap.put(column.name(), (ShortColumn) column);
       }
       if (column instanceof LongColumn) {
         longColumnMap.put(column.name(), (LongColumn) column);
+        numericColumnMap.put(column.name(), (LongColumn) column);
       }
       if (column instanceof FloatColumn) {
         floatColumnMap.put(column.name(), (FloatColumn) column);
+        numericColumnMap.put(column.name(), (FloatColumn) column);
       }
       if (column instanceof BooleanColumn) {
         booleanColumnMap.put(column.name(), (BooleanColumn) column);
@@ -292,6 +298,12 @@ public class Row implements Iterator<Row> {
     return stringColumnMap.get(columnName).get(getIndex(rowNumber));
   }
 
+  public boolean isMissing(String columnName) {
+    Column<?> x = columnMap.get(columnName);
+    int i = getIndex(rowNumber);
+    return x.isMissing(i);
+  }
+
   @Override
   public boolean hasNext() {
     return rowNumber < this.tableSlice.rowCount() - 1;
@@ -303,6 +315,14 @@ public class Row implements Iterator<Row> {
     return this;
   }
 
+  public void setMissing(int columnIndex) {
+    setMissing(columnNames[columnIndex]);
+  }
+
+  public void setMissing(String columnName) {
+    columnMap.get(columnName).setMissing(getIndex(rowNumber));
+  }
+
   public void setBoolean(int columnIndex, boolean value) {
     setBoolean(columnNames[columnIndex], value);
   }
@@ -395,10 +415,24 @@ public class Row implements Iterator<Row> {
     setTime(columnNames[columnIndex], value);
   }
 
+  /**
+   * Returns the row number for this row, relative to the backing column
+   *
+   * @param rowNumber the rowNumber in the TableSlice backing this (row)
+   * @return the matching row number in the underlying column.
+   */
   private int getIndex(int rowNumber) {
     return tableSlice.mappedRowNumber(rowNumber);
   }
 
+  public double getNumber(String columnName) {
+    return numericColumnMap.get(columnName).getDouble(rowNumber);
+  }
+
+  public ColumnType getColumnType(String columnName) {
+    return columnMap.get(columnName).type();
+  }
+
   @Override
   public String toString() {
     Table t = tableSlice.getTable().emptyCopy();
@@ -412,4 +446,4 @@ public class Row implements Iterator<Row> {
   public void setTime(String columnName, LocalTime value) {
     timeColumnMap.get(columnName).set(rowNumber, value);
   }
-}
+}
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 978ba5a3..795781cb 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,9 +1,10 @@
 # Tablesaw Documentation
 
-* [Introductory Tutorial](https://jtablesaw.github.io/tablesaw/gettingstarted) If you haven't used Tablesaw, or are having trouble 'getting it', you should definitely start here. It covers most everything you need to begin using it productively. 
-* [User Guide](https://jtablesaw.github.io/tablesaw/userguide/toc)
+* [The Tornado Tutorial](https://jtablesaw.github.io/tablesaw/tutorial) A short tutorial using Tablesaw to analyze a Tornado dataset.
+* [Getting Started](https://jtablesaw.github.io/tablesaw/gettingstarted)  If you haven't used Tablesaw, or are having trouble 'getting it', you should start here. It covers most everything you need to begin using it productively. 
+* [User Guide](https://jtablesaw.github.io/tablesaw/userguide/toc) An in-depth guide to much of Tablesaw's functionality.
 * [Javadoc](http://www.javadoc.io/doc/tech.tablesaw/tablesaw-core/)
-* [Contributor's Guide](https://jtablesaw.github.io/tablesaw/contributing)
+* [Contributor's Guide](https://jtablesaw.github.io/tablesaw/contributing) We welcome contributions! Here's how. 
 * [Release Notes](https://github.com/jtablesaw/tablesaw/releases)
 
 Can't find what you're looking for?
diff --git a/docs/gettingstarted.md b/docs/gettingstarted.md
index 9000fa45..e38b7025 100644
--- a/docs/gettingstarted.md
+++ b/docs/gettingstarted.md
@@ -2,7 +2,7 @@
 
 Java is a great language, but it wasn't designed for data analysis. Tablesaw makes it easy to do data analysis in Java. 
 
-Once you're up and running, this tutorial will tell you what you need to know to use Tablesaw.
+This tutorial will help you get up and running, and introduce some of Tablesaw's basic functionality.
 
 ## Setup
 
@@ -28,7 +28,7 @@ As you would expect, Tablesaw is all about tables, and tables are made of column
 
 A column is a named, one-dimensional collection of data. It may or may not be part of a table. All data in a column must be of the same type. 
 
-Tablesaw supports columns for Strings, doubles, booleans, LocalDates, LocalTimes, and LocalDateTimes. The date and time columns are comparable with the java.time classes introduced in Java 8.
+Tablesaw supports columns for Strings, floats, doubles, ints, shorts, longs, booleans, LocalDates, LocalTimes, Instants, and LocalDateTimes. The date and time columns are comparable with the java.time classes introduced in Java 8. 
 
 To create a column you can use one of its static *create()* methods:
 
@@ -47,7 +47,7 @@ Column: Test
 ```
 Each column has an associated 0-based index. To get an individual value call *get()* with the index. 
 ```java
-nc.get(2);
+double three = nc.get(2);
 ```
 which returns 3.0.
 
@@ -74,7 +74,7 @@ There are so many columnar operations in Tablesaw that, as a general rule, if yo
 
 #### Objects and Primitives
 
-Many Java programs and programmers work exclusively with Objects, rather than primitives. In Tablesaw, we use primitives whenever possible because they use  *much* less memory than their boxed alternatives.  A Byte object, for example, uses as much memory as a primitive double, even though bytes have a range of only 256 values. 
+Many Java programs and programmers work exclusively with Objects, rather than primitives. In Tablesaw, we often use primitives because they use *much* less memory than their boxed alternatives.  A Byte *object*, for example, uses as much memory as a primitive double, even though bytes have a range of only 256 values. 
 
 There is a price for this frugality. When you work with primitives, you forgo some common java capabilities, like the use of standard Java 8 predicates. While Java thoughtfully provides some specialized predicate interfaces (e.g. *IntPredicate*), they don't provide any primitive *BiPredicate* implementations, nor do their primitive interfaces cover all primitive types. Without an IntBiPredicate, we can't implement operations like a < b. So we were left to roll our own. You can find them in the package *tech.tablesaw.filtering.predicates*. They work like the standard objects. 
 
@@ -112,10 +112,10 @@ If the methods returned columns directly, they couldn't be combined this way.  I
 These examples show how to select using predicates. You can also use a selection to retrieve the value at a specific index, or indexes. All of the following are supported:
 
 ```java
-nc.where(Selection.with(0, 2));  	// returns 2 rows with the given indexes
-nc.where(Selection.selectNRowsAtRandom(2)); // returns 2 randomly selected rows
-nc.where(Selection.withRange(1, 3));		// returns rows 1-3 inclusive
-nc.where(Selection.withoutRange(1, 3));		// returns row 0
+nc.where(Selection.with(0, 2));  							// returns 2 rows with the given indexes
+nc.where(Selection.selectNRowsAtRandom(2)); 	// returns 2 randomly selected rows
+nc.where(Selection.withRange(1, 3));					// returns rows 1-3 inclusive
+nc.where(Selection.withoutRange(1, 3));				// returns row 0
 ```
 
 If you have several columns of the same length as you would in a table of data, you can make a selection with one column and use it to filter another:
@@ -143,7 +143,7 @@ StringColumn s;
 s = aStringColumn.upperCase();
 s = s.replaceFirst("foo", "bar")
 s = s.substring(3, 10);
-s = s.padEnd(4, 'x');				// put 4 x chars at the end of each string
+s = s.padEnd(4, 'x');					// put 4 x chars at the end of each string
 
 // this returns the common prefix of each row in two columns
 y = s.commonPrefix(anotherStringColumn);
@@ -202,7 +202,7 @@ Tablesaw does a pretty good job at guessing the column types for many data sets,
 
 #### Exploring Tables
 
-Because Tablesaw excels at manipulating tables, we use them whenever we can.  When you ask tablesaw for the structure of a table, the answer comes back in the form of another table where one column contains the column names, etc.  The structure() method is one of several that help you to know a new data set. Here are some examples.
+Because Tablesaw excels at manipulating tables, we use them whenever we can.  When you ask tablesaw for the structure of a table, the answer comes back in the form of another table where one column contains the column names, etc.  The methods ` structure()`, `shape()`, `first(n)`, and `last(n)` can help you get to know a new data set. Here are some examples.
 
 ```java
 Table structure = bushTable.structure();
@@ -225,8 +225,7 @@ Table head = bushTable.first(3);
 	 2004-01-21  |      53.0  |  fox  |
 	 2004-01-07  |      58.0  |  fox  |
                      
-Table tail = myTable.last(3);
-> etc.
+Table tail = myTable.last(3);  // etc.
 ```
 
 Table's *toString()* method returns a String representation like those shown above. It returns a limited number of rows by default, but you can also use *table.printAll()*, or *table.print(n)* to get the output you want.
@@ -238,12 +237,12 @@ Of course, this is just the beginning of exploratory data analysis. You can also
 Often you'll work with specific columns in a table. Here are some useful methods:
 
 ```java
-table.columnNames();  			// returns all column names
-List<Column> = table.columns(); // returns all the columns in the table
+table.columnNames();  			              // returns all column names
+List<Column> = table.columns();           // returns all the columns in the table
 
 // removing columns
-table.removeColumns("Foo");			// keep everything but "foo"
-table.retainColumns("Foo", "Bar");  // only keep foo and bar
+table.removeColumns("Foo");			         // keep everything but "foo"
+table.retainColumns("Foo", "Bar");       // only keep foo and bar
 table.removeColumnsWithMissingValues();
 
 // adding columns
@@ -265,23 +264,26 @@ remembering column names is enough of a burden without having to remember exactl
 Columns can be retrieved from tables by name or position. The simplest method *column()* returns a object of type Column. This may be good enough, but often you want to get a column of a specific type. For example, you would need to cast the value returned to a NumberColumn to use its values in a scatter plot. 
 
 ```java
-Column nc = table.column("Foo"); // returns the column named 'Foo' if it's in the table.
+Column column = table.column("Foo"); // returns the column named 'Foo' if it's in the table.
 // or 
-Column nc = table.column(0);  // returns the first column
+Column column = table.column(0);     // returns the first column
+```
+
+When a variable type is "Column" it only provides methods that are available on *all* columns. You can't perform math or do a string replace directly on a Column type. If you need a StringColumn you could cast the column, for example: 
 
-// To work with a StringColumn you can cast the return value
-StringColumn nc = (StringColumn) table.column(0); 
-// Now you can do some numeric stuff with the column nc
+```java
+StringColumn sc = (StringColumn) table.column(0); 
 ```
 
 Table also supports methods that return columns of the desired type directly:
 
 ```Java
-StringColumn nc = table.stringColumn(0); 
-DateColumn dc = table.dateColumn("start date");
+StringColumn strings = table.stringColumn(0); 
+DateColumn dates = table.dateColumn("start date");
+DoubleColumn doubles = table.doubleColumn("doubles");
 ```
 
-If you want all the columns of specific type, you can get those as well. The method columnsOfType(aColumnType) returns them as a List, but you still have to cast the results. For example, to format all columns as ints when you print them, you could do this. 
+If you want all the columns of specific type, you can get those as well. The method columnsOfType(aColumnType) returns them as a List, but you still have to cast the results. For example, to format all DoubleColumns as ints when you print them, you could do this: 
 
 ```Java
 table.columnOfType(ColumnType.DOUBLE).forEach(x ->                                               		((DoubleColumn)x).setPrintFormatter(NumberColumnFormatter.ints()));
@@ -301,7 +303,7 @@ result = table.dropRowsWithMissingValues();
 result = table.dropWhere(table.numberColumn(0).isLessThan(100));
 
 // add rows
-table.addRow(43, sourceTable);	// adds row 43 from sourceTable to the receiver
+destinationTable.addRow(43, sourceTable);	// adds row 43 from sourceTable to the receiver
 
 // sampling
 table.sample(200);				// select 200 rows at random from table 
@@ -315,7 +317,7 @@ for (Row row : table) {
 }
 ```
 
-There are better ways, however. Another approach lets you skip the iteration and just provide a Consumer for each row.
+Another approach lets you skip the iteration and just provide a Consumer for each row.
 
 ```java
 table.stream().forEach(row -> {
@@ -353,8 +355,8 @@ Table sorted = table.sort("foo", "bar", "bam");
 The above code sorts in ascending order by default.  Other options are shown below:
 
 ```java
+sorted = table.sortAscending("bar"); 	 // just like sort(), but makes the order explicit.
 sorted = table.sortDescending("foo");
-sorted = table.sortAscending("bar"); 	// just like sort(), but makes order explicit.
 
 /* sort on foo ascending, then bar descending. Note the minus sign preceding the name of column bar. */
 sorted = table.sort("foo", "-bar");		 
@@ -364,7 +366,7 @@ See [Sorting](https://jtablesaw.github.io/tablesaw/userguide/sorting) for more i
 
 #### Filtering tables with selections
 
-Tables also use selections to perform filtering. The basic approach is similar.
+We showed an example above of how to use the where() method to perform filtering. The basic approach is similar.
 
 ```java
 Table t = Table.create("test").addColumns(nc1, nc2);
@@ -379,8 +381,8 @@ Query filters can be combined using the logical operations *and*, *or*, and *not
 
 ```java
 Table result = t.where(
-	t.and(nc1.isGreaterThan(4),
-         t.or(t.not(nc2.isLessThanOrEqualTo(5)),
+	and(nc1.isGreaterThan(4),
+         or(t.not(nc2.isLessThanOrEqualTo(5)),
          		nc2.isEven()));
 ```
 
diff --git a/docs/tutorial.md b/docs/tutorial.md
index 66c21b1c..9f481efd 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -1,9 +1,8 @@
 
 
-# Introducing Tablesaw: A brief tutorial
+# Tablesaw: A brief tutorial
 
-Tablesaw is a fairly large library. In this section, we touch on the most common operations. If you haven't already done
-so, we strongly recommend that you read the Getting Started guide, before continuing here. 
+Tablesaw is a large library. We'll use Tablesaw to look at data about Tornadoes. If you haven't already done so, we strongly recommend that you read the Getting Started guide, before continuing here. 
 
 ## Exploring Tornadoes 
 
@@ -25,9 +24,11 @@ All the data is in the Tablesaw *data* folder. The code is taken from the Tornad
 
 Here we read a csv file of tornado data. Tablesaw infers the column types by sampling the data.
 
-    Table tornadoes = Table.read().csv("../data/tornadoes_1950-2014.csv");
+```java
+Table tornadoes = Table.read().csv("../data/tornadoes_1950-2014.csv");
+```
 
-Note that the file is addressed relative to the current working directory. You may have to change it for your code. 
+**Note:** that the file is addressed relative to the current working directory. You may have to change it for your code. 
 
 ### Viewing table metadata
 
@@ -46,21 +47,23 @@ The *shape()* method displays the row and column counts:
 
 *structure()* shows the index, name and type of each column
 
-    tornadoes.structure();
-    
-    >>  Structure of data/tornadoes_1950-2014.csv
-            Index Column Names Column Type 
-            0     Date         LOCAL_DATE  
-            1     Time         LOCAL_TIME  
-            2     State        CATEGORY    
-            3     State No     DOUBLE     
-            4     Scale        DOUBLE     
-            5     Injuries     DOUBLE     
-            6     Fatalities   DOUBLE     
-            7     Start Lat    DOUBLE       
-            8     Start Lon    DOUBLE       
-            9     Length       DOUBLE       
-            10    Width        DOUBLE       
+```java
+tornadoes.structure().printAll();
+
+>>  Structure of data/tornadoes_1950-2014.csv
+        Index Column Names Column Type 
+        0     Date         LOCAL_DATE  
+        1     Time         LOCAL_TIME  
+        2     State        STRING    
+        3     State No     DOUBLE     
+        4     Scale        DOUBLE     
+        5     Injuries     INTEGER     
+        6     Fatalities   INTEGER     
+        7     Start Lat    DOUBLE       
+        8     Start Lon    DOUBLE       
+        9     Length       DOUBLE       
+        10    Width        DOUBLE       
+```
 
 Like many Tablesaw methods, *structure()* returns a table.  You can then produce a string representation for display. For convenience, calling *toString()* on a table invokes *print()*, which produces a string representation of the table table. To display the table then, you can simply call. 
 
@@ -98,7 +101,7 @@ Mapping operations in Tablesaw take one or more columns as inputs and produce a
 StringColumn month = tornadoes.dateColumn("Date").month();
 ```
 
-Now that you have a new column, you can add it to a table:
+Now that you have a new column, you can add it to the table:
 
 ```java
 tornadoes.addColumn(month);
@@ -139,13 +142,13 @@ table.column("Fatalities").summary().print();
 You can write your own methods to filter rows, but it’s easier to use the built-in filter classes as shown below:
 
 ```java 
-result = tornadoes.selectWhere(DOUBLEColumn("Fatalities").isGreaterThan(0));
+result = tornadoes.selectWhere(doubleColumn("Fatalities").isGreaterThan(0));
 
 result = tornadoes.selectWhere(dateColumn("Date").isInApril());
 
 result = tornadoes.selectWhere(either
-           (DOUBLEColumn("Width").isGreaterThan(300)),   // 300 yards
-           (DOUBLEColumn("Length").isGreaterThan(10)));  // 10 miles
+           (doubleColumn("Width").isGreaterThan(300)),   // 300 yards
+           (doubleColumn("Length").isGreaterThan(10)));  // 10 miles
 
 result = tornadoes.select("State", "Date").where(column("Date").isInQ2());
 ```
@@ -223,7 +226,7 @@ summer.addColumn(summer.dateColumn("Date").lag(1));
 DateColumn summerDate = summer.dateColumn("Date");
 DateColumn laggedDate = summer.dateColumn("Date lag(1)");
 
-DOUBLEColumn delta = laggedDate.daysUntil(summerDate);
+DoubleColumn delta = laggedDate.daysUntil(summerDate);
 summer.addColumn(delta);
 ```
 
@@ -252,8 +255,6 @@ summary.nCol(1).mean();
 >>	Average days between tornadoes in the summer: 1.761269943549373
 ```
 
-
-
 ### Saving your data
 
 To save a table, you can write it as a CSV file:
@@ -262,5 +263,5 @@ To save a table, you can write it as a CSV file:
 tornadoes.write().csv("data/rev_tornadoes_1950-2014.csv");
 ```
 
-And that’s it for the introduction. Stay tuned for more info about advanced features.
+And that’s it for the introduction. Please see the User Guide for more information.
 
diff --git a/docs/userguide/importing_data.md b/docs/userguide/importing_data.md
index f186fc36..50515d5c 100644
--- a/docs/userguide/importing_data.md
+++ b/docs/userguide/importing_data.md
@@ -1,13 +1,32 @@
 [Contents](https://jtablesaw.github.io/tablesaw/userguide/toc)
 
-Importing data
-==============
+# Importing & exporting data
 
-To minimize the size of the core library, the readers are packaged in separate modules except for the CSV reader. You will need to include them in your project to use the `Table.read()` methods.
+## Table of contents
+
+[TOC]
+
+## Supported Formats
+
+Tablesaw supports importing and exporting data to and from a variety of data types and sources. 
+
+| Format                         | Import | Export |
+| :----------------------------- | :----- | :----- |
+| CSV (and other delimited text) | Yes    | Yes    |
+| JSON                           | Yes    | Yes    |
+| RDBMS (via JDBC)               | Yes    |        |
+| Fixed Width Text               | Yes    | Yes    |
+| Excel                          | Yes    | Yes    |
+
+## Importing data
+
+**Note**: To minimize the size of the core library, some of the readers (currently JSON, HTML tables, and Excel files) are packaged in separate modules. You will need to include them in your project to use the `Table.read()` methods.
 
 See the Javadoc for [DataFrameReader](http://static.javadoc.io/tech.tablesaw/tablesaw-core/0.31.0/tech/tablesaw/io/DataFrameReader.html) for a listing of all the `Table.read()` methods that are available.
 
-## Reading CSV files
+### Text files (CSV, Tab-delimited, Fixed-width field, etc.)
+
+Most text formats are treated similarly. This section covers rectangular text formats like CSV, but much of the information is also relevant for JSON, Excel, and HTML tables. 
 
 The easiest way to load data from a CSV file on disk is to use:
 
@@ -22,9 +41,9 @@ You can create an options object with a builder:
 ```Java
 CsvReadOptionsBuilder builder = 
 	CsvReadOptions.builder("myFile.csv")
-		.separator('\t')			// table is tab-delimited
-		.header(false)				// no header
-		.dateFormat("yyyy.MM.dd");  // the date format to use. 
+		.separator('\t')										// table is tab-delimited
+		.header(false)											// no header
+		.dateFormat("yyyy.MM.dd");  				// the date format to use. 
 
 CsvReadOptions options = builder.build();
 
@@ -39,13 +58,13 @@ The *dateFormat* lets you provide a format for reading dates. All dates in the f
 
 When the table is created, it is given a default name based on the name of the file it was loaded from. You can change the name at any time using ```table.setName(aString);```. The table name is used in printing the table and information about it.
 
-### Column types
+#### Column types
 
 With all these methods, Tablesaw looks at the data in each column in the file and takes a wild guess at the type. Actually, it looks at a *sample* of the data and applies some heuristics. Of course, it’s possible that the data set includes rare values that are missed in the type inference sample. If that happens, you can set the option ```sample(false)``` to consider all the data when performing type inference. 
 
 If nothing else seems to fit, the column is read as a StringColumn. Usually, Tablesaw gets it right, but sometimes it needs a little help. 
 
-#### Specifying the datatypes for each column
+##### Specifying the datatypes for each column
 
 By a little help, we mean you could specify the types explicitly, by passing an array of ColumnType objects to the read().csv() method. For example:
 
@@ -58,13 +77,13 @@ Table t = Table.read().usingOptions(CsvReadOptions
 
 If that seems like a lot of work, it does have some advantages.
 
-First, it reduces the loading time as the system does not need to infer the column types, which takes some time. The amount of time saved can be noticeable if the file is large.
+First, it reduces the loading time as the system does not need to infer the column types. The time saved can be significant if the file is large.
 
 Second, it gives you complete control over the types for your columns. 
 
 In some cases, you *must* specify the column type, because Tablesaw can’t guess correctly. For example, if a file has times encoded as HHmm so that noon appears as ‘1200’, it’s impossible to infer that this means 12:00 noon, and not the integer 1,200.  
 
-#### A shortcut: Getting the guessed column types
+##### A shortcut: Getting the guessed column types
 
 If the table has many columns, it can be tedious to build the column type array by hand. To help, CsvReader has methods that return the inferred ColumnTypes in the form of an array, or as a String. The String is formatted so that it resembles a Java array literal. This method can be used even if reading the file fails.
 
@@ -80,7 +99,7 @@ System.out.println(types);
 
 Note that the returned String is a legal array literal you can paste into Java code: the types are comma separated, and the index position and the column name are provided such that they would be interpreted as comments. You can paste it into your code and then edit it to fix whatever column types are incorrect.
 
-#### Skipping columns during import
+##### Skipping columns during import
 
 Another advantage to specifying the column types is that you can skip some if you don't need them. You can prevent those columns from being imported by using the special “SKIP” column type as shown below:
 
@@ -93,7 +112,7 @@ Table t = Table.read().usingOptions(CsvReadOptions
 
 In this example, the first and last columns are not loaded.
 
-### Handling Missing data
+#### Handling Missing data
 
 Tablesaw has a predefined set of strings that it interprets as missing data when reading from a CSV file. These are: “NaN”,  “*”, “NA”, “null” and, of course, the empty string “”.
 
@@ -107,7 +126,7 @@ Table t = Table.read().usingOptions(CsvReadOptions
     .missingValueIndicator("-"));
 ```
 
-### Dealing with Dates and Times
+#### Dealing with Dates and Times
 
 Importing dates and times can be tricky because of Locales and the wide variety of possible formats. As with other Column types, Tablesaw does its best to determine what type is represented and import it correctly. When this fails, two things can help. The first is to specify a locale.  A locale can also help with number formats. 
 
@@ -122,7 +141,7 @@ Table t = Table.read().usingOptions(CsvReadOptions
     .dateTimeFormat("yyyy.MM.dd::HH:mm:ss");
 ```
 
-## Using the Stream API
+### Using the Stream API
 
 All the examples above attempt to streamline the loading process when you have a CSV file stored on your file system. A more flexible way to load a CSV is using the Stream interface, which takes a java.io.InputStream as a parameter.
 
@@ -132,7 +151,7 @@ Table.read().csv(InputStream stream, String tableName);
 
 It can be used to read local files, but also files read across the net, in S3, etc. Here’s some examples of how it can be used.
 
-### Loading a CSV from a Website:
+#### Loading a CSV from a Website:
 
 ```java
 ColumnType[] types = {SHORT_INT, FLOAT, SHORT_INT};
@@ -143,7 +162,7 @@ Table table = Table.read().usingOptions(CsvReadOptions.builder(new URL(location)
   	.columnTypes(types)));
 ```
 
-### Loading a CSV from S3:
+#### Loading a CSV from S3:
 
 ```Java
 ColumnTypes[] types = {SHORT_INT, FLOAT, SHORT_INT};
@@ -169,7 +188,7 @@ Table restaurants = Table.read()
 		.usingOptions(CsvReadOptions.builder(reader, "restaurants"));
 ```
 
-## Importing from a Database
+### Importing from a Database
 
 It's equally easy to create a table from the results of a database query. In this case, you never need to specify the column types, because they are inferred from the database column types. 
 
@@ -190,7 +209,7 @@ try (Statement stmt = conn.createStatement()) {
 }
 ```
 
-## Importing from HTML, JSON, Excel
+### Importing from HTML, JSON, Excel
 
 Tablesaw supports importing data from HTML, JSON, and Excel. See the Javadoc for the [Table.read()](http://static.javadoc.io/tech.tablesaw/tablesaw-core/0.31.0/tech/tablesaw/io/DataFrameReader.html) methods for more info. You will need to add the corresponding optional dependency:
 
@@ -214,3 +233,9 @@ Tablesaw supports importing data from HTML, JSON, and Excel. See the Javadoc for
   <artifactId>tablesaw-excel</artifactId>
 </dependency>
 ```
+
+## Exporting data
+
+
+
+TODO
\ No newline at end of file
diff --git a/docs/userguide/toc.md b/docs/userguide/toc.md
index fc1518ad..66c05a29 100644
--- a/docs/userguide/toc.md
+++ b/docs/userguide/toc.md
@@ -11,7 +11,7 @@ Tablesaw User Guide
     * [Sorting](https://jtablesaw.github.io/tablesaw/userguide/sorting)
     * [Filtering (Queries)](https://jtablesaw.github.io/tablesaw/userguide/filters)
     * [Mapping](https://jtablesaw.github.io/tablesaw/userguide/mapping)
-    * [Summarizing (Aggregation/Reducing)](https://jtablesaw.github.io/tablesaw/userguide/reducing)
+    * [Summarizing (a.k.a. "Aggregation", or "Reducing")](https://jtablesaw.github.io/tablesaw/userguide/reducing)
   * [Working with Rows](https://jtablesaw.github.io/tablesaw/userguide/rows)
 * Plotting and Visualization
   * [Introduction](https://jtablesaw.github.io/tablesaw/userguide/Introduction_to_Plotting)
@@ -21,11 +21,11 @@ Tablesaw User Guide
   * [Simple plotting API: Time Series, Line Charts and Area charts](https://jtablesaw.github.io/tablesaw/userguide/TimeSeries)
   * [Writing Custom Visualizations](https://jtablesaw.github.io/tablesaw/userguide/Visualization_custom)
 * Data Science
-  * Introduction
+  * Introduction (*Not yet available*)
   * [Linear Regression with Smile and Tablesaw: Moneyball Tutorial](https://jtablesaw.github.io/tablesaw/userguide/ml/Moneyball Linear regression)
-  * [Creating cross-tabs](https://jtablesaw.github.io/tablesaw/userguide/crosstabs)
-  * Non-hierarchical clustering (with Smile)
-  * Hierarchical clustering (with Smile)
-  * Classification using Logistic Regression (with Smile)
-  * Classification using Random Forests (with Smile)
-* Using Tablesaw in Jupyter
+  * [Creating cross-tabs](https://jtablesaw.github.io/tablesaw/userguide/crosstabs) 
+  * Non-hierarchical clustering (with Smile) (*Not yet available*)
+  * Hierarchical clustering (with Smile) (*Not yet available*)
+  * Classification using Logistic Regression (with Smile) (*Not yet available*)
+  * Classification using Random Forests (with Smile) (*Not yet available*)
+* Using Tablesaw in Jupyter () (*Not yet available*)


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/api/RowTest.java b/core/src/test/java/tech/tablesaw/api/RowTest.java
index 76c5ec6c..c3c4a9a3 100644
--- a/core/src/test/java/tech/tablesaw/api/RowTest.java
+++ b/core/src/test/java/tech/tablesaw/api/RowTest.java
@@ -2,7 +2,9 @@ package tech.tablesaw.api;
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static tech.tablesaw.api.ColumnType.BOOLEAN;
 import static tech.tablesaw.api.ColumnType.DOUBLE;
 import static tech.tablesaw.api.ColumnType.FLOAT;
@@ -652,4 +654,18 @@ public class RowTest {
             + " Use the method for LOCAL_DATE.",
         thrown.getMessage());
   }
+
+  @Test
+  void testGetNumber() throws IOException {
+    Table table = Table.read().csv("../data/bush.csv");
+    assertEquals(53.0, table.row(0).getNumber("approval"));
+  }
+
+  @Test
+  void testIsMissing() throws IOException {
+    Table table = Table.read().csv("../data/bush.csv");
+    assertFalse(table.row(0).isMissing("approval"));
+    table.row(0).setMissing("approval");
+    assertTrue(table.row(0).isMissing("approval"));
+  }
 }