npm package: @snlab/florence-datacontainer
Description: A powerful yet light-weight interface to manage data. Designed to be used with [florence](https://gitlab.com/spatialnetworkslab/florence).
Latest version: 0.2.2

# Florence DataContainer
A powerful yet light-weight interface to manage data. Designed to be used with [florence](https://gitlab.com/spatialnetworkslab/florence).

## Installation
In Node or browser applications, `Datacontainer` can be installed from NPM (`npm install @snlab/florence-datacontainer`). After installation, it can be imported into any project using ES6 import syntax.

```js
import DataContainer from '@snlab/florence-datacontainer'
```

You can also load the library directly into an HTML page, which exports a `DataContainer` global:

```html
<script src="https://unpkg.com/@snlab/florence-datacontainer"></script>
```

## API Reference

- [Florence DataContainer](#florence-datacontainer)
  - [Installation](#installation)
  - [API Reference](#api-reference)
    - [Loading data](#loading-data)
      - [Loading column- and row-oriented data](#loading-column--and-row-oriented-data)
      - [Loading GeoJSON data](#loading-geojson-data)
    - [Options](#options)
    - [Keying](#keying)
    - [Accessing data](#accessing-data)
    - [Domains and types](#domains-and-types)
    - [Checks](#checks)
    - [Transformations](#transformations)
    - [Adding and removing rows](#adding-and-removing-rows)
    - [Adding and removing columns](#adding-and-removing-columns)
    - [Classification](#classification)

### Loading data

Loading data to `DataContainer` is done by passing a supported data structure as first argument to the `DataContainer` constructor:

```js
const dataContainer = new DataContainer(supportedDataStructure)
```

`DataContainer` currently supports 3 data structures:

- Column-oriented data
- Row-oriented data
- GeoJSON

More structures might be supported in the future.
`DataContainer` internally stores data in a [column-oriented](https://www.kdnuggets.com/2017/02/apache-arrow-parquet-columnar-data.html) format.
This means that loading column-oriented data will be slightly faster than row-oriented data.

`DataContainer` supports 6 data types. These data types correspond to native JS data types/structures (see table below).

|  Data type   |                                          JS equivalent                                           |        Loadable         | Column name |
| ------------ | ------------------------------------------------------------------------------------------------ | ----------------------- | ----------- |
| quantitative | `Number`                                                                                         | yes                     | NA          |
| categorical  | `String`                                                                                         | yes                     | NA          |
| temporal     | `Date`                                                                                           | yes                     | NA          |
| interval     | `Array` of two `Number`s                                                                         | yes                     | NA          |
| geometry     | [GeoJSON geometry](https://tools.ietf.org/html/rfc7946#appendix-A) (except `GeometryCollection`) | only by loading GeoJSON | `$geometry` |
| grouped      | `DataContainer`                                                                                  | no                      | `$grouped`  |

Data of the types quantitative, categorical, temporal and interval are 'loadable', which means that they can be passed 
in either column-oriented or row-oriented format to the `DataContainer` constructor. 
Geometry data can only be loaded from [GeoJSON](#loading-geojson-data).
Geometry data can only exists in a column called `$geometry`.
Grouped data is not loadable: it can only be generated by using `.groupBy` or `.bin` transformations (see [Transformations](#transformations)).
A column of grouped data is just a column of other `DataContainer`s.
Grouped data can only exist in a column called `$grouped`.

#### Loading column- and row-oriented data

```js
// Column-oriented data
const columnOriented = new DataContainer({
  fruit: ['apple', 'banana', 'coconut', 'durian'],
  amount: [1, 2, 3, 4]
})

// Row-oriented data
const rowOriented = new DataContainer([
  { fruit: 'apple', amount: 1 },
  { fruit: 'banana', amount: 2 },
  { fruit: 'coconut', amount: 3 },
  { fruit: 'durian', amount: 4 }
])

const s = JSON.stringify

s(columnOriented.column('fruit')) === s(rowOriented.column('fruit')) // true
s(columnOriented.column('amount')) === s(rowOriented.column('amount')) // true

```

#### Loading GeoJSON data

When loading [GeoJSON FeatureCollection](https://tools.ietf.org/html/rfc7946#section-1.5)s, the geometry data will end up in a
column called `$geometry`:

```js
const geojson = new DataContainer({
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      geometry: {
        type: 'Point', coordinates: [0, 0]
      },
      properties: {
        fruit: 'apple', amount: 1
      }
    }
  ]
})

geojson.column('$geometry') // [{ type: 'Point', coordinates: [0, 0] }]
geojson.column('fruit') // ['apple']

```

### Options

The `DataContainer` constructor takes a second argument, which is an optional object of options:

```js
const dataContainer = new DataContainer(data, { validate: true })
```

| Option name | Default value |  Type   |
| ----------- | ------------- | ------- |
| validate    | `true`        | Boolean |

Setting the `validate` option to `false` will disable column validation. This can save a bit of time
when you are certain your data is completely valid (i.e. all columns have only one data type and contain
at least one valid that is not `NaN`, `undefined`, `null`, or `Infinity`) or don't care if it is.
More options might be added in the future.

### Keying

The `DataContainer` automatically generates a key for each row when data is loaded. These keys are preserved during transformations like [arrange](#datacontainer_arrange) and [filter](#datacontainer_filter) to keep track of which row is which:

```js
const dataContainer = new DataContainer({ a: [2, 4, 6, 8, 10, 12, 14] })
dataContainer.keys() // ['0', '1', '2', '3', '4', '5', '6']
const transformed = dataContainer.filter(row => row.a > 10)
transformed.keys() // ['5', '6']
```

Furthermore, retrieving, updating or deleting rows (see [accessing data](#accessing-data)) can be done either by index or by key:

```js
const dataContainer = new DataContainer({ a: [2, 4, 6, 8, 10, 12, 14] })
const transformed = dataContainer.filter(row => row.a > 10)
transformed.row({ index: 0 }) // { a: 12, $key: '5' }
transformed.row({ key: '5' }) // { a: 12, $key: '5' }
```

Automatically generated keys are always strings. Besides using automatically generated keys, it is also possible to use a column present in the data as custom key (see [setKey](#datacontainer_setkey)). When using a custom key, it is recommended to use a column with primitive values, like `quantitative` or `categorical` columns, containing respectively `Number`s and `String`s. Other types and objects like dates are possible too, since the key functionality internally uses a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). Beware, however, that a reference to the exact same (and not an equivalent) object must be used in this case to retrieve, update or delete rows.

<a name="datacontainer_keys" href="#datacontainer_keys">#</a> <i>DataContainer</i>.<b>keys</b>()

Returns an array of keys:

```js
const dataContainer = new DataContainer({ a: [100, 200, 300], b: ['a', 'b', 'c'] })
dataContainer.keys() // ['0', '1', '2']
```

<a name="datacontainer_setkey" href="#datacontainer_setkey">#</a> <i>DataContainer</i>.<b>setKey</b>(columnName)

Sets a column as key:

```js
const dataContainer = new DataContainer({ a: [100, 200, 300], b: ['a', 'b', 'c'] })
dataContainer.setKey('b')
dataContainer.keys() // ['a', 'b', 'c']
```

<a name="datacontainer_resetkey" href="#datacontainer_resetkey">#</a> <i>DataContainer</i>.<b>resetKey</b>()

Resets the current keys:

```js
const dataContainer = new DataContainer({ a: [100, 200, 300], b: ['a', 'b', 'c'] })
dataContainer.setKey('b')
dataContainer.keys() // ['a', 'b', 'c']
dataContainer.resetKeys()
dataContainer.keys() ['0', '1', '2']
```

Also works to reset keys after a transformation:

```js
const dataContainer = new DataContainer({ a: [2, 4, 6, 8, 10, 12, 14] })
const transformed = dataContainer.filter(row => row.a > 10)
transformed.keys() // ['5', '6']
transformed.resetKey()
transformed.keys() // ['0', '1']
```

### Accessing data

<a name="datacontainer_data" href="#datacontainer_data">#</a> <i>DataContainer</i>.<b>data</b>()

Returns whatever data is currently loaded to the `DataContainer` in a column-oriented format.

```js
const dataContainer = new DataContainer([
  { fruit: 'apple', amount: 1 },
  { fruit: 'banana', amount: 2 }
])

dataContainer.data() // { fruit: ['apple', 'banana'], amount: [1, 2], $key: ['0', '1'] }
```

<a name="datacontainer_row" href="#datacontainer_row">#</a> <i>DataContainer</i>.<b>row</b>(accessorObject)

Returns an object representing a row. `accessorObject` is either `{ index: <Number> }` or `{ key: <key value> }`.

```js
const dataContainer = new DataContainer({ fruit: ['apple', 'banana'], amount: [1, 2] })
dataContainer.row({ index: 0 }) // { fruit: 'apple', amount: 1, $key: '0' }
```

<a name="datacontainer_rows" href="#datacontainer_rows">#</a> <i>DataContainer</i>.<b>rows</b>()

Returns an `Array` of rows.

```js
const dataContainer = new DataContainer({ fruit: ['apple', 'banana'], amount: [1, 2] })
dataContainer.rows() 
/* [
 *   { fruit: 'apple', amount: 1, $key: '0' },
 *   { fruit: 'banana', amount: 2, $key: '1' },
 * ] 
 */
```

<a name="datacontainer_column" href="#datacontainer_column">#</a> <i>DataContainer</i>.<b>column</b>(columnName)

Returns a column as an `Array`.

```js
const dataContainer = new DataContainer({ fruit: ['apple', 'banana'], amount: [1, 2] })
dataContainer.column('fruit') // ['apple', 'banana']
dataContainer.keys() // ['0', '1']
```

<a name="datacontainer_map" href="#datacontainer_map">#</a> <i>DataContainer</i>.<b>map</b>(columnName, func)

Equivalent to `.column(columnName).map(func)`

<a name="datacontainer_nrow" href="#datacontainer_nrow">#</a> <i>DataContainer</i>.<b>nrow</b>()

Returns the number of rows.

<a name="datacontainer_columnnames" href="#datacontainer_columnnames">#</a> <i>DataContainer</i>.<b>columnNames</b>()

Returns the names of the currently loaded columns.

### Domains and types

<a name="datacontainer_domain" href="#datacontainer_domain">#</a> <i>DataContainer</i>.<b>domain</b>(columnName)

Returns the domain of a column.

```js
const dataContainer = new DataContainer({
  fruit: ['apple', 'banana', 'apple', 'banana'],
  quantity: [1, 2, 3, 4],
  dayOfSale: [new Date(2019, 4, 3), new Date(2019, 4, 4), new Date(2019, 4, 5), new Date(2019, 4, 6)]
})

dataContainer.domain('fruit') // ['apple', 'banana']
dataContainer.domain('quantity') // [1, 4]
dataContainer.domain('dayOfSale') // [Date Fri May 03 2019 ..., Date Mon May 06 2019 ...]
```

For geometry data (`.domain('$geometry')`), this will return the bounding box.

<a name="datacontainer_min" href="#datacontainer_min">#</a> <i>DataContainer</i>.<b>min</b>(columnName)

Equivalent to `domain(columnName)[0]`. Only works for quantitative and interval columns.

<a name="datacontainer_max" href="#datacontainer_max">#</a> <i>DataContainer</i>.<b>max</b>(columnName)

Equivalent to `domain(columnName)[1]`. Only works for quantitative and interval columns.

<a name="datacontainer_bbox" href="#datacontainer_bbox">#</a> <i>DataContainer</i>.<b>bbox</b>()

Equivalent to `.domain('$geometry')`

<a name="datacontainer_type" href="#datacontainer_type">#</a> <i>DataContainer</i>.<b>type</b>(columnName)

Returns the type of a column.

```js
const dataContainer = new DataContainer({
  fruit: ['apple', 'banana', 'apple', 'banana'],
  quantity: [1, 2, 3, 4],
  dayOfSale: [new Date(2019, 4, 3), new Date(2019, 4, 4), new Date(2019, 4, 5), new Date(2019, 4, 6)]
})

dataContainer.type('fruit') // categorical
dataContainer.type('quantity') // quantitative
dataContainer.type('dayOfScale') // temporal
```

### Checks

Some convenience functions to check data during development.

<a name="datacontainer_hascolumn" href="#datacontainer_hascolumn">#</a> <i>DataContainer</i>.<b>hasColumn</b>(columnName)

Checks if the `DataContainer` has a column.

<a name="datacontainer_hasrow" href="datacontainer_hasrow">#</a> <i>DataContainer</i>.<b>hasRow</b>(accessorObject)

Checks if the `DataContainer` has a row. See [.row](#datacontainer_row) for an explanation of the `accessorObject`.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4] })
dataContainer.hasColumn('a') // true
dataContainer.hasColumn('b') // false
```

<a name="datacontainer_columnisvalid" href="#datacontainer_columnisvalid">#</a> <i>DataContainer</i>.<b>columnIsValid</b>(columnName)

Check if a column is valid.

```js
const dataContainer = new DataContainer({ a: [1, NaN, 3] })
  .mutate({ b: () => NaN })

dataContainer.columnIsValid('a') // true
dataContainer.columnIsValid('b') // false
```

<a name="datacontainer_validatecolumn" href="#datacontainer_validatecolumn">#</a> <i>DataContainer</i>.<b>validateColumn</b>(columnName)

Similar to `columnIsValid`, but throws an error if a column is invalid, instead of returning `false`.
, 
```js
const dataContainer = new DataContainer({ a: [1, NaN, 3] })
  .mutate({ b: () => NaN })

dataContainer.validateColumn('a') // nothing happens
dataContainer.validateColumn('b') // throws error
```

<a name="datacontainer_validateallcolumns" href="#datacontainer_validateallcolumns">#</a> <i>DataContainer</i>.<b>validateAllColumns</b>()

When data is first loaded to the `DataContainer`, `validateAllColumns` is ran by default.
To avoid wasting time on checks after every subsequent transformation, it is not ran after that.
If you want `DataContainer` to throw an error if any data has somehow become invalid, you can call this method manually.
Invalid here means that they contain mixed types, or only have invalid data like `NaN`.

### Transformations

`DataContainer`'s transformations are heavily inspired by R's [dplyr](https://dplyr.tidyverse.org/) 
(part of the [tidyverse](https://www.tidyverse.org/)). All transformations will return a new `DataContainer`.

<a name="datacontainer_select" href="#datacontainer_select">#</a> <i>DataContainer</i>.<b>select</b>(selectInstructions)

`select` returns a `DataContainer` with only the columns specified in `selectInstructions`. `selectInstructions` can be a single `String` column name, or an `Array` of column names.

```js
const dataContainer = new DataContainer({
  fruit: ['apple', 'banana'],
  quantity: [1, 2],
  dayOfSale: [new Date(2019, 4, 3), new Date(2019, 4, 4)]
})

const withoutDayOfSale = dataContainer.select(['fruit', 'quantity'])
withoutDayOfSale.data() // { fruit: ['apple', 'banana'], quantity: [1, 2], $key: ['0', '1'] }
```

<a name="datacontainer_rename" href="#datacontainer_rename">#</a> <i>DataContainer</i>.<b>rename</b>(renameInstructions)

`rename` is used to rename columns. `renameInstructions` must be an object with current column names as keys, and desired new column names as values.

```js
const dataContainer = new DataContainer({ f: ['apple', 'banana'], a: [1, 2] })
const renamed = dataContainer.rename({ f: 'fruit', a: 'amount' })
renamed.column('fruit') // ['apple', 'banana']
```

<a name="datacontainer_filter" href="#datacontainer_filter">#</a> <i>DataContainer</i>.<b>filter</b>(filterFunction)

`filter` will throw away all rows that do not satisfy the condition expressed in the `filterFunction`.

```js
const dataContainer = new DataContainer({ fruit: ['apple', 'banana'], amount: [1, 2] })
dataContainer.filter(row => row.fruit !== 'banana').data() // { fruit: ['apple'], amount: [1], $key: ['0'] }
```

<a name="datacontainer_dropna" href="#datacontainer_dropna">#</a> <i>DataContainer</i>.<b>dropNA</b>(dropNAInstructions)

`dropNA` is essentially a special case of `filter` that disposes of invalid values like `NaN`, `null` or `undefined`.
`dropNAInstructions` can be 

- nothing, in which case it will dispose of all rows in all columns that contain invalid values
- a `String` value with a column name. All rows that have invalid values in this column will be removed
- an `Array` of column names (`String`s). All rows that have invalid values in any of these columns will be removed

```js
const dataContainer = new DataContainer(
  { a: [1, 2, undefined, 4], b: [5, null, 7, 8], c: [NaN, 10, 11, 12] }
)

dataContainer.dropNA().data() // { a: [4], b: [8], c: [12], $key: [3] }
dataContainer.dropNA(['a', 'b']).data() // { a: [1, 4], b: [5, 8], c: [NaN, 12], $key: ['0', '3'] }
```

<a name="datacontainer_arrange" href="#datacontainer_arrange">#</a> <i>DataContainer</i>.<b>arrange</b>(arrangeInstructions)

`arrange` is used to sort data. `arrangeInstructions` can be an

- `Object`, with exactly one key (the column by which to sort) and one value 
(how to sort it, either `'ascending'`, `'descending`' or a [compareFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description))
- `Array`, containing `Object`s as described in the line above

```js
const dataContainer = new DataContainer({
  fruit: ['apple', 'banana', 'coconut' 'durian', 'coconut', 'banana'],
  amount: [4, 3, 7, 2, 4, 5]
})

const arranged = dataContainer.arrange([ { fruit: 'descending' }, { amount: 'ascending' } ])
arranged.data()
/* {
 *   fruit: ['durian', 'coconut', 'coconut', 'banana', 'banana', 'apple']
 *   value: [2, 4, 7, 3, 5, 4],
 *   $key: ['3', '4', '2', '1', '5', '0']
 * } */
```

<a name="datacontainer_mutate" href="#datacontainer_mutate">#</a> <i>DataContainer</i>.<b>mutate</b>(mutateInstructions)

`mutate` can be used to generate a new column based on existing rows.
`mutateInstructions` must be an object with new column names as keys, and functions showing how to calculate the new column as values.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4 ]})
const dataContainerWithASquared = dataContainer.mutate({ aSquared: row => row.a * row.a })
dataContainerWithASquared.column('aSquared') // [1, 4, 9, 16]
```

<a name="datacontainer_transmute" href="#datacontainer_transmute">#</a> <i>DataContainer</i>.<b>transmute</b>(mutateInstructions)

Same as `mutate`, except that it removes all the old columns.

<a name="datacontainer_groupby" href="#datacontainer_groupby">#</a> <i>DataContainer</i>.<b>groupBy</b>(groupByInstructions)

Used to split up a `DataContainer` in several `DataContainer`s based on different categories.
`groupByInstructions` can be a `String` containing a single column name, or an `Array` containing multiple column names.

```js
const dataContainer = new DataContainer(
  { fruit: ['apple', 'banana', 'banana', 'apple'], amount: [10, 5, 13, 9] }
)

const grouped = dataContainer.groupBy('fruit')
grouped.column('fruit') // ['apple', 'banana']
grouped.column('$grouped') // [<DataContainer>, <DataContainer>]
grouped.map('$grouped', group => group.column('amount')) // [[10, 9], [5, 13]]
```

<a name="datacontainer_bin" href="#datacontainer_bin">#</a> <i>DataContainer</i>.<b>bin</b>(binInstructions)

Used to split up a `DataContainer` in several `DataContainers` based on classification of quantitative data.

```js
const dataContainer = new DataContainer(
  { a: [1, 2, 3, 4, 5, 6, 7], b: [8, 9, 10, 11, 12, 13, 14] }
)

const binned = dataContainer.bin({ column: 'a', method: 'EqualInterval', numClasses: 3 })
binned.column('bins') // [[1, 3], [3, 5], [5, 7]]
binned.type('bins') // 'interval'
binned.row(1).$grouped.rows() // [{ a: 3, b: 10, $key: '2' }, { a: 4, b: 11, $key: '3' }]
```

Besides `'EqualInterval'`, other methods of classification are supported. Different methods might require different additional
keys to be passed to `binInstructions`. See the table below for an overview.

|       Class. method       |   option name   |
| ------------------------- | --------------- |
| `'EqualInterval'`         | `numClasses`    |
| `'StandardDeviation'`     | `numClasses`    |
| `'Quantile'`              | `numClasses`    |
| `'Jenks'`                 | `numClasses`    |
| `'CKMeans'`               | `numClasses`    |
| `'IntervalSize'`          | `binSize`       |
| `'Manual'`                | `manualClasses` |

For `'Manual'`, `manualClasses` is required and must be an Array of `interval`s, which will become the bins.
The classification is performed internally by [classify-series](https://github.com/Fischerfredl/classify-series).

It is also possible to bin over multiple dimensions by providing an `Array` of `binInstructions`.
Instead of a single `bins` column, this will create multiple columns called `bins_<original column name>`:

```js
const dataContainer = new DataContainer({
  a: [1, 2, 3, 5, 1, 2, 3, 5, 1, 2, 3, 5, 1, 2, 3, 5],
  b: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 5, 5, 5, 5]
})

const binned = dataContainer.bin([
  { column: 'a', method: 'IntervalSize', binSize: 2 },
  { column: 'b', method: 'IntervalSize', binSize: 2 }
])

console.log(binned.column('bins_a')) // [[1, 3], [1, 3], [3, 5], [3, 5]]
console.log(binned.column('bins_b')) // [[1, 3], [3, 5], [1, 3], [3, 5]]
```

<a name="datacontainer_summarise" href="#datacontainer_summarise">#</a> <i>DataContainer</i>.<b>summarise</b>(summariseInstructions)

Used to summarise columns. You can also use `summarize` if you prefer. `summariseInstructions` must be an Object with new column 
names as keys, and `columnInstruction` `Object`s as values. These `columnInstruction`s must have the name of the column
that is to be summarised as key, and the `summaryMethod` as value.
When applying `summarise` to a grouped `DataContainer`, the summaries of the groups will be taken.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4], b: ['a', 'b', 'a', 'b'] })
const grouped = dataContainer.groupBy('b')

dataContainer.summarise({ mean_a: { a: 'mean' }}).data() // { mean_a: [2.5], $key: ['0'] }
grouped.summarise({ mean_a: { a: 'mean' } }).data() // { b: ['a', 'b'], mean_a: [2, 3], $key: ['0', '1'] }
```

The following `summaryMethod`s are available:

- count
- sum
- mean
- median
- mode
- min
- max

It also possible to create your own `summaryMethod` by providing a function that receives the requested column as first argument,
and returns a value that's either quantitative, categorical, temporal or an interval.

<a name="datacontainer_mutarise" href="#datacontainer_mutarise">#</a> <i>DataContainer</i>.<b>mutarise</b>(mutariseInstructions)

`mutarise` (or `mutarize`) is similar to `summarise`, but instead of collapsing the data to a single row, the summarised value
will be added as a new column.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4], b: ['a', 'b', 'a', 'b'] })
const mutarised = dataContainer.groupBy('b').mutarise({ mean_a: { a: 'mean' } })
mutarised.column('a') // [1, 2, 3, 4]
mutarised.column('mean_a') // [2, 3, 2, 3]
```

<a name="datacontainer_transform" href="#datacontainer_transform">#</a> <i>DataContainer</i>.<b>transform</b>(transformFunction)

Used for arbitrary transformations on the data. `transformFunction` receives an `Object` with all the columns currently loaded, 
and should return another object with columns.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4] })
const transformed = dataContainer.transform(columns => {
  const b = columns.a.map(a => a ** 2)
  return { b }
})

transformed.column('b') // [1, 4, 9, 16]
```

<a name="datacontainer_reproject" href="#datacontainer_reproject">#</a> <i>DataContainer</i>.<b>reproject</b>(reprojectFunction)

Used to reproject data in the `$geometry` column. Can only be used when a `$geometry` column is present.
`reprojectFunction` should be a function that accepts an `Array` of two `Number`s and returns an `Array` of two `Number`s.
Particularly convenient to use with [proj4](https://github.com/proj4js/proj4js):

```js
const reprojectFunction = proj4('EPSG:4326', 'EPSG:3857').forward
const dataContainer = new DataContainer(geojson).reproject(reprojectFunction)
```

<a name="datacontainer_cumsum" href="#datacontainer_cumsum">#</a> <i>DataContainer</i>.<b>cumsum</b>(cumsumInstructions, { asInterval: false })

Calculates the cumulative sum of one or more columns. `cumsumInstructions` is an `Object` with new column 
names as keys, and the columns on which the cumulative sum should be based as values. Only quantitative
columns can be used.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4] })
dataContainer.cumsum({ cumsum_a: 'a' }).column('cumsum_a') // [1, 3, 6, 10]
```

If `asInterval` is set to `true`, intervals instead of integers will be returned (mimicking d3's 
[stack](https://github.com/d3/d3-shape#stack)):

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4] })
  .cumsum({ cumsum_a: 'a' }, { asInterval: true })
dataContainer.column('cumsum_a') // [[0, 1], [1, 3], [3, 6], [6, 10]]
```

<a name="datacontainer_rowCumsum" href="#datacontainer_rowCumsum">#</a> <i>DataContainer</i>.<b>rowCumsum</b>(cumsumInstructions, { asInterval: false })

Calculates the cumulative sum over all rows for the selected columns. `cumsumInstructions` is an `Array` with either:
1. column names (`String`s)
2. `Object`s with only one key and one value, where the key is the new column name and the value the old.

In the case of 1., the old columns will be overwritten.

```js
const dataContainer = new DataContainer({
  a: [1, 2, 3, 4],
  b: [1, 2, 3, 4],
  c: [1, 2, 3, 4]
}).rowCumsum(['a', 'b', { rowCumsum_c: 'c' }])

dataContainer.column('a') // [1, 2, 3, 4]
dataContainer.column('b') // [2, 4, 6, 8]
dataContainer.column('rowCumsum_c') // [3, 6, 9, 12]
dataContainer.column('c') // [1, 2, 3, 4]
```

`rowCumsum`'s `asInterval` functionality works similar to `cumsum`'s.

<a name="datacontainer_pivotlonger" href="#datacontainer_pivotlonger">#</a> <i>DataContainer</i>.<b>pivotLonger</b>(pivotInstructions)

Pivots 'wide' data to 'long' data- meaning that a set of selected columns and their values will be converted to two columns: one containing the selected columns' names, and one containing their values.

```js
const dataContainer = new DataContainer({
  col1: [1, 2],
  col2: [10, 20],
  col3: ['a', 'b'],
  col4: ['aa', 'bb']
}).pivotLonger({
  columns: ['col3', 'col4'],
  namesTo: 'name',
  valuesTo: 'value'
})

dataContainer.column('col1') // [1, 1, 2, 2]
dataContainer.column('name') // ['col3', 'col4', 'col3', 'col4']
dataContainer.column('value') // ['a', 'aa', 'b', 'bb']
```

<a name="datacontainer_pivotwider" href="#datacontainer_pivotwider">#</a> <i>DataContainer</i>.<b>pivotWider</b>(pivotInstructions)

The opposite of [pivotLonger](#datacontainer_pivotlonger): pivots 'long' to 'wide' data, meaning that two columns will be converted into a larger set of columns. Missing values in the wider data will be filled with `null`. This default behavior can be changed by providing a `valuesFill` option in de `pivotInstructions` object, besides the `namesFrom` and `valuesFrom` options.

```js
const dataContainer = new DataContainer({
  idCol: ['a', 'a', 'b', 'b', 'b', 'c', 'c'],
  names: ['x', 'y', 'x', 'y', 'z', 'x', 'z'],
  values: [1, 2, 10, 20, 30, 100, 300]
}).pivotWider({
  namesFrom: 'names',
  valuesFrom: 'values'
})

dataContainer.column('idCol') // ['a', 'b', 'c']
dataContainer.column('x') // [1, 10, 100]
dataContainer.column('y') // [2, 20, null]
```

### Adding and removing rows

All of these functions work in-place.

<a name="datacontainer_addrow" href="#datacontainer_addrow">#</a> <i>DataContainer</i>.<b>addRow</b>(row)

Adds a new row to the `DataContainer`. `row` must be an object with one key for every column.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3], b: ['a', 'b', 'c'] })
dataContainer.addRow({ a: 4, b: 'd' })
dataContainer.column('b') // ['a', 'b', 'c', 'd']
```

<a name="datacontainer_updaterow" href="#datacontainer_updaterow">#</a> <i>DataContainer</i>.<b>updateRow</b>(accessorObject, row)

Updates an existing row. `accessorObject` is either `{ index: <Number> }` or `{ key: <key value> }` (see [keying](#keying)).

```js
const dataContainer = new DataContainer({ a: [1, 2, 3], b: ['a', 'b', 'c'] })
dataContainer.updateRow({ index: 2 }, { a: 100 })
dataContainer.column('a') // [1, 2, 100]
```

Instead of using an `Object` as the second argument, it is also possible to use a `Function`. This function
will receive the existing row as first argument, and must return an `Object`.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3], b: ['a', 'b', 'c'] })
dataContainer.updateRow({ index: 2 }, row => ({ a: row.a + 100 }))
dataContainer.column('a') // [1, 2, 103]
```

<a name="datacontainer_deleterow" href="#datacontainer_deleterow">#</a> <i>DataContainer</i>.<b>deleteRow</b>(accessorObject)

Deletes an existing row.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3], b: ['a', 'b', 'c'] })
dataContainer.deleteRow({ index: 2 })
dataContainer.column('a') // [1, 2]
```

### Adding and removing columns

All of these functions work in-place.

<a name="datacontainer_addcolumn" href="#datacontainer_addcolumn">#</a> <i>DataContainer</i>.<b>addColumn</b>(columnName, column)

Adds a new column. `column` must be an `Array` of the same length as the rest of the data.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3], b: ['a', 'b', 'c'] })
dataContainer.addColumn('c', [4, 5, 6])
dataContainer.column('c') // [4, 5, 6]
```

<a name="datacontainer_replacecolumn" href="#datacontainer_replacecolumn">#</a> <i>DataContainer</i>.<b>replaceColumn</b>(columnName, column)

Equivalent to calling `.deleteColumn` followed by `addColumn`.

<a name="datacontainer_deletecolumn" href="#datacontainer_deletecolumn">#</a> <i>DataContainer</i>.<b>deleteColumn</b>(columnName)

Deletes an existing column.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3], b: ['a', 'b', 'c'] })
dataContainer.deleteColumn('b')
dataContainer.data() // { $key: ['0', '1', '2'], a: [1, 2, 3] }
```

### Classification

<a name="datacontainer_bounds" href="#datacontainer_bounds">#</a> <i>DataContainer</i>.<b>bounds</b>(binInstructions)

Returns an array containing the boundaries of the classes found by the classification/binning algorithm. See [bin](#datacontainer_bin) for the structure of `binInstructions`.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4, 5, 6, 7] })
dataContainer.bounds(
  { column: 'a', method: 'EqualInterval', numClasses: 3 }
) // [3, 5]
```

<a name="datacontainer_fullbounds" href="#datacontainer_fullbounds">#</a> <i>DataContainer</i>.<b>fullBounds</b>(binInstructions)

Similar to [.bounds](#datacontainer_bounds), but returns the minimum and maximum value as well:

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4, 5, 6, 7] })
dataContainer.fullBounds(
  { column: 'a', method: 'EqualInterval', numClasses: 3 }
) // [1, 3, 5, 7]
```

<a name="datacontainer_boundranges" href="#datacontainer_boundranges">#</a> <i>DataContainer</i>.<b>boundRanges</b>(binInstructions)

Similar to [.fullBounds](#datacontainer_fullbounds), but different format:

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4, 5, 6, 7] })
dataContainer.boundRanges(
  { column: 'a', method: 'EqualInterval', numClasses: 3 }
) // [[1, 3], [3, 5], [5, 7]]
```

<a name="datacontainer_classify" href="#datacontainer_classify">#</a> <i>DataContainer</i>.<b>classify</b>(binInstructions, range)

Returns a [threshold](https://github.com/d3/d3-scale#threshold-scales) scale based on the bounds determined by classification/binning algorithm, with any type of `range`. `range` must be an array with the same length as `numClasses` in the `binInstructions`.

```js
const dataContainer = new DataContainer({ a: [1, 2, 3, 4, 5, 6, 7] })
const scale = dataContainer.classify(
  { column: 'a', method: 'EqualInterval', numClasses: 3 },
  ['red', 'blue', 'green']
)

scale(2) // 'red'
scale(4) // 'blue'
scale(6) // 'green'
```