From 8f184cd6bfa1de5d35f0c60f69588e3a26f15c6e Mon Sep 17 00:00:00 2001 From: Larry White Date: Sun, 25 Aug 2019 10:15:11 -0400 Subject: [PATCH] added a few map functions and tests for same. --- core/src/main/java/tech/tablesaw/api/Row.java | 14 ++-- .../columns/numbers/NumberMapFunctions.java | 13 +++ .../columns/strings/StringMapFunctions.java | 84 ++++++++++++++++--- .../tech/tablesaw/api/NumericColumnTest.java | 13 ++- .../tech/tablesaw/api/StringColumnTest.java | 27 ++++++ 5 files changed, 132 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/tech/tablesaw/api/Row.java b/core/src/main/java/tech/tablesaw/api/Row.java index edee4c320..9a61334a5 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 @@ private void throwWrongTypeError(String columnName) { 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,7 +84,7 @@ private String columnTypeName() { private final ColumnMap> stringColumnMap = new ColumnMap<>(ColumnType.STRING); private final ColumnMap booleanColumnMap = new ColumnMap<>(ColumnType.BOOLEAN); private final ColumnMap dateTimeColumnMap = - new ColumnMap<>(ColumnType.LOCAL_DATE_TIME); + new ColumnMap<>(ColumnType.LOCAL_DATE_TIME); private final ColumnMap instantColumnMap = new ColumnMap<>(ColumnType.INSTANT); private final ColumnMap timeColumnMap = new ColumnMap<>(ColumnType.LOCAL_TIME); private final ColumnMap> columnMap = new ColumnMap<>(); @@ -446,4 +446,4 @@ public String toString() { public void setTime(String columnName, LocalTime value) { timeColumnMap.get(columnName).set(rowNumber, value); } -} \ No newline at end of file +} diff --git a/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapFunctions.java b/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapFunctions.java index f3af7d813..e8d70a77f 100644 --- a/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapFunctions.java +++ b/core/src/main/java/tech/tablesaw/columns/numbers/NumberMapFunctions.java @@ -259,6 +259,19 @@ default DoubleColumn remainder(DoubleColumn column2) { return result; } + default DoubleColumn remainder(double val2) { + DoubleColumn result = DoubleColumn.create(name() + " % " + val2, size()); + for (int r = 0; r < size(); r++) { + double val1 = getDouble(r); + if (DoubleColumnType.isMissingValue(val1) || DoubleColumnType.isMissingValue(val2)) { + result.setMissing(r); + } else { + result.set(r, getDouble(r) % val2); + } + } + return result; + } + /** Returns the natural log of the values in this column as a NumberColumn. */ default DoubleColumn logN() { DoubleColumn newColumn = DoubleColumn.create(name() + "[logN]", size()); diff --git a/core/src/main/java/tech/tablesaw/columns/strings/StringMapFunctions.java b/core/src/main/java/tech/tablesaw/columns/strings/StringMapFunctions.java index 8271b3063..943b1ea4c 100644 --- a/core/src/main/java/tech/tablesaw/columns/strings/StringMapFunctions.java +++ b/core/src/main/java/tech/tablesaw/columns/strings/StringMapFunctions.java @@ -62,6 +62,51 @@ default StringColumn lowerCase() { return newColumn; } + /** + * Capitalizes each String changing the first character of each to title case as per {@link + * Character#toTitleCase(int)}, as if in a sentence. No other characters are changed. + * + *
+   * capitalize(null)  = null
+   * capitalize("")    = ""
+   * capitalize("cat") = "Cat"
+   * capitalize("cAt") = "CAt"
+   * capitalize("'cat'") = "'cat'"
+   * 
+ */ + default StringColumn capitalize() { + + StringColumn newColumn = StringColumn.create(name() + "[titleCase]"); + + for (int r = 0; r < size(); r++) { + String value = getString(r); + newColumn.append(StringUtils.capitalize(value)); + } + return newColumn; + } + + /** + * Repeats each the column's values elementwise, concatinating the results into a new StringColumn + * + * @param times The number of repeat desired + *
+   *  repeat("", 2)   = ""
+   *  repeat("cat", 3) = "catcatcat"
+   * 
+ * + * @return the new StringColumn + */ + default StringColumn repeat(int times) { + + StringColumn newColumn = StringColumn.create(String.format("%s [rep %d]", name(), times)); + + for (int r = 0; r < size(); r++) { + String value = getString(r); + newColumn.append(StringUtils.repeat(value, times)); + } + return newColumn; + } + default StringColumn trim() { StringColumn newColumn = StringColumn.create(name() + "[trim]"); @@ -224,7 +269,7 @@ default StringColumn padStart(int minLength, char padChar) { return newColumn; } - default StringColumn commonPrefix(Column column2) { + default StringColumn commonPrefix(Column column2) { StringColumn newColumn = StringColumn.create(name() + column2.name() + "[prefix]"); @@ -236,7 +281,7 @@ default StringColumn commonPrefix(Column column2) { return newColumn; } - default StringColumn commonSuffix(Column column2) { + default StringColumn commonSuffix(Column column2) { StringColumn newColumn = StringColumn.create(name() + column2.name() + "[suffix]"); @@ -249,7 +294,7 @@ default StringColumn commonSuffix(Column column2) { } /** Returns a column containing the levenshtein distance between the two given string columns */ - default DoubleColumn distance(Column column2) { + default DoubleColumn distance(Column column2) { DoubleColumn newColumn = DoubleColumn.create(name() + column2.name() + "[distance]"); @@ -267,14 +312,14 @@ default DoubleColumn distance(Column column2) { * @param columns the column to append * @return the new column */ - default StringColumn join(String separator, Column... columns) { + default StringColumn join(String separator, Column... columns) { StringColumn newColumn = StringColumn.create(name() + "[column appended]", this.size()); for (int r = 0; r < size(); r++) { - String result = getString(r); + StringBuilder result = new StringBuilder(getString(r)); for (Column stringColumn : columns) { - result = result + separator + stringColumn.get(r); + result.append(separator).append(stringColumn.get(r)); } - newColumn.set(r, result); + newColumn.set(r, result.toString()); } return newColumn; } @@ -282,13 +327,32 @@ default StringColumn join(String separator, Column... columns) { /** * Return a copy of this column with the given string appended to each element * - * @param append the string to append + * @param stringsToAppend the stringified objects to append + * @return the new column + */ + default StringColumn concatenate(Object... stringsToAppend) { + StringColumn newColumn = StringColumn.create(name() + "[append]", this.size()); + for (int r = 0; r < size(); r++) { + for (Object o : stringsToAppend) { + newColumn.set(r, getString(r) + o); + } + } + return newColumn; + } + + /** + * Return a copy of this column with the corresponding value of each column argument appended to + * each element. getString is used to ensure the value returned by the args are strings + * + * @param stringColumns the string columns to append * @return the new column */ - default StringColumn concatenate(String append) { + default StringColumn concatenate(Column... stringColumns) { StringColumn newColumn = StringColumn.create(name() + "[append]", this.size()); for (int r = 0; r < size(); r++) { - newColumn.set(r, getString(r) + append); + StringBuilder s = new StringBuilder(getString(r)); + for (Column stringColumn : stringColumns) s.append(stringColumn.getString(r)); + newColumn.set(r, s.toString()); } return newColumn; } diff --git a/core/src/test/java/tech/tablesaw/api/NumericColumnTest.java b/core/src/test/java/tech/tablesaw/api/NumericColumnTest.java index 78a42633c..d3af92a4f 100644 --- a/core/src/test/java/tech/tablesaw/api/NumericColumnTest.java +++ b/core/src/test/java/tech/tablesaw/api/NumericColumnTest.java @@ -1,15 +1,16 @@ package tech.tablesaw.api; +import static java.lang.Double.NaN; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import tech.tablesaw.columns.numbers.DoubleColumnType; -public class NumericColumnTest { +class NumericColumnTest { @Test - public void testPctChange() { + void testPctChange() { double[] data = new double[] {100, 100, 100, 100, 101, 102, 99, 98}; double missing = DoubleColumnType.missingValueIndicator(); double[] pctChange = new double[] {missing, missing, missing, missing, .01, .02, -.01, -.02}; @@ -17,4 +18,12 @@ public void testPctChange() { assertArrayEquals(pctChange, result.asDoubleArray(), 0.000001); assertEquals("data 4-period Percent Change", result.name()); } + + @Test + void testRemainder() { + double[] data = new double[] {100, 101, NaN, 105}; + DoubleColumn result = DoubleColumn.create("data", data).remainder(20); + double[] expected = {0, 1, NaN, 5}; + assertArrayEquals(expected, result.asDoubleArray()); + } } diff --git a/core/src/test/java/tech/tablesaw/api/StringColumnTest.java b/core/src/test/java/tech/tablesaw/api/StringColumnTest.java index c8129ffec..b2a2ee66e 100644 --- a/core/src/test/java/tech/tablesaw/api/StringColumnTest.java +++ b/core/src/test/java/tech/tablesaw/api/StringColumnTest.java @@ -393,6 +393,33 @@ void testSubstring() { assertEquals(result.get(2), "k"); } + @Test + void testRepeat() { + String[] words = {"running", "icecube", "back"}; + StringColumn wordColumn = StringColumn.create("words", words); + StringColumn result = wordColumn.repeat(3); + assertEquals(result.get(0), "runningrunningrunning"); + } + + @Test + void testCapitalize() { + String[] words = {"running", "ice cube", "stuff4us"}; + StringColumn wordColumn = StringColumn.create("words", words); + StringColumn result = wordColumn.capitalize(); + assertEquals(result.get(0), "Running"); + assertEquals(result.get(1), "Ice cube"); + assertEquals(result.get(2), "Stuff4us"); + } + + @Test + void testConcatinate() { + String[] words = {"running", "icecube", "back"}; + StringColumn wordColumn1 = StringColumn.create("words1", words); + StringColumn wordColumn2 = StringColumn.create("words2", words); + StringColumn result = wordColumn1.concatenate(wordColumn2); + assertEquals(result.get(0), "runningrunning"); + } + @Test void testSubstring2() { String[] words = {"running", "icecube", "back"};