-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Histogram and Scatterplot visualizations support for Table (#1608)
- Loading branch information
Showing
9 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
distribution/std-lib/Standard/src/Visualization/Helpers.enso
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,40 @@ | ||
from Standard.Base import all | ||
|
||
import Standard.Table.Data.Column | ||
import Standard.Table.Data.Storage | ||
import Standard.Table.Data.Table | ||
|
||
## PRIVATE | ||
Any.catch_: Any -> Any | ||
Any.catch_ ~val = this.catch (_-> val) | ||
|
||
## PRIVATE | ||
Any.catch_ : Any -> Any | ||
Error.catch_ ~val = this.catch (_-> val) | ||
|
||
## PRIVATE | ||
recover_errors : Any -> Any | ||
recover_errors ~body = | ||
result = Panic.recover body | ||
result.catch err-> | ||
Json.from_pairs [["error", err.to_display_text]] . to_text | ||
|
||
## PRIVATE | ||
|
||
Returns all the columns in the table, including indices. | ||
Index columns are placed before other columns. | ||
Table.Table.all_columns : Vector | ||
Table.Table.all_columns = | ||
index = this.index.catch_ [] | ||
index_columns = case index of | ||
Vector.Vector _ -> index | ||
a -> [a] | ||
index_columns + this.columns | ||
|
||
|
||
## PRIVATE | ||
|
||
Checks if the column stores numbers. | ||
Column.Column.is_numeric : Boolean | ||
Column.Column.is_numeric = | ||
[Storage.Integer,Storage.Decimal].contains this.storage_type |
69 changes: 69 additions & 0 deletions
69
distribution/std-lib/Standard/src/Visualization/Histogram.enso
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from Standard.Base import all | ||
|
||
import Standard.Table.Data.Column | ||
import Standard.Table.Data.Table | ||
import Standard.Visualization.Helpers | ||
|
||
## PRIVATE | ||
Get first numeric column of the table. | ||
Table.Table.first_numeric : Table -> Column ! Nothing | ||
Table.Table.first_numeric = this.all_columns.find _.is_numeric | ||
|
||
## PRIVATE | ||
Get the value column - the column that will be used to create histogram. | ||
Table.Table.value_column : Table -> Column ! Nothing | ||
Table.Table.value_column = | ||
named_col = this.at 'value' | ||
named_col.catch_ <| this.first_numeric | ||
|
||
## PRIVATE | ||
Information that are placed in an update sent to a visualization. | ||
type Update | ||
## PRIVATE | ||
type Update values label | ||
|
||
## PRIVATE | ||
Generate JSON that can be consumed by the visualization. | ||
to_json : Object | ||
to_json = | ||
data = ['data', Json.from_pairs [['values', this.values]]] | ||
axis = ['axis', Json.from_pairs [['x', Json.from_pairs [['label', this.label]]]]] | ||
ret_pairs = case this.label of | ||
Nothing -> [data] | ||
_ -> [axis,data] | ||
Json.from_pairs ret_pairs | ||
|
||
## PRIVATE | ||
from_table : Table -> Update | ||
from_table table = | ||
col = table.value_column | ||
label = col.name.catch_ Nothing | ||
values = col.to_vector.catch_ [] | ||
Update values label | ||
|
||
## PRIVATE | ||
from_vector : Vector -> Update | ||
from_vector vector = | ||
Update vector Nothing | ||
|
||
## PRIVATE | ||
from_value : Any -> Update | ||
from_value value = | ||
case value of | ||
Table.Table _ -> here.from_table value | ||
Vector.Vector _ -> here.from_vector value | ||
Column.Column _ -> here.from_table value.to_table | ||
_ -> here.from_vector value.to_vector | ||
|
||
## PRIVATE | ||
|
||
Default preprocessor for the histogram visualization. | ||
|
||
Generates JSON text describing the histogram visualization. | ||
|
||
Arguments: | ||
- value: the value to be visualized. | ||
process_to_json_text : Any -> Text | ||
process_to_json_text value = | ||
update = here.from_value value | ||
update.to_json.to_text |
151 changes: 151 additions & 0 deletions
151
distribution/std-lib/Standard/src/Visualization/Scatter_Plot.enso
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
from Standard.Base import all | ||
|
||
import Standard.Table.Data.Column | ||
import Standard.Table.Data.Table | ||
import Standard.Visualization.Helpers | ||
|
||
## PRIVATE | ||
Name of the index column that may be generated to plot against. | ||
index_name : Text | ||
index_name = 'index' | ||
|
||
## PRIVATE | ||
data_field : Text | ||
data_field = 'data' | ||
|
||
## PRIVATE | ||
axis_field : Text | ||
axis_field = 'axis' | ||
|
||
## PRIVATE | ||
label_field : Text | ||
label_field = 'label' | ||
|
||
## PRIVATE | ||
Represents a recognized point data field for a scatter plot visualization. | ||
type PointData | ||
## PRIVATE | ||
type PointData | ||
## PRIVATE | ||
type X | ||
## PRIVATE | ||
type Y | ||
## PRIVATE | ||
type Color | ||
## PRIVATE | ||
type Shape | ||
## PRIVATE | ||
type Label | ||
## PRIVATE | ||
type Size | ||
|
||
## PRIVATE | ||
Returns all recognized point data fields. | ||
all_fields : Vector | ||
all_fields = [X,Y,Color,Shape,Label,Size] | ||
|
||
## PRIVATE | ||
recognized_names : Vector | ||
recognized_names = this.all_fields.map _.name | ||
|
||
## PRIVATE | ||
is_recognized : Column -> Boolean | ||
is_recognized column = this.recognized_names.contains column.name | ||
|
||
## PRIVATE | ||
name : Text | ||
name = this.to_text.to_lower_case | ||
|
||
## PRIVATE | ||
fallback_column : Table -> Column ! Nothing | ||
fallback_column table = case this of | ||
X -> table.index.catch_ <| this.iota table.row_count | ||
Y -> | ||
x_column = X.lookup_in table | ||
candidates = table.all_columns | ||
is_good_enough c = c.is_numeric && c.name != x_column.name | ||
is_good c = is_good_enough c && (this.is_recognized c).not | ||
|
||
candidates.find is_good . catch_ <| candidates.find is_good_enough | ||
_ -> Error.throw Nothing | ||
|
||
## PRIVATE | ||
Returns a vector of subsequent integers beginning from 0. | ||
iota : Number -> Vector | ||
iota count = | ||
# FIXME [mwu]: Adjust once https://github.com/enso-org/enso/issues/1439 | ||
# is addressed. | ||
range = 0.up_to <| count + 1 | ||
Column.from_vector here.index_name range.to_vector | ||
|
||
## PRIVATE | ||
lookup_in : Table -> Column | ||
lookup_in table = | ||
named = table.at this.name | ||
named.catch_ <| this.fallback_column table | ||
|
||
## PRIVATE | ||
Generates JSON that describes points data. | ||
Table.Table.point_data : Table -> Object | ||
Table.Table.point_data = | ||
get_point_data field = field.lookup_in this . rename field.name | ||
is_valid column = column.is_error.not | ||
columns = PointData.all_fields.map get_point_data . filter is_valid | ||
(0.up_to <| this.row_count + 1).to_vector.map <| row_n-> | ||
pairs = columns.map column-> | ||
value = column.at row_n . catch_ Nothing | ||
[column.name, value] | ||
Json.from_pairs pairs | ||
|
||
## PRIVATE | ||
Generates JSON that describes plot axes. | ||
Table.Table.axes : Table -> Object | ||
Table.Table.axes = | ||
describe_axis field = | ||
col_name = field.lookup_in this . name | ||
label = Json.from_pairs [[here.label_field, col_name]] | ||
[field.name, label] | ||
x_axis = describe_axis X | ||
y_axis = describe_axis Y | ||
is_valid axis_pair = | ||
label = axis_pair.at 1 | ||
label.is_error.not && (this.all_columns.length > 0) | ||
axes_obj = Json.from_pairs <| [x_axis, y_axis].filter is_valid | ||
if axes_obj.fields.size > 0 then axes_obj else Nothing | ||
|
||
## PRIVATE | ||
Vector.Vector.point_data : Vector -> Object | ||
Vector.Vector.point_data = | ||
this.map_with_index <| i-> elem-> | ||
Json.from_pairs [[X.name,i],[Y.name,elem]] | ||
|
||
## PRIVATE | ||
|
||
json_from_table table = | ||
data = table.point_data | ||
axes = table.axes | ||
Json.from_pairs <| [[here.data_field,data], [here.axis_field, axes]] | ||
|
||
## PRIVATE | ||
json_from_vector vec = | ||
data = [here.data_field, vec.point_data] | ||
axes = [here.axis_field, Nothing] | ||
Json.from_pairs [data, axes] | ||
|
||
## PRIVATE | ||
|
||
Default preprocessor for the scatterplot visualization. | ||
|
||
Generates JSON text describing the scatterplot visualization. | ||
|
||
Arguments: | ||
- value: the value to be visualized. | ||
process_to_json_text : Any -> Text | ||
process_to_json_text value = | ||
json = case value of | ||
Column.Column _ -> here.json_from_table value.to_table | ||
Table.Table _ -> here.json_from_table value | ||
Vector.Vector _ -> here.json_from_vector value | ||
_ -> here.json_from_vector value.to_vector | ||
|
||
json.to_text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name: Visualization_Tests | ||
version: 0.0.1 | ||
enso-version: default | ||
license: MIT | ||
author: [email protected] | ||
maintainer: [email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from Standard.Base import all | ||
|
||
import Standard.Table.Data.Table | ||
import Standard.Test | ||
import Standard.Visualization.Helpers | ||
|
||
import Visualization_Tests | ||
|
||
spec = | ||
Test.group "Table.all_columns" <| | ||
Test.specify "works with empty table" <| | ||
table = Table.from_rows [] [] | ||
table.all_columns.map (_.name) . should_equal [] | ||
|
||
Test.specify "works when there is no index set" <| | ||
header = ['a', 'b'] | ||
row_1 = [11 , 10 ] | ||
row_2 = [21 , 20 ] | ||
table = Table.from_rows header [row_1, row_2] | ||
table.all_columns.map (_.name) . should_equal ['a','b'] | ||
|
||
Test.specify "works when there is nothing but index" <| | ||
header = ['a'] | ||
row_1 = [11 ] | ||
row_2 = [21 ] | ||
table = Table.from_rows header [row_1, row_2] | ||
table.all_columns.map (_.name) . should_equal ['a'] | ||
|
||
Test.specify "includes both normal and index columns" <| | ||
header = ['a', 'b'] | ||
row_1 = [11 , 10 ] | ||
row_2 = [21 , 20 ] | ||
table = Table.from_rows header [row_1, row_2] . set_index 'a' | ||
table.all_columns.map (_.name) . should_equal ['a','b'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
from Standard.Base import all | ||
|
||
import Standard.Table.Data.Column | ||
import Standard.Table.Data.Table | ||
import Standard.Test | ||
import Standard.Visualization.Histogram | ||
|
||
import Visualization_Tests | ||
|
||
spec = | ||
expect value expected_label expected_values = | ||
text = Histogram.process_to_json_text value | ||
json = Json.parse text | ||
expected_data = Json.from_pairs [['values', expected_values]] | ||
expected_json = case expected_label of | ||
Nothing -> Json.from_pairs [['data', expected_data]] | ||
_ -> | ||
expected_x = Json.from_pairs [['label', expected_label]] | ||
expected_axis = ['axis', Json.from_pairs [['x', expected_x]]] | ||
Json.from_pairs [['data', expected_data], expected_axis] | ||
json.should_equal expected_json | ||
|
||
Test.group "Histogram Visualization" <| | ||
|
||
Test.specify "deals with an empty table" <| | ||
table = Table.from_rows [] [] | ||
expect table Nothing [] | ||
|
||
Test.specify "plots first column if none recognized" <| | ||
header = ['α', 'ω'] | ||
row_1 = [11 , 10 ] | ||
row_2 = [21 , 20 ] | ||
table = Table.from_rows header [row_1, row_2] | ||
expect table 'α' [11,21] | ||
|
||
Test.specify "plots first column if none recognized even if index" <| | ||
header = ['α'] | ||
row_1 = [11 ] | ||
row_2 = [21 ] | ||
table = Table.from_rows header [row_1, row_2] . set_index 'α' | ||
expect table 'α' [11,21] | ||
|
||
Test.specify "plots 'value' numeric column if present" <| | ||
header = ['α', 'value'] | ||
row_1 = [11 , 10 ] | ||
row_2 = [21 , 20 ] | ||
table = Table.from_rows header [row_1, row_2] | ||
expect table 'value' [10,20] | ||
|
||
Test.specify "plots column" <| | ||
column = Column.from_vector 'my_name' [1,4,6] | ||
expect column 'my_name' [1,4,6] | ||
|
||
Test.specify "plots vector" <| | ||
vector = [1,2,3] | ||
expect vector Nothing vector | ||
|
||
Test.specify "plots range" <| | ||
vector = 2.up_to 4 | ||
expect vector Nothing [2,3,4] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from Standard.Base import all | ||
|
||
import Standard.Test | ||
|
||
import Visualization_Tests.Helpers_Spec | ||
import Visualization_Tests.Histogram_Spec | ||
import Visualization_Tests.Scatter_Plot_Spec | ||
|
||
main = Test.Suite.runMain <| | ||
Helpers_Spec.spec | ||
Histogram_Spec.spec | ||
Scatter_Plot_Spec.spec |
Oops, something went wrong.