Repository: jtablesaw/tablesaw
Pull request #718 (2019-11-10 21:31:35)

Change Relation.summary() to return a Table #707
Thanks for contributing.

- [ x ] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

Changed Relation.summary() method to return a Table instead of a String. 
This is especially useful for numerical tables, where you can then see all the statistics in one table. This is similar to describe() on the Pandas DataFrame.
There are a few options for handling different column types, here I've merged into one table, but we could also return a List<Table> and summarise by type. The test for StringColumn's with DoubleColumn's shows the challenge here

## Testing

Added two unit tests


emilianbold: Why not add a new method called `describe` and leave the existing `summary` in place?
jackie-h: I've decided to break this up into a few smaller PR's. First one is here #742
benmccann: @jackie-h the other PR looks good. just one minor comment on it. It looks like lots of extra commits have ended up on this PR as well
benmccann: I'll take another look at this one after you rebase it against https://github.com/jtablesaw/tablesaw/pull/742

Patch:
diff --git a/core/src/main/java/tech/tablesaw/api/BooleanColumn.java b/core/src/main/java/tech/tablesaw/api/BooleanColumn.java
index 5c95258f..4b8f4d2f 100644
--- a/core/src/main/java/tech/tablesaw/api/BooleanColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/BooleanColumn.java
@@ -175,13 +175,13 @@ public class BooleanColumn extends AbstractColumn<BooleanColumn, Boolean>
 
     Table table = Table.create(name());
 
-    BooleanColumn booleanColumn = create("Value");
+    StringColumn label = StringColumn.create("Value");
     DoubleColumn countColumn = DoubleColumn.create("Count");
-    table.addColumns(booleanColumn);
+    table.addColumns(label);
     table.addColumns(countColumn);
 
     for (Map.Entry<Byte, Integer> entry : counts.byte2IntEntrySet()) {
-      booleanColumn.append(entry.getKey());
+      label.append(entry.getKey() == 1 ? "true" : "false");
       countColumn.append(entry.getValue());
     }
     return table;
diff --git a/core/src/main/java/tech/tablesaw/columns/numbers/Stats.java b/core/src/main/java/tech/tablesaw/columns/numbers/Stats.java
index 7ef9a3fe..8e6cfce4 100644
--- a/core/src/main/java/tech/tablesaw/columns/numbers/Stats.java
+++ b/core/src/main/java/tech/tablesaw/columns/numbers/Stats.java
@@ -130,7 +130,7 @@ public class Stats {
     t.addColumns(measure);
     t.addColumns(value);
 
-    measure.append("n");
+    measure.append("Count");
     value.append(n);
 
     measure.append("sum");
diff --git a/core/src/main/java/tech/tablesaw/table/Relation.java b/core/src/main/java/tech/tablesaw/table/Relation.java
index 456c2517..6d6625a9 100644
--- a/core/src/main/java/tech/tablesaw/table/Relation.java
+++ b/core/src/main/java/tech/tablesaw/table/Relation.java
@@ -240,19 +240,25 @@ public abstract class Relation implements Iterable<Row> {
     return structure;
   }
 
-  public String summary() {
-    StringBuilder builder = new StringBuilder();
-    builder
-        .append(System.lineSeparator())
-        .append("Table summary for: ")
-        .append(name())
-        .append(System.lineSeparator());
-    for (Column<?> column : columns()) {
-      builder.append(column.summary().print());
-      builder.append(System.lineSeparator());
+  public Table summary() {
+    Table summaryTable = null;
+
+    for (int i = 0; i < this.columnCount(); i++) {
+      Table columnSummary = this.column(i).summary();
+      columnSummary.column(1).setName(this.column(i).name());
+
+      if (summaryTable == null) {
+        // First column, so just create the table from this
+        summaryTable = columnSummary;
+        summaryTable.setName(this.name());
+      } else {
+        String name = summaryTable.column(0).name();
+        summaryTable =
+            summaryTable.joinOn(name).fullOuter(columnSummary, columnSummary.column(0).name());
+      }
     }
-    builder.append(System.lineSeparator());
-    return builder.toString();
+    summaryTable.column(0).setName("Summary");
+    return summaryTable;
   }
 
   public BooleanColumn booleanColumn(int columnIndex) {


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/table/TableSummaryTest.java b/core/src/test/java/tech/tablesaw/table/TableSummaryTest.java
new file mode 100644
index 00000000..ca2e3855
--- /dev/null
+++ b/core/src/test/java/tech/tablesaw/table/TableSummaryTest.java
@@ -0,0 +1,73 @@
+package tech.tablesaw.table;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+import org.junit.jupiter.api.Test;
+import tech.tablesaw.api.BooleanColumn;
+import tech.tablesaw.api.DateColumn;
+import tech.tablesaw.api.DoubleColumn;
+import tech.tablesaw.api.StringColumn;
+import tech.tablesaw.api.Table;
+
+public class TableSummaryTest {
+  @Test
+  public void summaryTestTwoDoubleColumnsStatistics() {
+    Table testTable =
+        Table.create(
+            "Data",
+            DoubleColumn.create("value1", new double[] {1.0, 1.1, 1.2}),
+            DoubleColumn.create("value2", new double[] {2.0, 2.1, 2.2}));
+    Table result = testTable.summary();
+    assertEquals(
+        "                            Data                             \n"
+            + " Summary   |         value1         |        value2         |\n"
+            + "-------------------------------------------------------------\n"
+            + "    Count  |                     3  |                    3  |\n"
+            + "      sum  |                   3.3  |                  6.3  |\n"
+            + "     Mean  |                   1.1  |                  2.1  |\n"
+            + "      Min  |                     1  |                    2  |\n"
+            + "      Max  |                   1.2  |                  2.2  |\n"
+            + "    Range  |   0.19999999999999996  |  0.20000000000000018  |\n"
+            + " Variance  |  0.009999999999999995  |  0.01000000000000004  |\n"
+            + " Std. Dev  |   0.09999999999999998  |   0.1000000000000002  |",
+        result.print());
+  }
+
+  @Test
+  public void summaryMixedTypes() {
+    Table testTable =
+        Table.create(
+            "Data",
+            StringColumn.create("label", new String[] {"yellow", "yellow", "green"}),
+            DoubleColumn.create("value1", new double[] {1.0, 1.1, 1.2}),
+            BooleanColumn.create("truthy", new boolean[] {true, false, true}),
+            DateColumn.create(
+                "dates",
+                new LocalDate[] {
+                  LocalDate.of(2001, 1, 1), LocalDate.of(2002, 1, 1), LocalDate.of(2001, 1, 1)
+                }));
+    Table result = testTable.summary();
+    assertEquals(
+        "                                   Data                                    \n"
+            + "  Summary   |  label   |         value1         |  truthy  |    dates     |\n"
+            + "---------------------------------------------------------------------------\n"
+            + "     Count  |       3  |                     3  |          |           3  |\n"
+            + "    Unique  |       2  |                        |          |              |\n"
+            + "       Top  |  yellow  |                        |          |              |\n"
+            + " Top Freq.  |       2  |                        |          |              |\n"
+            + "       sum  |          |                   3.3  |          |              |\n"
+            + "      Mean  |          |                   1.1  |          |              |\n"
+            + "       Min  |          |                     1  |          |              |\n"
+            + "       Max  |          |                   1.2  |          |              |\n"
+            + "     Range  |          |   0.19999999999999996  |          |              |\n"
+            + "  Variance  |          |  0.009999999999999995  |          |              |\n"
+            + "  Std. Dev  |          |   0.09999999999999998  |          |              |\n"
+            + "     false  |          |                        |       1  |              |\n"
+            + "      true  |          |                        |       2  |              |\n"
+            + "   Missing  |          |                        |          |           0  |\n"
+            + "  Earliest  |          |                        |          |  2001-01-01  |\n"
+            + "    Latest  |          |                        |          |  2002-01-01  |",
+        result.print());
+  }
+}