Repository: jtablesaw/tablesaw
Pull request #268 (2018-04-07 02:45:12)

Table enhancements
Thanks for contributing.

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

Extended Table API. 
-- (re) added the ability to select by row number, or range. 
-- Extended the options for selecting a random sample.
-- added the ability to to remove either rows or columns with missing values

## Testing

Did you add a unit test?
- several


Patch:
diff --git a/core/src/main/java/tech/tablesaw/api/BooleanColumn.java b/core/src/main/java/tech/tablesaw/api/BooleanColumn.java
index 97b2aad5..90818505 100644
--- a/core/src/main/java/tech/tablesaw/api/BooleanColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/BooleanColumn.java
@@ -71,10 +71,15 @@ public class BooleanColumn extends AbstractColumn implements BooleanMapUtils, In
         data = values;
     }
 
-    public static boolean isMissing(byte b) {
+    public static boolean valueIsMissing(byte b) {
         return b == MISSING_VALUE;
     }
 
+    @Override
+    public boolean isMissing(int rowNumber) {
+        return valueIsMissing(getByte(rowNumber));
+    }
+
     public static BooleanColumn create(String name, Selection hits, int columnSize) {
         BooleanColumn column = create(name, columnSize);
 
@@ -183,7 +188,7 @@ public class BooleanColumn extends AbstractColumn implements BooleanMapUtils, In
     public int countMissing() {
         int count = 0;
         for (int i = 0; i < size(); i++) {
-            if (isMissing(getByte(i))) {
+            if (valueIsMissing(getByte(i))) {
                 count++;
             }
         }
@@ -332,6 +337,15 @@ public class BooleanColumn extends AbstractColumn implements BooleanMapUtils, In
         return count;
     }
 
+    /**
+     * Returns the proportion of non-missing row elements that contain true
+     */
+    public double proportionTrue() {
+        double n = size() - countMissing();
+        double trueCount = countTrue();
+        return trueCount / size();
+    }
+
     /**
      * Returns true if the column contains any true values, and false otherwise
      */
@@ -598,5 +612,4 @@ public class BooleanColumn extends AbstractColumn implements BooleanMapUtils, In
             return null;
         }
     }
-
 }
diff --git a/core/src/main/java/tech/tablesaw/api/DateColumn.java b/core/src/main/java/tech/tablesaw/api/DateColumn.java
index 7802b976..e0bfc288 100644
--- a/core/src/main/java/tech/tablesaw/api/DateColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/DateColumn.java
@@ -300,7 +300,7 @@ public class DateColumn extends AbstractColumn implements DateFilters,
      * matches the selection criteria
      * <p>
      * Example:
-     * myColumn.set(LocalDate.now(), myColumn.isMissing()); // no more missing values
+     * myColumn.set(LocalDate.now(), myColumn.valueIsMissing()); // no more missing values
      */
     public void set(LocalDate newValue, Selection rowSelection) {
         for (int row : rowSelection) {
@@ -414,7 +414,7 @@ public class DateColumn extends AbstractColumn implements DateFilters,
         return table;
     }
 
-    public static boolean isMissing(int i) {
+    public static boolean valueIsMissing(int i) {
         return i == MISSING_VALUE;
     }
 
@@ -580,6 +580,11 @@ public class DateColumn extends AbstractColumn implements DateFilters,
         return doubles;
     }
 
+    @Override
+    public boolean isMissing(int rowNumber) {
+        return valueIsMissing(getIntInternal(rowNumber));
+    }
+
     @Override
     public double getDouble(int i) {
         return getIntInternal(i);
diff --git a/core/src/main/java/tech/tablesaw/api/DateTimeColumn.java b/core/src/main/java/tech/tablesaw/api/DateTimeColumn.java
index da402659..ae80b00e 100644
--- a/core/src/main/java/tech/tablesaw/api/DateTimeColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/DateTimeColumn.java
@@ -76,10 +76,15 @@ public class DateTimeColumn extends AbstractColumn
 
     private Locale locale;
 
-    public static boolean isMissing(long value) {
+    public static boolean valueIsMissing(long value) {
         return MISSING_VALUE == value;
     }
 
+    @Override
+    public boolean isMissing(int rowNumber) {
+        return valueIsMissing(getLongInternal(rowNumber));
+    }
+
     public static DateTimeColumn create(String name) {
         return create(name, DEFAULT_ARRAY_SIZE);
     }
@@ -331,7 +336,7 @@ public class DateTimeColumn extends AbstractColumn
         TimeColumn newColumn = TimeColumn.create(this.name() + " time");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getPackedDateTime(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(TimeColumn.MISSING_VALUE);
             } else {
                 newColumn.appendInternal(PackedLocalDateTime.time(c1));
@@ -347,7 +352,7 @@ public class DateTimeColumn extends AbstractColumn
         DateColumn newColumn = DateColumn.create(this.name() + " date");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getPackedDateTime(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(DateColumn.MISSING_VALUE);
             } else {
                 newColumn.appendInternal(PackedLocalDateTime.date(c1));
@@ -360,7 +365,7 @@ public class DateTimeColumn extends AbstractColumn
         NumberColumn newColumn = NumberColumn.create(this.name() + " month");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getPackedDateTime(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append((short) PackedLocalDateTime.getMonthValue(c1));
@@ -373,7 +378,7 @@ public class DateTimeColumn extends AbstractColumn
         NumberColumn newColumn = NumberColumn.create(this.name() + " year");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getPackedDateTime(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append(PackedLocalDate.getYear(PackedLocalDateTime.date(c1)));
@@ -387,7 +392,7 @@ public class DateTimeColumn extends AbstractColumn
      * matches the selection criteria
      * <p>
      * Example:
-     * myColumn.set(LocalDateTime.now(), myColumn.isMissing()); // no more missing values
+     * myColumn.set(LocalDateTime.now(), myColumn.valueIsMissing()); // no more missing values
      */
     public void set(LocalDateTime newValue, Selection rowSelection) {
         for (int row : rowSelection) {
diff --git a/core/src/main/java/tech/tablesaw/api/NumberColumn.java b/core/src/main/java/tech/tablesaw/api/NumberColumn.java
index ae6ce246..84743157 100644
--- a/core/src/main/java/tech/tablesaw/api/NumberColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/NumberColumn.java
@@ -83,10 +83,15 @@ public class NumberColumn extends AbstractColumn implements DoubleIterable, IntC
         }
     };
 
-    public static boolean isMissing(double value) {
+    public static boolean valueIsMissing(double value) {
         return Double.isNaN(value);
     }
 
+    @Override
+    public boolean isMissing(int rowNumber) {
+        return valueIsMissing(get(rowNumber));
+    }
+
     public void setPrintFormatter(NumberFormat format, String missingValueString) {
         this.printFormatter = new NumberColumnFormatter(format, missingValueString);
     }
@@ -267,7 +272,7 @@ public class NumberColumn extends AbstractColumn implements DoubleIterable, IntC
     @Override
     public String getString(int row) {
         double value = data.getDouble(row);
-        if (isMissing(value)) {
+        if (valueIsMissing(value)) {
             return "";
         }
         return String.valueOf(printFormatter.format(value));
@@ -363,7 +368,7 @@ public class NumberColumn extends AbstractColumn implements DoubleIterable, IntC
      */
     public Integer roundInt(int i) {
         double value = get(i);
-        if (isMissing(value)) {
+        if (valueIsMissing(value)) {
             return null;
         }
         return (int) Math.round(get(i));
@@ -377,7 +382,7 @@ public class NumberColumn extends AbstractColumn implements DoubleIterable, IntC
      */
     public long getLong(int i) {
         double value = data.getDouble(i);
-        return isMissing(value) ? DateTimeColumn.MISSING_VALUE : Math.round(value);
+        return valueIsMissing(value) ? DateTimeColumn.MISSING_VALUE : Math.round(value);
     }
 
     /**
@@ -402,7 +407,7 @@ public class NumberColumn extends AbstractColumn implements DoubleIterable, IntC
      * matches the selection criteria
      * <p>
      * Example:
-     * myColumn.set(4.0, myColumn.isMissing()); // no more missing values
+     * myColumn.set(4.0, myColumn.valueIsMissing()); // no more missing values
      */
     public void set(Selection rowSelection, double newValue) {
         for (int row : rowSelection) {
@@ -538,7 +543,7 @@ public class NumberColumn extends AbstractColumn implements DoubleIterable, IntC
     public IntSet asIntegerSet() {
         IntSet ints = new IntOpenHashSet();
         for (double d : this) {
-            if (!isMissing(d)) {
+            if (!valueIsMissing(d)) {
                 ints.add((int) Math.round(d));
             }
         }
diff --git a/core/src/main/java/tech/tablesaw/api/StringColumn.java b/core/src/main/java/tech/tablesaw/api/StringColumn.java
index 8c365b35..bb96d54f 100644
--- a/core/src/main/java/tech/tablesaw/api/StringColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/StringColumn.java
@@ -132,6 +132,11 @@ public class StringColumn extends AbstractColumn
         return stringValue;
     }
 
+    @Override
+    public boolean isMissing(int rowNumber) {
+        return get(rowNumber).equals(MISSING_VALUE);
+    }
+
     @Override
     public ColumnType type() {
         return STRING;
@@ -297,7 +302,7 @@ public class StringColumn extends AbstractColumn
      * <p>
      * Examples:
      * myCatColumn.set("Dog", myCatColumn.isEqualTo("Cat")); // no more cats
-     * myCatColumn.set("Fox", myCatColumn.isMissing()); // no more missing values
+     * myCatColumn.set("Fox", myCatColumn.valueIsMissing()); // no more missing values
      */
     public void set(String newValue, Selection rowSelection) {
         for (int row : rowSelection) {
diff --git a/core/src/main/java/tech/tablesaw/api/Table.java b/core/src/main/java/tech/tablesaw/api/Table.java
index 23be81ae..a056e958 100644
--- a/core/src/main/java/tech/tablesaw/api/Table.java
+++ b/core/src/main/java/tech/tablesaw/api/Table.java
@@ -16,6 +16,7 @@ package tech.tablesaw.api;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.primitives.Ints;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntArrays;
 import it.unimi.dsi.fastutil.ints.IntComparator;
@@ -465,16 +466,32 @@ public class Table extends Relation implements IntIterable {
      *
      * @param proportion The proportion to go in the sample
      */
-    public Table sample(double proportion) {
+    public Table selectSample(double proportion) {
+        Preconditions.checkArgument(proportion <= 1 && proportion >= 0,
+                "The sample proportion must be between 0 and 1");
 
-        int tableCount = (int) Math.round(rowCount() * proportion);
+        int tableSize = (int) Math.round(rowCount() * proportion);
+        return selectWhere(selectNRows(tableSize));
+    }
+
+    /**
+     * Returns a table consisting of randomly selected records from this table
+     *
+     * @param nRows The number of rows to go in the sample
+     */
+    public Table selectSample(int nRows) {
+        Preconditions.checkArgument(nRows > 0 && nRows < rowCount(),
+                "The number of rows sampled must be greater than 0 and less than the number of rows in the table.");
+        return selectWhere(selectNRows(nRows));
+    }
 
+    private Selection selectNRows(int tableCount) {
         Selection table1Selection = new BitmapBackedSelection();
         int[] selectedRecords = generateUniformBitmap(tableCount, rowCount());
         for (int selectedRecord : selectedRecords) {
             table1Selection.add(selectedRecord);
         }
-        return selectWhere(table1Selection);
+        return table1Selection;
     }
 
     /**
@@ -734,6 +751,28 @@ public class Table extends Relation implements IntIterable {
         }
     }
 
+    public Table selectRows(int... rowNumbers) {
+        Preconditions.checkArgument(Ints.max(rowNumbers) <= rowCount());
+        return selectWhere(Selection.with(rowNumbers));
+    }
+
+    public Table rejectRows(int... rowNumbers) {
+        Preconditions.checkArgument(Ints.max(rowNumbers) <= rowCount());
+        Selection selection = Selection.withRange(0, rowCount())
+                .andNot(Selection.with(rowNumbers));
+        return selectWhere(selection);
+    }
+
+    public Table selectRange(int rowStart, int rowEnd) {
+        Preconditions.checkArgument(rowEnd <= rowCount());
+        return selectWhere(Selection.withRange(rowStart, rowEnd));
+    }
+
+    public Table rejectRange(int rowStart, int rowEnd) {
+        Preconditions.checkArgument(rowEnd <= rowCount());
+        return selectWhere(Selection.withoutRange(0, rowCount(), rowStart, rowEnd));
+    }
+
     public Table selectWhere(Selection selection) {
         Table newTable = this.emptyCopy(selection.size());
         Rows.copyRowsToTable(selection, this, newTable);
@@ -796,7 +835,7 @@ public class Table extends Relation implements IntIterable {
      * Returns the unique records in this table
      * Note: Uses a lot of memory for a sort
      */
-    public Table uniqueRecords() {
+    public Table rejectDuplicateRows() {
 
         Table sorted = this.sortOn(columnNames().toArray(new String[columns().size()]));
         Table temp = emptyCopy();
@@ -809,6 +848,28 @@ public class Table extends Relation implements IntIterable {
         return temp;
     }
 
+    /**
+     * Returns only those records in this table that have no columns with missing values
+     */
+    public Table rejectRowsWithMissingValues() {
+
+        Table temp = emptyCopy();
+        for (int row = 0; row < rowCount(); row++) {
+            boolean add = true;
+            for (int col = 0; col < columnCount(); col++) {
+                Column c = column(col);
+                if (c.isMissing(row)) {
+                    add = false;
+                    break;
+                }
+            }
+            if (add) {
+                Rows.appendRowToTable(row, this, temp);
+            }
+        }
+        return temp;
+    }
+
     public Projection project(String... columnName) {
         return new Projection(this, columnName);
     }
@@ -822,6 +883,14 @@ public class Table extends Relation implements IntIterable {
         return this;
     }
 
+    /**
+     * Removes the given columns with missing values
+     */
+    public Table removeColumnsWithMissingValues() {
+        removeColumns(columnList.stream().filter(x -> x.countMissing() > 0).toArray(Column[]::new));
+        return this;
+    }
+
     /**
      * Removes all columns except for those given in the argument from this table
      */
diff --git a/core/src/main/java/tech/tablesaw/api/TimeColumn.java b/core/src/main/java/tech/tablesaw/api/TimeColumn.java
index 87f24926..65eb68a2 100644
--- a/core/src/main/java/tech/tablesaw/api/TimeColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/TimeColumn.java
@@ -82,7 +82,7 @@ public class TimeColumn extends AbstractColumn implements Iterable<LocalTime>, T
         this.locale = locale;
     }
 
-    public static boolean isMissing(int i) {
+    public static boolean valueIsMissing(int i) {
         return i == MISSING_VALUE;
     }
 
@@ -132,6 +132,12 @@ public class TimeColumn extends AbstractColumn implements Iterable<LocalTime>, T
         return copy;
     }
 
+
+    @Override
+    public boolean isMissing(int rowNumber) {
+        return valueIsMissing(getIntInternal(rowNumber));
+    }
+
     public int size() {
         return data.size();
     }
@@ -475,7 +481,7 @@ public class TimeColumn extends AbstractColumn implements Iterable<LocalTime>, T
      * matches the selection criteria
      * <p>
      * Example:
-     * myColumn.set(LocalTime.now(), myColumn.isMissing()); // no more missing values
+     * myColumn.set(LocalTime.now(), myColumn.valueIsMissing()); // no more missing values
      */
     public void set(LocalTime newValue, Selection rowSelection) {
         for (int row : rowSelection) {
diff --git a/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java b/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java
index 2b0b9f63..5772eebc 100644
--- a/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java
+++ b/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java
@@ -78,6 +78,4 @@ public abstract class AbstractColumn implements Column {
         }
         return builder.toString();
     }
-
-
 }
\ No newline at end of file
diff --git a/core/src/main/java/tech/tablesaw/columns/Column.java b/core/src/main/java/tech/tablesaw/columns/Column.java
index b87d296d..5e0183fa 100644
--- a/core/src/main/java/tech/tablesaw/columns/Column.java
+++ b/core/src/main/java/tech/tablesaw/columns/Column.java
@@ -219,4 +219,6 @@ public interface Column {
     }
 
     String getUnformattedString(int r);
+
+    boolean isMissing(int rowNumber);
 }
diff --git a/core/src/main/java/tech/tablesaw/columns/dates/DateMapUtils.java b/core/src/main/java/tech/tablesaw/columns/dates/DateMapUtils.java
index 453fc19c..3dd210c9 100644
--- a/core/src/main/java/tech/tablesaw/columns/dates/DateMapUtils.java
+++ b/core/src/main/java/tech/tablesaw/columns/dates/DateMapUtils.java
@@ -62,7 +62,7 @@ public interface DateMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " day of month");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append(PackedLocalDate.getDayOfMonth(c1));
@@ -75,7 +75,7 @@ public interface DateMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " day of year");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append((short) PackedLocalDate.getDayOfYear(c1));
@@ -89,7 +89,7 @@ public interface DateMapUtils extends Column {
 
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append(PackedLocalDate.getMonthValue(c1));
@@ -103,7 +103,7 @@ public interface DateMapUtils extends Column {
 
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 newColumn.append(PackedLocalDate.getMonth(c1).name());
@@ -116,7 +116,7 @@ public interface DateMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " year");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append(PackedLocalDate.getYear(c1));
@@ -137,7 +137,7 @@ public interface DateMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & quarter");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String yq = String.valueOf(PackedLocalDate.getYear(c1));
@@ -161,7 +161,7 @@ public interface DateMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & month");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String ym = String.valueOf(PackedLocalDate.getYear(c1));
@@ -185,7 +185,7 @@ public interface DateMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & month");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String ym = String.valueOf(PackedLocalDate.getYear(c1));
@@ -209,7 +209,7 @@ public interface DateMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & month");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String ym = String.valueOf(PackedLocalDate.getYear(c1));
@@ -225,7 +225,7 @@ public interface DateMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " day of week", this.size());
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.set(r, NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append((short) PackedLocalDate.getDayOfWeek(c1).getValue());
@@ -238,7 +238,7 @@ public interface DateMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " day of week");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 newColumn.append(PackedLocalDate.getDayOfWeek(c1).toString());
@@ -259,7 +259,7 @@ public interface DateMapUtils extends Column {
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
             int c2 = end.getIntInternal(r);
-            if (DateColumn.isMissing(c1) || DateColumn.isMissing(c2)) {
+            if (DateColumn.valueIsMissing(c1) || DateColumn.valueIsMissing(c2)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 switch (unit) {
@@ -407,7 +407,7 @@ public interface DateMapUtils extends Column {
         DateTimeColumn newColumn = DateTimeColumn.create(this.name() + " " + time.toString());
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (DateColumn.isMissing(c1)) {
+            if (DateColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(DateTimeColumn.MISSING_VALUE);
             } else {
                 LocalDate value1 = PackedLocalDate.asLocalDate(c1);
@@ -426,7 +426,7 @@ public interface DateMapUtils extends Column {
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
             int c2 = timeColumn.getIntInternal(r);
-            if (DateColumn.isMissing(c1) || DateColumn.isMissing(c2)) {
+            if (DateColumn.valueIsMissing(c1) || DateColumn.valueIsMissing(c2)) {
                 newColumn.appendInternal(DateTimeColumn.MISSING_VALUE);
             } else {
                 newColumn.appendInternal(PackedLocalDateTime.create(c1, c2));
diff --git a/core/src/main/java/tech/tablesaw/columns/datetimes/DateTimeMapUtils.java b/core/src/main/java/tech/tablesaw/columns/datetimes/DateTimeMapUtils.java
index 55d1badd..aae32171 100644
--- a/core/src/main/java/tech/tablesaw/columns/datetimes/DateTimeMapUtils.java
+++ b/core/src/main/java/tech/tablesaw/columns/datetimes/DateTimeMapUtils.java
@@ -121,7 +121,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " month");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 newColumn.append(Month.of(getMonthValue(c1)).name());
@@ -142,7 +142,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & quarter");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String yq = String.valueOf(getYear(c1));
@@ -166,7 +166,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & month");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String ym = String.valueOf(getYear(c1));
@@ -190,7 +190,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & month");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String ym = String.valueOf(getYear(c1));
@@ -214,7 +214,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " hour & minute");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String hm = Strings.padStart(String.valueOf(getHour(c1)), 2, '0');
@@ -238,7 +238,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " year & month");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String ym = String.valueOf(getYear(c1));
@@ -254,7 +254,7 @@ public interface DateTimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " day of week", this.size());
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 newColumn.append(getDayOfWeek(c1).toString());
@@ -267,7 +267,7 @@ public interface DateTimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " day of week", this.size());
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append((short) getDayOfWeek(c1).getValue());
@@ -280,7 +280,7 @@ public interface DateTimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " day of year", this.size());
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append((short) getDayOfYear(c1));
@@ -293,7 +293,7 @@ public interface DateTimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(this.name() + " day of month");
         for (int r = 0; r < this.size(); r++) {
             long c1 = this.getLongInternal(r);
-            if (DateTimeColumn.isMissing(c1)) {
+            if (DateTimeColumn.valueIsMissing(c1)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append(getDayOfMonth(c1));
diff --git a/core/src/main/java/tech/tablesaw/columns/numbers/NumberColumnFormatter.java b/core/src/main/java/tech/tablesaw/columns/numbers/NumberColumnFormatter.java
index 07535894..0fabe31d 100644
--- a/core/src/main/java/tech/tablesaw/columns/numbers/NumberColumnFormatter.java
+++ b/core/src/main/java/tech/tablesaw/columns/numbers/NumberColumnFormatter.java
@@ -64,7 +64,7 @@ public class NumberColumnFormatter {
 
     public String format(double value) {
 
-        if (NumberColumn.isMissing(value)) {
+        if (NumberColumn.valueIsMissing(value)) {
             return missingString;
         }
         if (format == null) {
diff --git a/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapUtils.java b/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapUtils.java
index 97180d0c..aba71ff2 100644
--- a/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapUtils.java
+++ b/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapUtils.java
@@ -156,21 +156,21 @@ public interface NumberMapUtils extends Column, NumberReduceUtils, DoubleIterabl
     }
 
     default double add(double val1, double val2) {
-        if (NumberColumn.isMissing(val1) || NumberColumn.isMissing(val2)) {
+        if (NumberColumn.valueIsMissing(val1) || NumberColumn.valueIsMissing(val2)) {
             return MISSING_VALUE;
         }
         return val1 + val2;
     }
 
     default double multiply(double val1, double val2) {
-        if (NumberColumn.isMissing(val1) || NumberColumn.isMissing(val2)) {
+        if (NumberColumn.valueIsMissing(val1) || NumberColumn.valueIsMissing(val2)) {
             return MISSING_VALUE;
         }
         return val1 * val2;
     }
 
     default double divide(double val1, double val2) {
-        if (NumberColumn.isMissing(val1) || NumberColumn.isMissing(val2)) {
+        if (NumberColumn.valueIsMissing(val1) || NumberColumn.valueIsMissing(val2)) {
             return MISSING_VALUE;
         }
         return val1 / val2;
@@ -180,7 +180,7 @@ public interface NumberMapUtils extends Column, NumberReduceUtils, DoubleIterabl
      * Returns the result of subtracting val2 from val1, after handling missing values
      */
     default double subtract(double val1, double val2) {
-        if (NumberColumn.isMissing(val1) || NumberColumn.isMissing(val2)) {
+        if (NumberColumn.valueIsMissing(val1) || NumberColumn.valueIsMissing(val2)) {
             return MISSING_VALUE;
         }
         return val1 - val2;
@@ -227,7 +227,7 @@ public interface NumberMapUtils extends Column, NumberReduceUtils, DoubleIterabl
         for (int r = 0; r < size(); r++) {
             double val1 = get(r);
             double val2 = column2.get(r);
-            if (NumberColumn.isMissing(val1) || NumberColumn.isMissing(val2)) {
+            if (NumberColumn.valueIsMissing(val1) || NumberColumn.valueIsMissing(val2)) {
                 result.append(MISSING_VALUE);
             } else {
                 result.append(get(r) % column2.get(r));
@@ -339,7 +339,7 @@ public interface NumberMapUtils extends Column, NumberReduceUtils, DoubleIterabl
         double total = 0.0;
         NumberColumn newColumn = create(name() + "[cumSum]", size());
         for (double value : this) {
-            if (NumberColumn.isMissing(value)) {
+            if (NumberColumn.valueIsMissing(value)) {
                 newColumn.append(MISSING_VALUE);
             } else {
                 total += value;
@@ -356,7 +356,7 @@ public interface NumberMapUtils extends Column, NumberReduceUtils, DoubleIterabl
         double total = 1.0;
         NumberColumn newColumn = create(name() + "[cumProd]", size());
         for (double value : this) {
-            if (NumberColumn.isMissing(value)) {
+            if (NumberColumn.valueIsMissing(value)) {
                 newColumn.append(MISSING_VALUE);
             } else {
                 total *= value;
diff --git a/core/src/main/java/tech/tablesaw/columns/numbers/NumberReduceUtils.java b/core/src/main/java/tech/tablesaw/columns/numbers/NumberReduceUtils.java
index 881f8ef5..d794f7e1 100644
--- a/core/src/main/java/tech/tablesaw/columns/numbers/NumberReduceUtils.java
+++ b/core/src/main/java/tech/tablesaw/columns/numbers/NumberReduceUtils.java
@@ -21,7 +21,7 @@ public interface NumberReduceUtils extends Column {
     default int countUnique() {
         DoubleSet doubles = new DoubleOpenHashSet();
         for (int i = 0; i < size(); i++) {
-            if (!NumberColumn.isMissing(get(i))) {
+            if (!NumberColumn.valueIsMissing(get(i))) {
                 doubles.add(get(i));
             }
         }
@@ -151,7 +151,7 @@ public interface NumberReduceUtils extends Column {
     default int countMissing() {
         int count = 0;
         for (int i = 0; i < size(); i++) {
-            if (NumberColumn.isMissing(get(i))) {
+            if (NumberColumn.valueIsMissing(get(i))) {
                 count++;
             }
         }
diff --git a/core/src/main/java/tech/tablesaw/columns/times/TimeMapUtils.java b/core/src/main/java/tech/tablesaw/columns/times/TimeMapUtils.java
index be021438..f3145217 100644
--- a/core/src/main/java/tech/tablesaw/columns/times/TimeMapUtils.java
+++ b/core/src/main/java/tech/tablesaw/columns/times/TimeMapUtils.java
@@ -61,7 +61,7 @@ public interface TimeMapUtils extends Column {
         for (int r = 0; r < size(); r++) {
             int c1 = this.getIntInternal(r);
             int c2 = column2.getIntInternal(r);
-            if (TimeColumn.isMissing(c1) || TimeColumn.isMissing(c2)) {
+            if (TimeColumn.valueIsMissing(c1) || TimeColumn.valueIsMissing(c2)) {
                 newColumn.append(NumberColumn.MISSING_VALUE);
             } else {
                 newColumn.append((int) difference(c1, c2, unit));
@@ -75,7 +75,7 @@ public interface TimeMapUtils extends Column {
         String timeUnitString = "";
         for (int r = 0; r < size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (TimeColumn.isMissing(c1)) {
+            if (TimeColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(MISSING_VALUE);
             } else {
                 switch (unit) {
@@ -110,7 +110,7 @@ public interface TimeMapUtils extends Column {
         String timeUnitString = "";
         for (int r = 0; r < size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (TimeColumn.isMissing(c1)) {
+            if (TimeColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(MISSING_VALUE);
             } else {
                 switch (unit) {
@@ -144,7 +144,7 @@ public interface TimeMapUtils extends Column {
         String timeUnitString = "";
         for (int r = 0; r < size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (TimeColumn.isMissing(c1)) {
+            if (TimeColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(MISSING_VALUE);
             } else {
                 switch (unit) {
@@ -231,7 +231,7 @@ public interface TimeMapUtils extends Column {
         TimeColumn newColumn = TimeColumn.create("");
         for (int r = 0; r < size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (TimeColumn.isMissing(c1)) {
+            if (TimeColumn.valueIsMissing(c1)) {
                 newColumn.appendInternal(MISSING_VALUE);
             } else {
                 newColumn.appendInternal(PackedLocalTime.truncatedTo(unit, c1));
@@ -244,7 +244,7 @@ public interface TimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(name() + "[" + "hour" + "]");
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
-            if (!TimeColumn.isMissing(c1)) {
+            if (!TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(PackedLocalTime.getHour(c1));
             } else {
                 newColumn.append(NumberColumn.MISSING_VALUE);
@@ -257,7 +257,7 @@ public interface TimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(name() + "[" + "minute" + "]");
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
-            if (!TimeColumn.isMissing(c1)) {
+            if (!TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(PackedLocalTime.getMinute(c1));
             } else {
                 newColumn.append(NumberColumn.MISSING_VALUE);
@@ -270,7 +270,7 @@ public interface TimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(name() + "[" + "second" + "]");
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
-            if (!TimeColumn.isMissing(c1)) {
+            if (!TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(PackedLocalTime.getSecond(c1));
             } else {
                 newColumn.append(NumberColumn.MISSING_VALUE);
@@ -283,7 +283,7 @@ public interface TimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(name() + "[" + "ms" + "]");
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
-            if (!TimeColumn.isMissing(c1)) {
+            if (!TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(PackedLocalTime.getMilliseconds(c1));
             } else {
                 newColumn.append(NumberColumn.MISSING_VALUE);
@@ -296,7 +296,7 @@ public interface TimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(name() + "[" + "minute-of-day" + "]");
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
-            if (!TimeColumn.isMissing(c1)) {
+            if (!TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(PackedLocalTime.getMinuteOfDay(c1));
             } else {
                 newColumn.append(NumberColumn.MISSING_VALUE);
@@ -309,7 +309,7 @@ public interface TimeMapUtils extends Column {
         NumberColumn newColumn = NumberColumn.create(name() + "[" + "second-of-day" + "]");
         for (int r = 0; r < size(); r++) {
             int c1 = getIntInternal(r);
-            if (!TimeColumn.isMissing(c1)) {
+            if (!TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(PackedLocalTime.getSecondOfDay(c1));
             } else {
                 newColumn.append(NumberColumn.MISSING_VALUE);
@@ -365,7 +365,7 @@ public interface TimeMapUtils extends Column {
         StringColumn newColumn = StringColumn.create(this.name() + " hour & minute");
         for (int r = 0; r < this.size(); r++) {
             int c1 = this.getIntInternal(r);
-            if (TimeColumn.isMissing(c1)) {
+            if (TimeColumn.valueIsMissing(c1)) {
                 newColumn.append(StringColumn.MISSING_VALUE);
             } else {
                 String hm = Strings.padStart(String.valueOf(PackedLocalTime.getHour(c1)), 2, '0');


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/TableFilteringTest.java b/core/src/test/java/tech/tablesaw/TableFilteringTest.java
index 572bcb82..73c7746a 100644
--- a/core/src/test/java/tech/tablesaw/TableFilteringTest.java
+++ b/core/src/test/java/tech/tablesaw/TableFilteringTest.java
@@ -19,7 +19,9 @@ import org.junit.Test;
 import tech.tablesaw.api.DateColumn;
 import tech.tablesaw.api.NumberColumn;
 import tech.tablesaw.api.QueryHelper;
+import tech.tablesaw.api.StringColumn;
 import tech.tablesaw.api.Table;
+import tech.tablesaw.columns.Column;
 import tech.tablesaw.columns.dates.PackedLocalDate;
 import tech.tablesaw.io.csv.CsvReadOptions;
 
@@ -71,6 +73,74 @@ public class TableFilteringTest {
         }
     }
 
+    @Test
+    public void testRejectWithMissingValues() {
+
+        String[] values = {"a", "b", "", "d"};
+        double[] values2 = {1, Double.NaN, 3, 4};
+        StringColumn sc = StringColumn.create("s", values);
+        NumberColumn nc = NumberColumn.create("n", values2);
+        Table test = Table.create("test", sc, nc);
+        Table result = test.rejectRowsWithMissingValues();
+        assertEquals(2, result.rowCount());
+        assertEquals("a", result.stringColumn("s").get(0));
+        assertEquals("d", result.stringColumn("s").get(1));
+    }
+
+    @Test
+    public void testSelectRange() {
+        Table result = table.selectRange(20, 30);
+        assertEquals(10, result.rowCount());
+        for (Column c: result.columns()) {
+            for (int r = 0; r < result.rowCount(); r++) {
+                assertEquals(table.get(r+20, c.name()), result.get(r, c.name()));
+            }
+        }
+    }
+
+    @Test
+    public void testSelectRows() {
+        Table result = table.selectRows(20, 30);
+        assertEquals(2, result.rowCount());
+        for (Column c: result.columns()) {
+            assertEquals(table.get(20, c.name()), result.get(0, c.name()));
+            assertEquals(table.get(30, c.name()), result.get(1, c.name()));
+        }
+    }
+
+    @Test
+    public void testSampleRows() {
+        Table result = table.selectSample(20);
+        assertEquals(20, result.rowCount());
+    }
+
+    @Test
+    public void testSampleProportion() {
+        Table result = table.selectSample(.1);
+        assertEquals(32, result.rowCount());
+    }
+
+    @Test
+    public void testRejectRows() {
+        Table result = table.rejectRows(20, 30);
+        assertEquals(table.rowCount() - 2, result.rowCount());
+        for (Column c: result.columns()) {
+            assertEquals(table.get(21, c.name()), result.get(20, c.name()));
+            assertEquals(table.get(32, c.name()), result.get(30, c.name()));
+        }
+    }
+
+    @Test
+    public void testRejectRange() {
+        Table result = table.rejectRange(20, 30);
+        assertEquals(table.rowCount() - 10, result.rowCount());
+        for (Column c: result.columns()) {
+            for (int r = 30; r < result.rowCount(); r++) {
+                assertEquals(result.get(r, c.name()), table.get(r + 10, c.name()));
+            }
+        }
+    }
+
     @Test
     public void testFilter2() {
         Table result = table.selectWhere(dateColumn("date").isInApril());