Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Additional Methods for Table and Column #1628

Merged
merged 3 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 81 additions & 8 deletions distribution/std-lib/Standard/src/Table/Data/Column.enso
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,20 @@ type Column
Nothing -> Error.throw Table.No_Index_Set_Error
i -> Column i

## Returns the item contained in this column at the given index.
at : Integer -> Any
## Returns the value contained in this column at the given index.

If the value is an NA then this method returns nothing. If the index is
not an index in the column it returns an `Index_Out_Of_Bounds_Error`.

Arguments:
- `index`: The index in the column from which to get the value
at : Integer -> (Any | Nothing) ! Index_Out_Of_Bounds_Error
at index =
storage = this.java_column.getStorage
if storage.isNa index then Nothing else
storage.getItem index
valid_index = (index >= 0) && (index < this.length)
if valid_index.not then Error.throw (Index_Out_Of_Bounds_Error index this.length) else
storage = this.java_column.getStorage
if storage.isNa index then Nothing else
storage.getItem index

## Selects only the rows of this column that correspond to `True` values in
`indexes`.
Expand Down Expand Up @@ -380,10 +388,66 @@ type Column

## UNSTABLE

Returns a column truncated to at most `max_rows` rows.
Returns a column containing the first `count` elements in this column.

Arguments:
- count: The number of elements to take from the start of this column.

If `this` has a number of items in it less than `count`, the entire
column will be returned.
take_start : Integer -> Column
take_start max_rows =
Column (this.java_column.slice 0 max_rows)
take_start count =
Column (this.java_column.slice 0 count)

## UNSTABLE

Returns a column containing the last `count` elements in this column.

Arguments:
- count: The number of elements to take from the end of this column.

If `this` has a number of items in it less than `count`, the entire
column will be returned.
take_end : Integer -> Column
take_end count =
start_point = Math.max (this.length - count) 0
Column (this.java_column.slice start_point count)

## UNSTABLE

Returns the first element in the column, if it exists.

If the column is empty, this method will return a dataflow error
containing `Nothing`.
first : Any ! Nothing
iamrecursion marked this conversation as resolved.
Show resolved Hide resolved
first = this.at 0 . catch (_ -> Error.throw Nothing)

## UNSTABLE

Returns the first element in the column, if it exists.

If the column is empty, this method will return a dataflow error
containing `Nothing`.
head : Any ! Nothing
head = this.first

## UNSTABLE

Returns the last element in the column, if it exists.

If the column is empty, this method will return a dataflow error
containing `Nothing`.
last : Any ! Nothing
last = this.at (this.length - 1) . catch (_ -> Error.throw Nothing)

## UNSTABLE

Returns a column containing the values of `this` column with their order
reversed.
reverse : Column
reverse =
mask = OrderBuilder.buildReversedMask this.length
Column <| this.java_column.applyMask mask

## Creates a new column given a name and a vector of elements.
from_vector : Text -> Vector -> Column
Expand Down Expand Up @@ -527,3 +591,12 @@ get_item_string column ix =
if tp == Storage_Type_String then column.getItem ix else
column.getItem ix . to_text

## A type representing an error for an out-of-bounds index in a column.
type Index_Out_Of_Bounds_Error index length

## Pretty-prints the index out of bounds error.
Index_Out_Of_Bounds_Error.to_display_text : Text
Index_Out_Of_Bounds_Error.to_display_text =
ix_text = this.index.to_text
len_text = this.length.to_text
"The index " + ix_text + " is out of bounds in a column of length " + len_text + "."
90 changes: 82 additions & 8 deletions distribution/std-lib/Standard/src/Table/Data/Table.enso
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,14 @@ type Table

## Selects only the rows of this table that correspond to `True` values in
`indexes`.

This is useful for filtering the rows by given predicate.

Arguments:
- indexes: The column to mask the table by. This column should contain
boolean values (`True` or `False`) that determine whether or not the
corresponding row is kept.

> Example
Select only the rows of `my_table` where the `"Status"` column has the
value `"Valid"`
Expand Down Expand Up @@ -110,8 +116,10 @@ type Table
set_index index =
Table (this.java_table.indexFromColumn index)

## Returns the index of this table, as a column (indexed by itself).
Throws `No_Index_Set_Error` if there is no index set.
## Returns the index of this table, as a column that is indexed by itself.

Throws `No_Index_Set_Error` if there is no index set in the table it is
being called on.
index : Column.Column ! No_Index_Set_Error
index = case this.java_table.getIndex.toColumn of
Nothing -> Error.throw No_Index_Set_Error
Expand Down Expand Up @@ -165,10 +173,14 @@ type Table
index = this.java_table.getIndex
Table (Java_Table.new (non_missing.map .java_column . to_array) index)

## Returns the amount of rows in this table.
## Returns the number of rows in this table.
row_count : Integer
row_count = this.java_table.rowCount

## Returns the number of rows in this table.
length : Integer
length = this.row_count

## Returns a Table describing this table's contents.

The table lists all columns, counts of non-null items and storage types
Expand All @@ -181,7 +193,9 @@ type Table
## Returns an aggregate table resulting from grouping the elements by the
value of the specified column.

If the `by` argument is not set, the index is used for grouping instead.
Arguments:
- by: The column in the table to perform grouping by. If this argument
is not set, the index is used for grouping instead.

> Example
Creates a simple table and computes aggregation statistics:
Expand Down Expand Up @@ -311,10 +325,69 @@ type Table

## UNSTABLE

Returns a table truncated to at most `max_rows` rows.
Returns a table containing the first `count` elements in this table.

Arguments:
- count: The number of elements to take from the start of this table.

If `this` has a number of rows in it less than `count`, the entire table
will be returned.
take_start : Integer -> Table
take_start max_rows =
Table (this.java_table.slice 0 max_rows)
take_start count = Table (this.java_table.slice 0 count)

## UNSTABLE

Returns a table containing the last `count` elements in this table.

Arguments:
- count: The number of elements to take from the end of this table.

If `this` has a number of rows in it less than `count`, the entire table
will be returned.
take_end : Integer -> Table
take_end count =
start_point = Math.max (this.row_count - count) 0
Table (this.java_table.slice start_point count)

## UNSTABLE

Returns the first row in the table, if it exists.

If the table is empty, this method will return a dataflow error
containing `Nothing`.
first : Table ! Nothing
first =
table = this.take_start 1
if table.row_count != 1 then Error.throw Nothing else table
iamrecursion marked this conversation as resolved.
Show resolved Hide resolved

## UNSTABLE

Returns the first row in the table, if it exists.

If the table is empty, this method will return a dataflow error
containing `Nothing`.
head : Table ! Nothing
head = this.first

## UNSTABLE

Returns the last row in the table, if it exists.

If the table is empty, this method will return a dataflow error
containing `Nothing`.
last : Table ! Nothing
last =
table = this.take_end 1
if table.row_count != 1 then Error.throw Nothing else table

## UNSTABLE

Returns a table containing the rows of `this` table with their order
reversed.
reverse : Table
reverse =
mask = OrderBuilder.buildReversedMask this.row_count
Table <| this.java_table.applyMask mask

## PRIVATE
comparator_to_java cmp x y = cmp x y . to_sign
Expand All @@ -332,7 +405,8 @@ type Aggregate_Table
values : Table
values = this.columns . map (_.values name_suffix='') . reduce .join

## Returns a column containing the number of elements in each group.
## Returns a column containing the number of elements in each group of the
aggregate table.
count : Column
count = Column.Column this.java_table.count

Expand Down
21 changes: 15 additions & 6 deletions std-bits/src/main/java/org/enso/table/operations/OrderBuilder.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package org.enso.table.operations;

import org.enso.table.data.column.storage.Storage;
import org.enso.table.data.mask.OrderMask;
import org.enso.table.data.table.Column;

import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;
import org.enso.table.data.column.storage.Storage;
import org.enso.table.data.mask.OrderMask;
import org.enso.table.data.table.Column;

/** Builds an order mask resulting in sorting storages according to specified rules. */
public class OrderBuilder {
Expand Down Expand Up @@ -80,8 +79,7 @@ public Comparator<Integer> toComparator(Comparator<Object> fallbackComparator) {
* used instead.
* @param fallbackComparator a comparator that should be used for columns that do not define a
* natural ordering.
* @return and order mask that will result in sorting any storage according to the specified
* rules.
* @return an order mask that will result in sorting any storage according to the specified rules.
*/
public static OrderMask buildOrderMask(
List<OrderRule> rules, Comparator<Object> fallbackComparator) {
Expand All @@ -96,4 +94,15 @@ public static OrderMask buildOrderMask(
IntStream.range(0, size).boxed().sorted(comparator).mapToInt(i -> i).toArray();
return new OrderMask(positions);
}

/**
* Builds an order mask based that will reverse the order of the data being masked.
*
* @param size the size of the data being masked
* @return an order mask that will result in reversing the data it is applied to
*/
public static OrderMask buildReversedMask(int size) {
int[] positions = IntStream.range(0, size).map(i -> size - i - 1).toArray();
return new OrderMask(positions);
}
}
48 changes: 48 additions & 0 deletions test/Table_Tests/src/Column_Spec.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from Standard.Base import all
from Standard.Table import all

import Standard.Table.Data.Column
import Standard.Test

spec = Test.group "Columns" <|
test_column = Column.from_vector "Test" [1, 3, 5, 2, 4, 6]
empty_column = Column.from_vector "Test" []

Test.specify "should allow getting specific elements" <|
test_column.at 0 . should_equal 1
test_column.at 2 . should_equal 5
test_column.at 5 . should_equal 6
test_column.at 6 . should_fail_with Column.Index_Out_Of_Bounds_Error
empty_column.at 0 . should_fail_with Column.Index_Out_Of_Bounds_Error

Test.specify "should be able to take the first n elements" <|
expected_1 = Column.from_vector "Test" [1, 3, 5]
expected_2 = Column.from_vector "Test" [1, 3, 5, 2, 4, 6]
expected_3 = Column.from_vector "Test" []
test_column.take_start 3 . to_vector . should_equal expected_1.to_vector
test_column.take_start 7 . to_vector . should_equal expected_2.to_vector
test_column.take_start 0 . to_vector . should_equal expected_3.to_vector

Test.specify "should be able to take the last n elements" <|
expected_1 = Column.from_vector "Test" [2, 4, 6]
expected_2 = Column.from_vector "Test" [1, 3, 5, 2, 4, 6]
expected_3 = Column.from_vector "Test" []
test_column.take_end 3 . to_vector . should_equal expected_1.to_vector
test_column.take_end 7 . to_vector . should_equal expected_2.to_vector
test_column.take_end 0 . to_vector . should_equal expected_3.to_vector

Test.specify "should be able to get the first / head element" <|
test_column.first . should_equal 1
test_column.head . should_equal 1
empty_column.first.should_fail_with Nothing
empty_column.head.should_fail_with Nothing

Test.specify "should be able to get the last element" <|
test_column.last . should_equal 6
empty_column.last.should_fail_with Nothing

Test.specify "should be able to be reversed" <|
expected_1 = Column.from_vector "Test" [6, 4, 2, 5, 3, 1]
test_column.reverse.to_vector . should_equal expected_1.to_vector
empty_column.reverse.to_vector . should_equal empty_column.to_vector

2 changes: 2 additions & 0 deletions test/Table_Tests/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ from Standard.Base import all
import Standard.Test

import Table_Tests.Table_Spec
import Table_Tests.Column_Spec

main = Test.Suite.runMain <|
Column_Spec.spec
Table_Spec.spec
Loading