Repository: jtablesaw/tablesaw
Pull request #270 (2018-04-08 16:58:58)

restored some summarization functionality
Removed ability to select multiple columns for summarization during refactoring. This restores a variation on the original.

Thanks for contributing.

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)




Patch:
diff --git a/core/src/main/java/tech/tablesaw/aggregate/Summarizer.java b/core/src/main/java/tech/tablesaw/aggregate/Summarizer.java
new file mode 100644
index 00000000..10d0f331
--- /dev/null
+++ b/core/src/main/java/tech/tablesaw/aggregate/Summarizer.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tech.tablesaw.aggregate;
+
+import tech.tablesaw.api.CategoricalColumn;
+import tech.tablesaw.api.DoubleColumn;
+import tech.tablesaw.api.NumberColumn;
+import tech.tablesaw.api.Table;
+import tech.tablesaw.columns.Column;
+import tech.tablesaw.table.SelectionTableSliceGroup;
+import tech.tablesaw.table.StandardTableSliceGroup;
+import tech.tablesaw.table.TableSliceGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Summarizes the data in a table, by applying functions to a subset of its columns.
+ *
+ * How to use:
+ *
+ * 1. Create an instance providing a source table, the column or columns to summarize, and a function or functions to apply
+ * 2. Applying the functions to the designated columns, possibly creating subgroup summaries using one of the by() methods
+ */
+public class Summarizer {
+
+    private final Table original;
+    private final List<String> summarizedColumns = new ArrayList<>();
+    private final AggregateFunction[] functions;
+
+    /**
+     * Returns an object capable of summarizing the given numericColumn in the given sourceTable,
+     * by applying the given functions
+     */
+    public Summarizer(Table sourceTable, NumberColumn numericColumn, AggregateFunction... functions) {
+        this.original = sourceTable;
+        summarizedColumns.add(numericColumn.name());
+        this.functions = functions;
+    }
+
+    /**
+     * Returns an object capable of summarizing the given numericColumn in the given sourceTable,
+     * by applying the given functions
+     */
+    public Summarizer(Table sourceTable, NumberColumn numericColumn1, NumberColumn numericColumn2, AggregateFunction... functions) {
+        this.original = sourceTable;
+        summarizedColumns.add(numericColumn1.name());
+        summarizedColumns.add(numericColumn2.name());
+        this.functions = functions;
+    }
+
+    /**
+     * Returns an object capable of summarizing the given numericColumn in the given sourceTable,
+     * by applying the given functions
+     */
+    public Summarizer(Table sourceTable,
+                      NumberColumn numericColumn1,
+                      NumberColumn numericColumn2,
+                      NumberColumn numericColumn3,
+                      NumberColumn numericColumn4,
+                      AggregateFunction... functions) {
+        this.original = sourceTable;
+        summarizedColumns.add(numericColumn1.name());
+        summarizedColumns.add(numericColumn2.name());
+        summarizedColumns.add(numericColumn3.name());
+        summarizedColumns.add(numericColumn4.name());
+        this.functions = functions;
+    }
+
+    /**
+     * Returns an object capable of summarizing the given numericColumn in the given sourceTable,
+     * by applying the given functions
+     */
+    public Summarizer(Table sourceTable,
+                      NumberColumn numericColumn1,
+                      NumberColumn numericColumn2,
+                      NumberColumn numericColumn3,
+                      AggregateFunction... functions) {
+        this.original = sourceTable;
+        summarizedColumns.add(numericColumn1.name());
+        summarizedColumns.add(numericColumn2.name());
+        summarizedColumns.add(numericColumn3.name());
+        this.functions = functions;
+    }
+
+    public Table by(String... columnNames) {
+        TableSliceGroup group = StandardTableSliceGroup.create(original, columnNames);
+        return summarize(group);
+    }
+
+    public Table by(CategoricalColumn... columns) {
+        TableSliceGroup group = StandardTableSliceGroup.create(original, columns);
+        return summarize(group);
+    }
+
+    public Table by(String groupNameTemplate, int step) {
+        TableSliceGroup group = SelectionTableSliceGroup.create(original, groupNameTemplate, step);
+        return summarize(group);
+    }
+
+    /**
+     * Returns the result of applying to the functions to all the values in the appropriate column
+     */
+    public Table apply() {
+        Table table = TableSliceGroup.summaryTableName(original);
+        for (String columnName : summarizedColumns) {
+            for (AggregateFunction function : functions) {
+                NumberColumn column = original.numberColumn(columnName);
+                double result = function.agg(column);
+                Column newColumn = DoubleColumn.create(TableSliceGroup.aggregateColumnName(columnName, function.functionName()));
+                ((DoubleColumn) newColumn).append(result);
+                table.addColumn(newColumn);
+            }
+        }
+        return table;
+    }
+
+    private Table summarize(TableSliceGroup group) {
+        List<Table> results = new ArrayList<>();
+        for (String name : summarizedColumns) {
+            results.add(group.aggregate(name, functions));
+        }
+        return combineTables(results);
+    }
+
+    private Table combineTables(List<Table> tables) {
+        if (tables.size() == 1) {
+            return tables.get(0);
+        }
+
+        Table result = null;
+        for (Table table : tables) {
+            if (result == null) {
+                result = table;
+            } else {
+                for (Column column : table.columns()) {
+                    if (!result.columnNames().contains(column.name())) {
+                        result.addColumn(column);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+}
diff --git a/core/src/main/java/tech/tablesaw/aggregate/SummaryFunction.java b/core/src/main/java/tech/tablesaw/aggregate/SummaryFunction.java
deleted file mode 100644
index 77f07449..00000000
--- a/core/src/main/java/tech/tablesaw/aggregate/SummaryFunction.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package tech.tablesaw.aggregate;
-
-import tech.tablesaw.api.CategoricalColumn;
-import tech.tablesaw.api.NumberColumn;
-import tech.tablesaw.api.Table;
-import tech.tablesaw.table.SelectionTableSliceGroup;
-import tech.tablesaw.table.StandardTableSliceGroup;
-import tech.tablesaw.table.TableSliceGroup;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class SummaryFunction {
-
-    private final Table original;
-    private final String summarizedColumnName;
-    private final AggregateFunction[] function;
-
-    public SummaryFunction(Table original, String summarizedColumnName, AggregateFunction... function) {
-        this.original = original;
-        this.summarizedColumnName = summarizedColumnName;
-        this.function = function;
-    }
-
-    public SummaryFunction(Table original, NumberColumn numberColumn, AggregateFunction... function) {
-        this.original = original;
-        this.summarizedColumnName = numberColumn.name();
-        this.function = function;
-    }
-
-    private String summarizedColumnName() {
-        return summarizedColumnName;
-    }
-
-    private Table original() {
-        return original;
-    }
-
-    public Table by(String... columnNames) {
-        TableSliceGroup group = StandardTableSliceGroup.create(original(), columnNames);
-        return group.aggregate(summarizedColumnName(), function);
-    }
-
-    public Table by(CategoricalColumn... columns) {
-        TableSliceGroup group = StandardTableSliceGroup.create(original(), columns);
-        return group.aggregate(summarizedColumnName(), function);
-    }
-
-    public Table by(String groupNameTemplate, int step) {
-        TableSliceGroup group = SelectionTableSliceGroup.create(original(), groupNameTemplate, step);
-        return group.aggregate(summarizedColumnName(), function);
-    }
-
-    /**
-     * Returns the result of applying to the functions to all the values in the appropriate column
-     */
-    public Map<AggregateFunction, Double> getAll() {
-        Map<AggregateFunction, Double> results = new HashMap<>();
-        for (AggregateFunction fun : function) {
-            results.put(fun, fun.agg(original.numberColumn(summarizedColumnName).asDoubleArray()));
-        }
-        return results;
-    }
-}
diff --git a/core/src/main/java/tech/tablesaw/api/DateColumn.java b/core/src/main/java/tech/tablesaw/api/DateColumn.java
index e0bfc288..1b35475c 100644
--- a/core/src/main/java/tech/tablesaw/api/DateColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/DateColumn.java
@@ -308,30 +308,6 @@ public class DateColumn extends AbstractColumn implements DateFilters,
         }
     }
 
-    /**
-     * Returns a StringColumn with the year and month from this column concatenated into a String that will sort
-     * lexicographically in temporal order.
-     * <p>
-     * This simplifies the production of plots and tables that aggregate values into standard temporal units (e.g.,
-     * you want monthly data but your source data is more than a year long and you don't want months from different
-     * years aggregated together).
-     */
-    public StringColumn yearMonthString() {
-        StringColumn newColumn = StringColumn.create(this.name() + " year & month");
-        for (int r = 0; r < this.size(); r++) {
-            int c1 = this.getPackedDate(r);
-            if (c1 == MISSING_VALUE) {
-                newColumn.append(StringColumn.MISSING_VALUE);
-            } else {
-                String ym = String.valueOf(PackedLocalDate.getYear(c1));
-                ym = ym + "-" + Strings.padStart(
-                        String.valueOf(PackedLocalDate.getMonthValue(c1)), 2, '0');
-                newColumn.append(ym);
-            }
-        }
-        return newColumn;
-    }
-
     public LocalDate get(int index) {
         return PackedLocalDate.asLocalDate(getPackedDate(index));
     }
diff --git a/core/src/main/java/tech/tablesaw/api/DoubleColumn.java b/core/src/main/java/tech/tablesaw/api/DoubleColumn.java
index 8acf240f..1b3a16af 100644
--- a/core/src/main/java/tech/tablesaw/api/DoubleColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/DoubleColumn.java
@@ -142,7 +142,7 @@ public class DoubleColumn extends AbstractColumn implements
     /**
      * Returns a new numeric column initialized with the given name and size. The values in the column are
      * integers beginning at startsWith and continuing through size (exclusive), monotonically increasing by 1
-     * TODO consider a generic fill funciton including steps or random samples from various distributions
+     * TODO consider a generic fill function including steps or random samples from various distributions
      */
     public static DoubleColumn indexColumn(String columnName, int size, int startsWith) {
         DoubleColumn indexColumn = DoubleColumn.create(columnName, size);
diff --git a/core/src/main/java/tech/tablesaw/api/Table.java b/core/src/main/java/tech/tablesaw/api/Table.java
index 46b31176..5735fb2a 100644
--- a/core/src/main/java/tech/tablesaw/api/Table.java
+++ b/core/src/main/java/tech/tablesaw/api/Table.java
@@ -24,7 +24,7 @@ import it.unimi.dsi.fastutil.ints.IntIterable;
 import it.unimi.dsi.fastutil.ints.IntIterator;
 import org.apache.commons.lang3.RandomUtils;
 import tech.tablesaw.aggregate.AggregateFunction;
-import tech.tablesaw.aggregate.SummaryFunction;
+import tech.tablesaw.aggregate.Summarizer;
 import tech.tablesaw.columns.Column;
 import tech.tablesaw.filtering.Filter;
 import tech.tablesaw.io.DataFrameReader;
@@ -816,7 +816,8 @@ public class Table extends Relation implements IntIterable {
 
     public Table structure() {
         Table t = new Table("Structure of " + name());
-        NumberColumn index = DoubleColumn.create("Index", columnCount());
+        //NumberColumn index = DoubleColumn.create("Index", columnCount());
+        NumberColumn index = DoubleColumn.indexColumn("Index", columnCount(), 0);
         StringColumn columnName = StringColumn.create("Column Name", columnCount());
         StringColumn columnType = StringColumn.create("Column Type", columnCount());
         t.addColumn(index);
@@ -825,7 +826,6 @@ public class Table extends Relation implements IntIterable {
         columnName.addAll(columnNames());
         for (int i = 0; i < columnCount(); i++) {
             Column column = columnList.get(i);
-            index.append(i);
             columnType.append(column.type().name());
         }
         return t;
@@ -919,12 +919,39 @@ public class Table extends Relation implements IntIterable {
         return this;
     }
 
-    public SummaryFunction summarize(String numericColumnName, AggregateFunction... function) {
-        return new SummaryFunction(this, numericColumnName, function);
+    public Summarizer summarize(String numericColumnName, AggregateFunction... functions) {
+        return summarize(numberColumn(numericColumnName), functions);
     }
 
-    public SummaryFunction summarize(NumberColumn numberColumn, AggregateFunction... function) {
-        return new SummaryFunction(this, numberColumn, function);
+    public Summarizer summarize(String numericColumn1Name, String numericColumn2Name, AggregateFunction... functions) {
+        return summarize(numberColumn(numericColumn1Name), numberColumn(numericColumn2Name), functions);
+    }
+
+    public Summarizer summarize(String col1Name, String col2Name, String col3Name, AggregateFunction... functions) {
+        return summarize(numberColumn(col1Name), numberColumn(col2Name), numberColumn(col3Name), functions);
+    }
+
+    public Summarizer summarize(String col1Name, String col2Name, String col3Name, String col4Name, AggregateFunction... functions) {
+        return summarize(numberColumn(col1Name), numberColumn(col2Name), numberColumn(col3Name), numberColumn(col4Name), functions);
+    }
+
+    public Summarizer summarize(NumberColumn numberColumn, AggregateFunction... function) {
+        return new Summarizer(this, numberColumn, function);
+    }
+
+    public Summarizer summarize(NumberColumn numberColumn1, NumberColumn numberColumn2,
+                                AggregateFunction... function) {
+        return new Summarizer(this, numberColumn1, numberColumn2, function);
+    }
+
+    public Summarizer summarize(NumberColumn column1, NumberColumn column2, NumberColumn column3,
+                                AggregateFunction... function) {
+        return new Summarizer(this, column1, column2, column3, function);
+    }
+
+    public Summarizer summarize(NumberColumn column1, NumberColumn column2, NumberColumn column3, NumberColumn column4,
+                                AggregateFunction... function) {
+        return new Summarizer(this, column1, column2, column3, column4, function);
     }
 
     /**
diff --git a/core/src/main/java/tech/tablesaw/table/SelectionTableSliceGroup.java b/core/src/main/java/tech/tablesaw/table/SelectionTableSliceGroup.java
index 6bbd21be..4a77c9bb 100644
--- a/core/src/main/java/tech/tablesaw/table/SelectionTableSliceGroup.java
+++ b/core/src/main/java/tech/tablesaw/table/SelectionTableSliceGroup.java
@@ -46,7 +46,7 @@ public class SelectionTableSliceGroup extends TableSliceGroup {
             TableSlice view = new TableSlice(getSourceTable(), selections.get(i));
             String name = nameTemplate + ": " + i + 1;
             view.setName(name);
-            getSubTables().add(view);
+            getSlices().add(view);
         }
     }
 }
diff --git a/core/src/main/java/tech/tablesaw/table/StandardTableSliceGroup.java b/core/src/main/java/tech/tablesaw/table/StandardTableSliceGroup.java
index 607c0fbb..34e04b84 100644
--- a/core/src/main/java/tech/tablesaw/table/StandardTableSliceGroup.java
+++ b/core/src/main/java/tech/tablesaw/table/StandardTableSliceGroup.java
@@ -107,7 +107,7 @@ public class StandardTableSliceGroup extends TableSliceGroup {
                 view = new TableSlice(getSourceTable(), selection);
                 view.setName(currentStringKey);
                 currentStringKey = newStringKey;
-                addViewToSubTables(view);
+                addSlice(view);
                 selection = new BitmapBackedSelection();
                 selection.add(row);
             } else {
@@ -117,7 +117,7 @@ public class StandardTableSliceGroup extends TableSliceGroup {
         if (!selection.isEmpty()) {
             view = new TableSlice(getSourceTable(), selection);
             view.setName(currentStringKey);
-            addViewToSubTables(view);
+            addSlice(view);
         }
     }
 }
diff --git a/core/src/main/java/tech/tablesaw/table/TableSliceGroup.java b/core/src/main/java/tech/tablesaw/table/TableSliceGroup.java
index dbc304f7..47f67213 100644
--- a/core/src/main/java/tech/tablesaw/table/TableSliceGroup.java
+++ b/core/src/main/java/tech/tablesaw/table/TableSliceGroup.java
@@ -14,7 +14,6 @@
 
 package tech.tablesaw.table;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ArrayListMultimap;
@@ -38,25 +37,38 @@ import java.util.Map;
  */
 public class TableSliceGroup implements Iterable<TableSlice> {
 
+    // A string that is used internally as a delimiter in creating a column name from all the grouping columns
     static final String SPLIT_STRING = "~~~";
+
+    // A function that splits the group column name back into the original column names for the grouping columns
     private static final Splitter SPLITTER = Splitter.on(SPLIT_STRING);
 
+    // The list of slices or views over the source table that I contain
     private final List<TableSlice> subTables = new ArrayList<>();
 
+    // Grouping columns that were calculated on the fly, rather than being in the source. We remove when we're done.
     final List<CategoricalColumn> columnsToRemove = new ArrayList<>();
 
     private final String[] splitColumnNames;
 
+    // The table that underlies all the manipulations performed here
     private Table sourceTable;
 
+    /**
+     * Returns an instance for calculating a single summary for the given table, with no sub-groupings
+     */
     protected TableSliceGroup(Table original) {
         sourceTable = original;
         splitColumnNames = new String[0];
     }
 
-    protected TableSliceGroup(Table original, String[] splitColumnNames) {
-        sourceTable = original;
-        this.splitColumnNames = splitColumnNames;
+    /**
+     * Returns an instance for calculating subgroups,
+     * one for each combination of the given groupColumnNames that appear in the source table
+     */
+    protected TableSliceGroup(Table sourceTable, String[] groupColumnNames) {
+        this.sourceTable = sourceTable;
+        this.splitColumnNames = groupColumnNames;
     }
 
     String[] getSplitColumnNames() {
@@ -71,11 +83,11 @@ public class TableSliceGroup implements Iterable<TableSlice> {
         return byteSize;
     }
 
-    void addViewToSubTables(TableSlice view) {
-        subTables.add(view);
+    void addSlice(TableSlice slice) {
+        subTables.add(slice);
     }
 
-    public List<TableSlice> getSubTables() {
+    public List<TableSlice> getSlices() {
         return subTables;
     }
 
@@ -83,11 +95,13 @@ public class TableSliceGroup implements Iterable<TableSlice> {
         return subTables.get(i);
     }
 
-    @VisibleForTesting
     public Table getSourceTable() {
         return sourceTable;
     }
 
+    /**
+     * Returns the number of slices
+     */
     public int size() {
         return subTables.size();
     }
@@ -125,15 +139,14 @@ public class TableSliceGroup implements Iterable<TableSlice> {
      * Applies the given aggregation to the given column.
      * The apply and combine steps of a split-apply-combine.
      */
-    public Table aggregate(String colName1, AggregateFunction... func1) {
-        ArrayListMultimap<String, AggregateFunction> map = ArrayListMultimap.create();
-        map.putAll(colName1, Lists.newArrayList(func1));
-        Table result = aggregate(map);
+    public Table aggregate(String colName1, AggregateFunction... functions) {
+        ArrayListMultimap<String, AggregateFunction> columnFunctionMap = ArrayListMultimap.create();
+        columnFunctionMap.putAll(colName1, Lists.newArrayList(functions));
+        Table result = aggregate(columnFunctionMap);
         for (CategoricalColumn column : columnsToRemove) {
             result.removeColumns(column);
         }
         return result;
-
     }
 
     /**
@@ -143,8 +156,8 @@ public class TableSliceGroup implements Iterable<TableSlice> {
      * @param functions map from column name to aggregation to apply on that function
      */
     public Table aggregate(ArrayListMultimap<String, AggregateFunction> functions) {
-        Preconditions.checkArgument(!getSubTables().isEmpty());
-        Table groupTable = Table.create(getSourceTable().name() + " summary");
+        Preconditions.checkArgument(!getSlices().isEmpty());
+        Table groupTable = summaryTableName(sourceTable);
         StringColumn groupColumn = StringColumn.create("Group", size());
         groupTable.addColumn(groupColumn);
         for (Map.Entry<String, Collection<AggregateFunction>> entry : functions.asMap().entrySet()) {
@@ -152,8 +165,8 @@ public class TableSliceGroup implements Iterable<TableSlice> {
             int functionCount = 0;
             for (AggregateFunction function : entry.getValue()) {
                 String colName = aggregateColumnName(columnName, function.functionName());
-                NumberColumn resultColumn = DoubleColumn.create(colName, getSubTables().size());
-                for (TableSlice subTable : getSubTables()) {
+                NumberColumn resultColumn = DoubleColumn.create(colName, getSlices().size());
+                for (TableSlice subTable : getSlices()) {
                     double result = subTable.reduce(columnName, function);
                     if (functionCount == 0) {
                         groupColumn.append(subTable.name());
@@ -171,6 +184,10 @@ public class TableSliceGroup implements Iterable<TableSlice> {
         return result;
     }
 
+    public static Table summaryTableName(Table source) {
+        return Table.create(source.name() + " summary");
+    }
+
     /**
      * Returns an iterator over elements of type {@code T}.
      *
@@ -181,10 +198,16 @@ public class TableSliceGroup implements Iterable<TableSlice> {
         return subTables.iterator();
     }
 
-    private String aggregateColumnName(String columnName, String functionName) {
+    /**
+     * Returns a column name for aggregated data based on the given source column name and function
+     */
+    public static String aggregateColumnName(String columnName, String functionName) {
         return String.format("%s [%s]", functionName, columnName);
     }
 
+    /**
+     * Returns a list of Tables created by reifying my list of slices (views) over the original table
+     */
     public List<Table> asTableList() {
         List<Table> tableList = new ArrayList<>();
         for (TableSlice view : this) {
diff --git a/docs/apidocs/tech/tablesaw/aggregate/SummaryFunction.html b/docs/apidocs/tech/tablesaw/aggregate/SummaryFunction.html
index f956cb8e..1d811e1c 100644
--- a/docs/apidocs/tech/tablesaw/aggregate/SummaryFunction.html
+++ b/docs/apidocs/tech/tablesaw/aggregate/SummaryFunction.html
@@ -125,7 +125,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
 <li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html?is-external=true" title="class or interface in java.lang">java.lang.Object</a></li>
 <li>
 <ul class="inheritance">
-<li>tech.tablesaw.aggregate.SummaryFunction</li>
+<li>tech.tablesaw.aggregate.Summarizer</li>
 </ul>
 </li>
 </ul>
diff --git a/docs/apidocs/tech/tablesaw/aggregate/class-use/SummaryFunction.html b/docs/apidocs/tech/tablesaw/aggregate/class-use/SummaryFunction.html
index 3ac54a9f..0c051ee2 100644
--- a/docs/apidocs/tech/tablesaw/aggregate/class-use/SummaryFunction.html
+++ b/docs/apidocs/tech/tablesaw/aggregate/class-use/SummaryFunction.html
@@ -3,7 +3,7 @@
 <html lang="en">
 <head>
 <!-- Generated by javadoc (9.0.4) on Sat Apr 07 13:48:21 EDT 2018 -->
-<title>Uses of Class tech.tablesaw.aggregate.SummaryFunction (Tablesaw 0.20.0-SNAPSHOT API)</title>
+<title>Uses of Class tech.tablesaw.aggregate.Summarizer (Tablesaw 0.20.0-SNAPSHOT API)</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="date" content="2018-04-07">
 <link rel="stylesheet" type="text/css" href="../../../../stylesheet.css" title="Style">
@@ -21,7 +21,7 @@
 <script type="text/javascript"><!--
     try {
         if (location.href.indexOf('is-external=true') == -1) {
-            parent.document.title="Uses of Class tech.tablesaw.aggregate.SummaryFunction (Tablesaw 0.20.0-SNAPSHOT API)";
+            parent.document.title="Uses of Class tech.tablesaw.aggregate.Summarizer (Tablesaw 0.20.0-SNAPSHOT API)";
         }
     }
     catch(err) {
@@ -95,7 +95,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
 //-->
 </script>
 <div class="header">
-<h2 title="Uses of Class tech.tablesaw.aggregate.SummaryFunction" class="title">Uses of Class<br>tech.tablesaw.aggregate.SummaryFunction</h2>
+<h2 title="Uses of Class tech.tablesaw.aggregate.Summarizer" class="title">Uses of Class<br>tech.tablesaw.aggregate.Summarizer</h2>
 </div>
 <div class="classUseContainer">
 <ul class="blockList">


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/aggregate/AggregateFunctionsTest.java b/core/src/test/java/tech/tablesaw/aggregate/AggregateFunctionsTest.java
index 633ea1aa..ad0ce8d3 100644
--- a/core/src/test/java/tech/tablesaw/aggregate/AggregateFunctionsTest.java
+++ b/core/src/test/java/tech/tablesaw/aggregate/AggregateFunctionsTest.java
@@ -47,13 +47,27 @@ public class AggregateFunctionsTest {
         assertEquals("10.648876067826901", result.get(0, 2));
     }
 
+    @Test
+    public void testGroupMean2() {
+        Table result = table.summarize("approval", mean, stdDev).apply();
+        assertEquals(2, result.columnCount());
+    }
+
     @Test
     public void testGroupMean3() {
-        SummaryFunction function = table.summarize("approval", mean, stdDev);
+        Summarizer function = table.summarize("approval", mean, stdDev);
         Table result = function.by("Group", 10);
         assertEquals(32, result.rowCount());
     }
 
+    @Test
+    public void testGroupMean4() {
+        table.addColumn(table.numberColumn("approval").cube());
+        table.column(3).setName("cubed");
+        Table result = table.summarize("approval", "cubed", mean, stdDev).apply();
+        assertEquals(4, result.columnCount());
+    }
+
     @Test
     public void testGroupMeanByStep() {
         TableSliceGroup group = SelectionTableSliceGroup.create(table, "Step", 5);
@@ -67,11 +81,22 @@ public class AggregateFunctionsTest {
     public void test2ColumnGroupMean() {
         CategoricalColumn byColumn1 = table.stringColumn("who");
         CategoricalColumn byColumn2 = table.categoricalColumn("date");
-        TableSliceGroup group = StandardTableSliceGroup.create(table, byColumn1, byColumn2);
-        Table result = group.aggregate("approval", mean, sum);
+        Table result = table.summarize("approval", mean, sum).by(byColumn1, byColumn2);
         assertEquals(4, result.columnCount());
         assertEquals("who", result.column(0).name());
         assertEquals(323, result.rowCount());
         assertEquals("46.0", result.get(0, 2));
     }
+
+    @Test
+    public void testComplexSummarizing() {
+        table.addColumn(table.numberColumn("approval").cube());
+        table.column(3).setName("cubed");
+        CategoricalColumn byColumn1 = table.stringColumn("who");
+        CategoricalColumn byColumn2 = table.dateColumn("date").yearMonth();
+        Table result = table.summarize("approval", "cubed", mean, sum).by(byColumn1, byColumn2);
+        assertEquals(6, result.columnCount());
+        assertEquals("who", result.column(0).name());
+        assertEquals("date year & month", result.column(1).name());
+    }
 }
\ No newline at end of file
diff --git a/core/src/test/java/tech/tablesaw/api/DateColumnTest.java b/core/src/test/java/tech/tablesaw/api/DateColumnTest.java
index 7f3e4d28..7cd93d6d 100644
--- a/core/src/test/java/tech/tablesaw/api/DateColumnTest.java
+++ b/core/src/test/java/tech/tablesaw/api/DateColumnTest.java
@@ -80,7 +80,7 @@ public class DateColumnTest {
         column1.appendCell("12/24/1924");
         column1.appendCell("12-May-2015");
         column1.appendCell("14-Jan-2015");
-        StringColumn c2 = column1.yearMonthString();
+        StringColumn c2 = column1.yearMonth();
         assertEquals("2013-10", c2.get(0));
         assertEquals("1924-12", c2.get(1));
         assertEquals("2015-05", c2.get(2));
diff --git a/core/src/test/java/tech/tablesaw/filters/TimeDependentFilteringTest.java b/core/src/test/java/tech/tablesaw/filters/TimeDependentFilteringTest.java
index 687a86e5..da2b1669 100644
--- a/core/src/test/java/tech/tablesaw/filters/TimeDependentFilteringTest.java
+++ b/core/src/test/java/tech/tablesaw/filters/TimeDependentFilteringTest.java
@@ -89,7 +89,7 @@ public class TimeDependentFilteringTest {
         TableSliceGroup patients = StandardTableSliceGroup.create(t, "patient");
 
         // Create a list of patient sub-tables to work with TODO(lwhite): Build the copy-on-write to ViewGroups to avoid
-        CopyOnWriteArrayList<TableSlice> patientTables = new CopyOnWriteArrayList<>(patients.getSubTables());
+        CopyOnWriteArrayList<TableSlice> patientTables = new CopyOnWriteArrayList<>(patients.getSlices());
 
         // Apply the independent temporal event filtering to the patient subtables and remove any that don't pass
         for (TableSlice patientTable : patients) {
diff --git a/core/src/test/java/tech/tablesaw/io/csv/CsvReaderTest.java b/core/src/test/java/tech/tablesaw/io/csv/CsvReaderTest.java
index 89e0f237..7c1e0cf9 100644
--- a/core/src/test/java/tech/tablesaw/io/csv/CsvReaderTest.java
+++ b/core/src/test/java/tech/tablesaw/io/csv/CsvReaderTest.java
@@ -14,19 +14,11 @@
 
 package tech.tablesaw.io.csv;
 
-import static java.util.Arrays.asList;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static tech.tablesaw.api.ColumnType.LOCAL_DATE;
-import static tech.tablesaw.api.ColumnType.LOCAL_DATE_TIME;
-import static tech.tablesaw.api.ColumnType.NUMBER;
-import static tech.tablesaw.api.ColumnType.SKIP;
-import static tech.tablesaw.api.ColumnType.STRING;
-import static tech.tablesaw.io.csv.CsvReader.detectColumnTypes;
+import org.junit.Ignore;
+import org.junit.Test;
+import tech.tablesaw.api.ColumnType;
+import tech.tablesaw.api.NumberColumn;
+import tech.tablesaw.api.Table;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -37,12 +29,11 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
-import org.junit.Ignore;
-import org.junit.Test;
-
-import tech.tablesaw.api.ColumnType;
-import tech.tablesaw.api.NumberColumn;
-import tech.tablesaw.api.Table;
+import static java.util.Arrays.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static tech.tablesaw.api.ColumnType.*;
+import static tech.tablesaw.io.csv.CsvReader.*;
 
 /**
  * Tests for CSV Reading
@@ -218,9 +209,9 @@ public class CsvReaderTest {
     public void testPrintStructure() throws Exception {
         String output =
                 "ColumnType[] columnTypes = {\n" +
-                        "LOCAL_DATE, // 0.0   date        \n" +
-                        "NUMBER,     // 1.0   approval    \n" +
-                        "STRING,     // 2.0   who         \n" +
+                        "LOCAL_DATE, // 0     date        \n" +
+                        "NUMBER,     // 1     approval    \n" +
+                        "STRING,     // 2     who         \n" +
                         "}\n";
         assertEquals(output, CsvReader.printColumnTypes("../data/bush.csv", true, ',', Locale.getDefault()));
     }
diff --git a/core/src/test/java/tech/tablesaw/table/TableSliceGroupTest.java b/core/src/test/java/tech/tablesaw/table/TableSliceGroupTest.java
index 0c0b27a8..ba8cf066 100644
--- a/core/src/test/java/tech/tablesaw/table/TableSliceGroupTest.java
+++ b/core/src/test/java/tech/tablesaw/table/TableSliceGroupTest.java
@@ -55,7 +55,7 @@ public class TableSliceGroupTest {
 
         TableSliceGroup group = StandardTableSliceGroup.create(table, table.categoricalColumn("who"));
         assertEquals(6, group.size());
-        List<TableSlice> viewList = group.getSubTables();
+        List<TableSlice> viewList = group.getSlices();
 
         int count = 0;
         for (TableSlice view : viewList) {
@@ -69,7 +69,7 @@ public class TableSliceGroupTest {
         TableSliceGroup group = StandardTableSliceGroup.create(table,
                 table.categoricalColumn("who"),
                 table.categoricalColumn("approval"));
-        List<TableSlice> viewList = group.getSubTables();
+        List<TableSlice> viewList = group.getSlices();
 
         int count = 0;
         for (TableSlice view : viewList) {