From e8973b70831114e6972dc235f9e87041e98c14ac Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 18 Apr 2012 15:25:59 -0700 Subject: [PATCH] Improves TimeSeriesData interface. --- lib/dataset/dataset-model.co | 39 +++++++++++++++---- lib/dataset/datasource-model.co | 2 +- lib/util/timeseries/csv.co | 7 +++- lib/util/timeseries/index.co | 2 + lib/util/timeseries/timeseries.co | 76 ++++++++++++++++++++++++++++++++++--- 5 files changed, 110 insertions(+), 16 deletions(-) diff --git a/lib/dataset/dataset-model.co b/lib/dataset/dataset-model.co index c6767a3..5d1e863 100644 --- a/lib/dataset/dataset-model.co +++ b/lib/dataset/dataset-model.co @@ -63,31 +63,54 @@ DataSet = exports.DataSet = BaseModel.extend do # {{{ this - # TODO: toJSON() must ensure columns in MetricList are ordered by index - # ...in theory, MetricList.comparator now does this + /* * * * TimeSeriesData interface * * * {{{ */ + + /** + * @returns {Array} The reified dataset, materialized to a list of rows including timestamps. + */ + getData: -> + _.zip ...@getColumns() - getDates: -> - dates = @metrics.invoke 'getDates' + /** + * @returns {Array} List of all columns (including date column). + */ + getColumns: -> + [ @getDateColumn() ].concat @getDataColumns() + + /** + * @returns {Array} The date column. + */ + getDateColumn: -> + dates = @metrics.invoke 'getDateColumn' maxLen = _.max _.pluck dates, 'length' _.find dates, -> it.length is maxLen /** - * @returns {Array} The reified dataset, materialized to an array of data-series arrays. + * @returns {Array} List of all columns except the date column. */ - getData: -> - _.zip ...[ @getDates() ].concat @metrics.invoke 'getData' + getDataColumns: -> + @metrics.invoke 'getData' + /** + * @returns {Array} List of column labels. + */ getLabels: -> ['Date'].concat @metrics.pluck 'label' + # }}} + + newMetric: -> index = @metrics.length @metrics.add m = new Metric { index, color:ColorBrewer.Spectral[11][index] } m - onMetricChange: -> @metrics.reset @get 'metrics' + # TODO: toJSON() must ensure columns in MetricList are ordered by index + # ...in theory, MetricList.comparator now does this + + # }}} diff --git a/lib/dataset/datasource-model.co b/lib/dataset/datasource-model.co index 4b13667..b5b8674 100644 --- a/lib/dataset/datasource-model.co +++ b/lib/dataset/datasource-model.co @@ -123,7 +123,7 @@ DataSource = exports.DataSource = BaseModel.extend do # {{{ @trigger 'load-error', this, txtStatus, err - getDates: -> + getDateColumn: -> @data.dateColumn getData: -> diff --git a/lib/util/timeseries/csv.co b/lib/util/timeseries/csv.co index f6ad4f1..8e9409e 100644 --- a/lib/util/timeseries/csv.co +++ b/lib/util/timeseries/csv.co @@ -1,6 +1,8 @@ _ = require 'kraken/util/underscore' op = require 'kraken/util/op' +TimeSeriesData = require 'kraken/util/timeseries/timeseries' + DASH_PATTERN = /-/g BLANK_LINE_PATTERN = /^(\s*)$/ @@ -48,7 +50,10 @@ class CSVData extends TimeSeriesData /** - * Parses a CSV string + * Parses and imports a CSV string. + * + * @private + * @returns {this} */ parse: (@rawData) -> if typeof rawData is not 'string' diff --git a/lib/util/timeseries/index.co b/lib/util/timeseries/index.co index e69de29..6fa58c2 100644 --- a/lib/util/timeseries/index.co +++ b/lib/util/timeseries/index.co @@ -0,0 +1,2 @@ +exports.TimeSeriesData = require 'kraken/timeseries/timeseries' +exports.CSVData = require 'kraken/timeseries/csv' diff --git a/lib/util/timeseries/timeseries.co b/lib/util/timeseries/timeseries.co index d7f46fe..216bc76 100644 --- a/lib/util/timeseries/timeseries.co +++ b/lib/util/timeseries/timeseries.co @@ -36,17 +36,52 @@ class TimeSeriesData @rebuildDerived() + /* * * * TimeSeriesData interface * * * */ + + + /** + * @returns {Array} List of rows, each of which includes all columns. + */ + getData: -> + @data + + /** + * @returns {Array} List of all columns (including date column). + */ + getColumns: -> + @columns + + /** + * @returns {Array} The date column. + */ + getDateColumn: -> + @dateColumn + + /** + * @returns {Array} List of all columns except the date column. + */ + getDataColumns: -> + @dataColumns + + /** + * @returns {Array} List of column labels. + */ + getLabels: -> + @labels + /* * * * Parsing * * * */ /** - * Stub. Subclass and override to perform preprocessing of the data. + * Subclass and override to perform preprocessing of the data. + * @private */ parse : (rawData) -> this /** * Rebuilds the row-oriented data matrix from the columns. + * @private */ rebuildData: -> @rows = _.zip ...@columns @@ -54,11 +89,15 @@ class TimeSeriesData /** * Rebuilds the column-oriented data matrix from the columns. + * @private */ rebuildColumns: -> @columns = _.zip ...@rows @rebuildDerived() + /** + * @private + */ rebuildDerived: -> while @transforms.length < @columns.length @transforms.push [] @@ -70,27 +109,39 @@ class TimeSeriesData /* * * * Data Transformation * * * */ + /** + * Applies the stack of transforms to the data. + * + * TODO: Apply transforms in @getData()? + * @private + * @returns {this} + */ applyTransforms: -> for fns, idx of @transforms for fn of fns @columns[idx] .= map fn, ctx @rebuildData() + /** + * Clears all transforms and restores the original data. + * @returns {this} + */ clearTransforms: -> @transforms = [] @rows = _.merge [], @untransformedRows @rebuildColumns() /** - * Map a function across the specified columns, one-by-one (in column-major - * order), replacing the data with the mapped result. + * Add a data transform to the specified columns. The function is + * applied one-by-one (in column-major order), replacing the data + * with the mapped result. * * @param {Number|Array} indices List one or more column indices to map. Negative * numbers are offset from the end of the columns list. * @param {Function} fn Mapping function of the form: * `(single_value, row_idx, column) -> new_value` - * @param {Object} [ctx=this] Execution context. - * @returns {this} + * @param {Object} [ctx=this] Execution context for the function. + * @returns {this} */ addTransform: (indices, fn, ctx=this) -> num_cols = @columns.length @@ -106,6 +157,16 @@ class TimeSeriesData @transforms[idx].push fn @applyTransforms() + /** + * Add a data transform to all columns except the date column. The function + * is applied one-by-one (in column-major order), replacing the data + * with the mapped result. + * + * @param {Function} fn Mapping function of the form: + * `(single_value, row_idx, column) -> new_value` + * @param {Object} [ctx=this] Execution context for the function. + * @returns {this} + */ addDataTransform: (fn, ctx=this) -> @addTransform _.range(1, @columns.length), fn, ctx @@ -113,8 +174,11 @@ class TimeSeriesData /* * * * Misc * * * */ + /** + * @returns {Array} Deep copy of the data rows (including all columns). + */ toJSON: -> - _.merge [], @rows + _.merge [], @getData() toString: -> labels = @labels -- 1.7.0.4