data-frame-ts
v1.0.3
Published
typescript DataFrame
Readme
Typescript DataFrame
A TypeScript library for working with two-dimensional data structures with tagging capabilities.
A lightweight, immutable, two-dimensional data structure for TypeScript that allows for manipulation and querying of tabular data.
Table of Contents
Installation
npm install data-frame-tsOverview
DataFrame is a two-dimensional data structure that stores data in a row-major format. It is designed to be immutable for immutable objects, meaning that modifications to the rows, columns, or elements will not modify the original DataFrame, but rather return a modified copy.
Key features:
- Create DataFrames from row or column data
- Access and manipulate rows, columns, and individual elements
- Transform data through mapping operations
- Tag rows, columns, and cells with metadata
- Immutable operations that return new DataFrames
Basic Usage
Creating a DataFrame
The DataFrame class provides three factory functions for creating a DataFrame object. Both of these factory methods are static (and remain part of the class for namespacing). The constructor is private and cannot be accessed directly.
DataFrame.from<V>(data: Array<Array<V>>, rowForm: boolean = true): Result<DataFrame<V>, string>fromColumnData<V>(data: Array<Array<V>>): Result<DataFrame<V>, string>empty<V>(): DataFrame<V>
import { DataFrame } from 'data-frame-ts';
// Create a DataFrame from row data and get back a [Result](https://github.com/robphilipp/result) holding the
// data-frame when the data matrix is valid. For example, if not all
// the rows have the same number of elements, then the `Result` holds
// a failure describing the issue.
const result = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
// You also create a `DataFrame` from column data and get back a `Result` holding the new data-frame.
// In this case, the data-frame's first row will be `[1, 2, 3]`, the second row will be `[4, 5, 6]`,
// and the third row will be `[7, 8, 9]`.
const result2 = DataFrame.fromColumnData([
[1, 4, 7], // first column
[2, 5, 8], // second column
[3, 6, 9] // third column
])The first function, from(...) accepts an array of rows, where each row is represented by an array of values of type V. The second function, fromColumnData(...) accepts an array of columns, where each column is represented by an array of values of the type, you guessed it, V. In both cases, the DataFrame converts the data to its internal representation, and all methods behave identically, regardless of the factory function used to instantiate the DataFrame object.
The third function creates an empty DataFrame.
import { DataFrame } from 'data-frame-ts';
const emptyDataFrame = DataFrame.empty<number>()
// returns true
const iFeelSoEmpty = emptyDataFrame.isEmpty()Accessing Data
Once you've created a DataFrame, you may want to ask questions about the data that it holds.
These are examples, and so the usage of the
Resultclass isn't idiomatic. Please see Result documentation for how to use theResultclass correctly.
// After creating a `DataFrame` you can ask questions about it.
// Here, we grab the data-frame directly from the result. (Normally,
// you would use the `Result` class' map method, but this is just
// and example.) See the [Result](https://github.com/robphilipp/result) class
// documentation for more information on `Result`.
const dataFrom = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]).getOrThrow()
// Get dimensions
const rowCount = dataFrom.rowCount(); // 3
const colCount = dataFrom.columnCount(); // 3
// Access an element
const element = dataFrom.elementAt(1, 2).getOrThrow(); // 6
// Get a row
const row = dataFrom.rowSlice(0).getOrThrow(); // [1, 2, 3]
// Get a column
const column = dataFrom.columnSlice(1).getOrThrow(); // [2, 5, 8]
// Get all rows (doesn't need to return a `Result`)
const allRows = dataFrom.rowSlices(); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// Get all columns (doesn't need to return a `Result`)
const allColumns = dataFrom.columnSlices(); // [[1, 4, 7], [2, 5, 8], [3, 6, 9]]Extracting subframes
You can extract a subframe from a DataFrame using the subFrame method. This method takes two parameters:
start: Index- The start (row, column) index of the selection rangeend: Index- The ending (row, column) index of the selection range
The method returns a Result holding a new DataFrame that is a subset of the original DataFrame if the range is valid; otherwise a failure explaining the issue.
// Create a DataFrame
const dataFrame = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]
]).getOrThrow()
// Extract a subframe from row 1, column 1 to row 2, column 2
// This will create a new DataFrame with the following data:
// [[5, 6],
// [8, 9]]
const subFrame = dataFrame.subFrame(indexFrom(1, 1), indexFrom(2, 2)).getOrThrow()
// You can also extract a single row as a subframe
const rowSubFrame = dataFrame.subFrame(indexFrom(1, 0), indexFrom(1, 2)).getOrThrow()
// rowSubFrame contains: [[4, 5, 6]]
// Or a single column as a subframe
const columnSubFrame = dataFrame.subFrame(indexFrom(0, 1), indexFrom(3, 1)).getOrThrow()
// columnSubFrame contains: [[2], [5], [8], [11]]When extracting a subframe, any tags associated with the cells in the range are preserved and their coordinates are properly adjusted to match the new subframe. This ensures that tag information is maintained consistently when working with subframes.
Note: As of version 0.3.0, a bug has been fixed where tags were getting out of sync when a row or column was removed. Now, when you delete rows or columns or extract subframes, the tags are properly maintained and their coordinates are correctly adjusted.
As another example, let's find all the rows in a data-frame that contain the same value as a some chosen element. Then let's create a new data-frame containing only those rows.
// the data contains two rows that contain the value 7.
const data = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 7, 12]
]
// create a new data-frame with only the rows that contain the value found at (row, column) = (2, 0), or
// the value of 7.
const filteredDataFrame = DataFrame
// create a data-frame from the data
.from(data)
// grab the value of the element at (2, 0), and then return a tuple with the original
// data-frame and the value.
.flatMap(dataFrame => dataFrame.elementAt(2, 0).map(value => ({dataFrame, value})))
// run through all the rows of the data-frame, filtering out all the rows that do NOT
// contain a value of 7
.map(pair => pair.dataFrame.rowSlices().filter(row => row.some(value => value === pair.value)))
// create a new data-frame from those filtered rows
.flatMap(rows => DataFrame.from(rows))
// unwrap the filtered data-frame (though in you code, this could go on... :)
.getOrThrow()
// the expected data-frame has only the last two rows of the original data-frame
const expectedDataFrame = DataFrame.from([
[7, 8, 9],
[10, 7, 12]
]).getOrThrow()
expect(filteredDataFrame).toEqual(expectedDataFrame)Modifying Data
Now that we can create data-frames and get basic information from them, we may also want to transform or update the data-frame. The idiomatic way of updating or transforming a data-frame leaves the original data-frame unmodified and returns a copy of the original data-frame with the updated values. When performance is an issue, there are also methods for updating and transforming the data-frame in-place.
// Create a data-frame
const dataFrame = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]).getOrThrow()
//
// The following examples returns a `Result` because, for example,
// the rowIndex and/or the columnIndex could be out of bounds.
// Notice that the original `dataFrame` is not modified.
//
// Set the value of element (0, 0) (returns a new DataFrame).
const updatedDf = dataFrame.setElementAt(0, 0, 100).getOrThrow();
// Add a row after the last row. Returns a `Result` because the specified
// row length may not equal the row lengths in the data-frame.
const dfWithAddedRow = dataFrame.pushRow([10, 11, 12]).getOrThrow();
// Insert a column
const dfWithNewColumn = dataFrame.insertColumnBefore(1, [10, 11, 12]).getOrThrow();
// Add a column at the end
const dfWithAddedColumn = dataFrame.pushColumn([10, 11, 12]).getOrThrow();
// Delete a row
const dfWithoutRow = dataFrame.deleteRowAt(1).getOrThrow();
// Delete a column
const dfWithoutColumn = dataFrame.deleteColumnAt(1).getOrThrow();
//
// These calls modify the original dataFrame object, and return the original
// modified dataFrame
//
// Set an element in-place (modifies the original DataFrame). In this case,
// `dataFrame` and `updatedInPlace` are the same data-frame. Only use this
// method when performance matters.
const originalDataFrame = dataFrame.setElementInPlaceAt(0, 0, 100).getOrThrow();
// Insert a row before the second row in the `dataFrame`. Returns a `Result`
// because the specified row length may not equal the row lengths in the
// data-frame, and the row-index may be out of bounds.
const stillOriginalDataFrame = dataFrame.insertRowBefore(1, [10, 11, 12]).getOrThrow();Transforming Data
The previous section should be an example of modifying the data-frames. This sections gives examples of transforming data-frames.
// Create a data-frame
const dataFrame = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]).getOrThrow()
//
// each of these examples leaves the original `dataFrame` unmodified and returns a new `DataFrame` instance.
//
// Transpose the DataFrame
const transposed = dataFrame.transpose();
// Map all elements in the DataFrame
const stringDF = dataFrame.mapElements(value => value.toString()); // Convert all numbers to strings
// Map a row (returns a new DataFrame)
// Note: The mapper function now receives the column index as the second parameter
const mappedRow = dataFrame.mapRow(1, (value, columnIndex) => value * columnIndex).getOrThrow();
// Map a column (returns a new DataFrame)
// Note: The mapper function now receives the row index as the second parameter
const mappedColumn = dataFrame.mapColumn(1, (value, rowIndex) => value * rowIndex).getOrThrow();
// Change data type through mapping (from numbers to strings)
const stringRowDF = dataFrame.mapRow(1, (value) => value.toString()).getOrThrow();
const stringColumnDF = dataFrame.mapColumn(1, (value) => value.toString()).getOrThrow();
//
// these two examples modifies the original `dataFrame` and returns it
//
// Map a row in-place (modifies the original DataFrame)
// Note: The mapper function now receives the column index as the second parameter
const mappedRowInPlace = dataFrame.mapRowInPlace(1, (value, columnIndex) => value * columnIndex).getOrThrow();
// Map a column in-place (modifies the original DataFrame)
// Note: The mapper function now receives the row index as the second parameter
const mappedColumnInPlace = dataFrame.mapColumnInPlace(1, (value, rowIndex) => value * rowIndex).getOrThrow();The previous example showed simple transformations of the dataFrame. The next examples show how to do multiple transformations on a data-frame idiomatically and get back the resultant transformed data.
// suppose we have some data that we want to transform
const data = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 7, 12]
]
// create a `DataFrame` and manipulate it idiomatically, thanks to the `Result` class
const dataFrame = DataFrame
// create a row-form data-frame
.from(data)
// grab the value (7) of the data element at row 2, column 0. return the value
// and the original data-frame a tuple. (flatMap because elementAt returns a Result)
.flatMap(dataFrame => dataFrame.elementAt(2, 0).map(value => ({dataFrame, value})))
// retrieve only the rows that contain the value 7 (simple map because rowSlices returns
// an array of rows)
.map(pair => pair.dataFrame.rowSlices().filter(row => row.some(value => value === pair.value)))
// and then create a new `DataFrame` from the filtered rows
.flatMap(rows => DataFrame.from(rows))
// transform each element by adding the product of the row-index and column-index to the value
.map(dataFrame => dataFrame.mapElements((value, rowIndex, columnIndex) => value + rowIndex * columnIndex))
// tranpose the data-frame
.map(dataFrame => dataFrame.transpose())
.getOrThrow()
// the values in the `dataFrame` are now
// [[7, 10],
// [8, 7 + 1],
// [9, 12 + 2]]Advanced Features
The DataFrame class supports tagging rows, columns, and cells. AI calls this an advanced feature. Who am I to argue with AI? According to AI, the other "advanced feature" is the use of the Result class for error handling. See below.
Tagging
Tags can be used to store metadata about the DataFrame's structure, such as column headers, row labels, or cell-specific information. The tagging system allows flexible annotation of data and can be used for filtering, highlighting, or providing additional context to your data.
The DataFrame class supports tagging rows, columns, and cells with metadata. Tags are name-value pairs associated with specific coordinates in the DataFrame.
Adding Tags
Tagging elements in a DataFrame is achieved by using the tagRow(...), tagColumn(...), and tagCell(...) methods.
- Tagging a row means that all the elements (cells) in that row have that tag.
- Tagging a column means that all the elements (cells) in that column have that tag.
- Tagging a cell only tags the cell at the specified (row, column) coordinates.
A row, column, and cell can be tagged with any data type that implements the TagValue interface. Essentially, the data type must implement a toString() method. Not a high bar.
A DataFrame is generally immutable. However, there may be times when modifying the data-frame in-place is required. This is true for tagging data as well. By default, adding tags will leave the original DataFrame unchanged, and instead return an updated version with the new tags. Each of the tagging functions has an optional argument modifyInPlace (that defaults to false) that when set to true will modify the original DataFrame and return it. This is not the idiomatic use of the tagging functions. But it is available as an escape hatch.
const dataFrame = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]).getOrThrow()
// Tag a row
dataFrame.tagRow(0, "category", "header").getOrThrow();
// Tag a column
dataFrame.tagColumn(1, "dataType", "numeric").getOrThrow();
// Tag a cell
dataFrame.tagCell(1, 2, "highlight", {color: "orange", opacity: 0.3}).getOrThrow();
// Conditionally tag cells with even number values using a predicate
const taggedDataFrame = dataFrame
.tagCellWhen((value) => value % 2 === 0, "conditional-tag", "even-numbers")
.getOrThrow()
// Chain multiple tag operations
const taggedDataFrame = dataFrame.tagRow(0, "category", "header")
.flatMap(df => df.tagColumn(1, "dataType", "numeric"))
// modify the data-frame in place by setting the "modifyInPlace argument to "true"
.flatMap(df => df.tagCell(1, 2, "highlight", {color: "orange", opacity: 0.3}, true))
.getOrThrow()
// taggedDataFrame and the original dataFrame are the same object
Removing Tags
The DataFrame class provides methods to remove tags from rows, columns, and cells. Like the tagging methods, these methods return a new DataFrame by default, but can modify the original DataFrame if the modifyInPlace parameter is set to true.
// Create a tagged DataFrame
const dataFrame = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]).getOrThrow();
// Add some tags
const taggedDataFrame = dataFrame.tagRow(0, "row-tag", "row-value")
.flatMap(df => df.tagColumn(1, "column-tag", "column-value"))
.flatMap(df => df.tagCell(2, 2, "cell-tag", "cell-value"))
.getOrThrow();
// Remove a row tag (returns a new DataFrame)
const dfWithoutRowTag = taggedDataFrame.removeRowTag(0, "row-tag").getOrThrow();
// Remove a column tag (returns a new DataFrame)
const dfWithoutColumnTag = taggedDataFrame.removeColumnTag(1, "column-tag").getOrThrow();
// Remove a cell tag (returns a new DataFrame)
const dfWithoutCellTag = taggedDataFrame.removeCellTag(2, 2, "cell-tag").getOrThrow();
// Chain multiple tag removal operations
const cleanDataFrame = taggedDataFrame.removeRowTag(0, "row-tag")
.flatMap(df => df.removeColumnTag(1, "column-tag"))
.flatMap(df => df.removeCellTag(2, 2, "cell-tag"))
.getOrThrow();
// Remove a tag in-place (modifies the original DataFrame)
taggedDataFrame.removeRowTag(0, "row-tag", true).getOrThrow();
// Now taggedDataFrame no longer has the "row-tag" tag on row 0Retrieving Tags
Presumably, data-frames are tagged so that we can use the information in the tag later. The next examples show how to retrieve tags based on their type (row, column, cell). There are some important distinctions in the behavior of the tag retrieval methods.
The tagsFor(rowIndex: number, columnIndex: number) method returns all tags on that cell. This means that if the cell is in a row that has a row-tag, that row-tag would also be returned. Likewise, if the cell is in a column that has a column-tag, it is also returned. This is the expected use case. However, if you really only want cell-tags, you can use the cellTagsFor(rowIndex: number, columnIndex: number) method.
The cellTagsFor(rowIndex: number, columnIndex: number) returns only cell-tags that apply to the element in the (rowIndex, columnIndex) of the data-frame.
The rowTagsFor(rowIndex: number) returns only row-tags for any cell in that row. And the columnTagsFor(columnIndex: number) returns only the column-tags for cells in that column.
// Create a tagged DataFrame
const dataFrame = DataFrame.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]
]).getOrThrow();
const taggedDataFrame = dataFrame.tagRow(0, "row-tag", "row-value")
.flatMap(df => df.tagColumn(1, "column-tag", "column-value"))
.flatMap(df => df.tagCell(2, 2, "cell-tag", "cell-value"))
.flatMap(df => df.tagRow(0, "row-tag-2", "row-value"))
.flatMap(df => df.tagRow(1, "row-tag-2", "row-value"))
.getOrThrow();
// Retrieve row tags
const rowTags = taggedDataFrame.rowTagsFor(0);
// rowTags contains two tags: "row-tag" and "row-tag-2"
// Retrieve column tags
const columnTags = taggedDataFrame.columnTagsFor(1);
// columnTags contains one tag: "column-tag"
// Retrieve cell tags for the third row and fourth column
const cellTags = taggedDataFrame.cellTagsFor(2, 2);
// Retrieve all tags for the third row and fourth column, including
// row and column tags if the cell is in a row or column that is tagged.
const allTags = taggedDataFrame.tagsFor(2, 2);
// Check if a row has tags
const hasRowTag = taggedDataFrame.hasRowTagFor(0); // true
const hasNoRowTag = taggedDataFrame.hasRowTagFor(2); // falseIn some cases, you may not want to retrieve the tag for a cell, row, or column. Instead, you may only need to know if there is a tag on that cell. The following example shows how to do that.
// Filter tags based on a predicate
const rowTags = taggedDataFrame.filterTags(tag => tag.name === "row-tag");
// rowTags contains all tags with the name "row-tag"
// Get cells tagged with a specific tag
const tag = newRowTag("row-tag", "row-value", RowCoordinate.of(0));
const cellValues = taggedDataFrame.cellsTaggedWith(tag).getOrThrow();
// cellValues contains all cells in the row tagged with "row-tag"
// Check if a column has tags
const hasColumnTag = taggedDataFrame.hasColumnTagFor(1); // true if column 1 has tags
// Check if a cell has tags
const hasCellTag = taggedDataFrame.hasCellTagFor(2, 2); // true if cell at (2,2) has tags
// Check if a position has any tags (row, column, or cell)
const hasAnyTag = taggedDataFrame.hasTagFor(1, 1); // true if position (1,1) has any tagsRetrieving Cells That Are Tagged
The previous example demonstrated how to retrieve tags and determine whether a cell is tagged. In this section we demonstrate retrieving the cells that are tagged. The cellsTaggedWith(tag: Tag<TagValue, TagCoordinate>): Result<Array<CellValue<V>>, string> method provides a means for doing this. When handed a tag, it returns a Result holding an array of CellValue<T> objects of the shape
{
value: V
row: number
column: number
}The reason that the cellsTaggedWith(...) method returns a Result is that a tag has associated coordinates. The coordinates must be valid. When they are out of bounds, the Result has an error message that explains the reason.
const dataFrame = DataFrame
.from([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]
])
.tagRow(0, "row-tag", "row-value")
.flatMap(df => df.tagColumn(1, "column-tag", "column-value"))
.flatMap(df => df.tagCell(2, 2, "cell-tag", "cell-value"))
.getOrThrow()
// Retrieve cells tagged with a row-tag that is name "row-tag", has a value"
// "row_value", and is in the first row. We expect to get back the array:
// [{"row": 0, "column": 0, "value": 1},
// {"row": 0, "column": 1, "value": 2},
// {"row": 0, "column": 2, "value": 3}]
const cellValues = dataFrame
.cellsTaggedWith(newRowTag("row-tag", "row-value", RowCoordinate.of(0)))
.getOrThrow()Error Handling
The library uses a Result class from the result-fn package for error handling. In a nutshell, the Result class is returned from a function that could fail. The Result holds either a success or a failure, depending on the outcome. The value of the success is the successful execution of the function. The value of the failure is an error object that could be a string or some other more complex object. Results can be chained. In a Results chain, the evaluation in the chain is only performed if the previous evaluation resulted in a success, otherwise it drops through. Here are some examples.
const dataFrame = DataFrame
// create a row-form data-frame
.from(data)
// grab the value (7) of the data element at row 2, column 0. return the value
// and the original data-frame a tuple. (flatMap because elementAt returns a Result)
.flatMap(dataFrame => dataFrame.elementAt(2, 0).map(value => ({dataFrame, value})))
// retrieve only the rows that contain the value 7 (simple map because rowSlices returns
// an array of rows)
.map(pair => pair.dataFrame.rowSlices().filter(row => row.some(value => value === pair.value)))
// and then create a new `DataFrame` from the filtered rows
.flatMap(rows => DataFrame.from(rows))
// transform each element by adding the product of the row-index and column-index to the value
.map(dataFrame => dataFrame.mapElements((value, rowIndex, columnIndex) => value + rowIndex * columnIndex))
// tranpose the data-frame
.map(dataFrame => dataFrame.transpose())
// when there is an error, this will return an empty data-frame (see also getOrThrow)
.getOrElse(DataFrame.empty())API Reference
DataFrame Creation Methods
DataFrame.from<V>(data: Array<Array<V>>, rowForm: boolean = true): Result<DataFrame<V>, string>- Creates a DataFrame from a 2D array of dataDataFrame.fromColumnData<V>(data: Array<Array<V>>): Result<DataFrame<V>, string>- Creates a DataFrame from column-oriented dataDataFrame.empty<V>(): Result<DataFrame<V>, string>- Creates an empty DataFrame
Data Access Methods
isEmpty(): boolean- Returns true if the DataFrame is empty, false otherwiserowCount(): number- Returns the number of rows in the DataFramecolumnCount(): number- Returns the number of columns in the DataFrameelementAt(rowIndex: number, columnIndex: number): Result<V, string>- Returns the element at the specified row and columnrowSlice(rowIndex: number): Result<Array<V>, string>- Returns a copy of the specified rowrowSlices(): Array<Array<V>>- Returns all rows as a 2D arraycolumnSlice(columnIndex: number): Result<Array<V>, string>- Returns a copy of the specified columncolumnSlices(): Array<Array<V>>- Returns all columns as a 2D arraysubFrame(start: Index, end: Index): Result<DataFrame<V>, string>- Returns a subframe for the specified range. The start and end parameters define the inclusive range of rows and columns to include. Any tags associated with cells in the range are preserved, and their coordinates are properly adjusted to match the new subframe.copy(): DataFrame<V>- Creates a copy of the DataFrameequals(other: DataFrame<V>): boolean- Checks if this DataFrame equals another DataFrame
Data Modification Methods
setElementAt(rowIndex: number, columnIndex: number, value: V): Result<DataFrame<V>, string>- Sets the value at the specified row and columnsetElementInPlaceAt(rowIndex: number, columnIndex: number, value: V): Result<DataFrame<V>, string>- Sets the value at the specified row and column and modifies the original DataFrameinsertRowBefore(rowIndex: number, row: Array<V>): Result<DataFrame<V>, string>- Inserts a row before the specified indexpushRow(row: Array<V>): Result<DataFrame<V>, string>- Adds a row at the end of the DataFrameinsertColumnBefore(columnIndex: number, column: Array<V>): Result<DataFrame<V>, string>- Inserts a column before the specified indexpushColumn(column: Array<V>): Result<DataFrame<V>, string>- Adds a column at the end of the DataFramedeleteRowAt(rowIndex: number): Result<DataFrame<V>, string>- Deletes the row at the specified indexdeleteColumnAt(columnIndex: number): Result<DataFrame<V>, string>- Deletes the column at the specified index
Data Transformation Methods
transpose(): DataFrame<V>- Transposes the DataFrame (rows become columns and columns become rows)mapElements<U>(mapper: (value: V, rowIndex: number, columnIndex: number) => U): DataFrame<U>- Applies a function to each element in the DataFrame and returns a new DataFramemapRow<U>(rowIndex: number, mapper: (value: V, columnIndex: number) => U): Result<DataFrame<U>, string>- Maps a function over a row and returns a new DataFrame. The mapper function receives the column index as the second parameter.mapRowInPlace(rowIndex: number, mapper: (value: V, columnIndex: number) => V): Result<DataFrame<V>, string>- Maps a function over a row and modifies the original DataFrame. The mapper function receives the column index as the second parameter.mapColumn<U>(columnIndex: number, mapper: (value: V, rowIndex: number) => U): Result<DataFrame<U>, string>- Maps a function over a column and returns a new DataFrame. The mapper function receives the row index as the second parameter.mapColumnInPlace(columnIndex: number, mapper: (value: V, rowIndex: number) => V): Result<DataFrame<V>, string>- Maps a function over a column and modifies the original DataFrame. The mapper function receives the row index as the second parameter.
Tagging Methods
DataFrame Tagging Methods
tagRow<T extends TagValue>(rowIndex: number, name: string, tag: T, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Tags a specific row with a name-value pairtagColumn<T extends TagValue>(columnIndex: number, name: string, tag: T, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Tags a specific column with a name-value pairtagCell<T extends TagValue>(rowIndex: number, columnIndex: number, name: string, tag: T, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Tags a specific cell with a name-value pairtagCellWhen<T extends TagValue>(predicate: (value: V, rowIndex: number, columnIndex: number) => boolean, name: string, tag: T, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Tags all cells for which the predicate returns trueremoveRowTag(rowIndex: number, name: string, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Removes a tag from a specific rowremoveColumnTag(columnIndex: number, name: string, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Removes a tag from a specific columnremoveCellTag(rowIndex: number, columnIndex: number, name: string, modifyInPlace: boolean = false): Result<DataFrame<V>, string>- Removes a tag from a specific cell
Tag Retrieval Methods
rowTagsFor(rowIndex: number): Array<Tag<TagValue, RowCoordinate>>- Retrieves all row tags associated with the specified row indexcolumnTagsFor(columnIndex: number): Array<Tag<TagValue, ColumnCoordinate>>- Retrieves all column tags associated with the specified column indexcellTagsFor(rowIndex: number, columnIndex: number): Array<Tag<TagValue, CellCoordinate>>- Retrieves all cell-specific tags associated with the specified (row, column) indextagsFor(rowIndex: number, columnIndex: number): Array<Tag<TagValue, TagCoordinate>>- Retrieves all tags (row, column, and cell) associated with the specified (row, column) index
Tag Query Methods
hasRowTagFor(rowIndex: number): boolean- Reports whether the row with the specified index has any row tagshasColumnTagFor(columnIndex: number): boolean- Reports whether the column with the specified index has any column tagshasCellTagFor(rowIndex: number, columnIndex: number): boolean- Reports whether the cell with the specified (row, column) index has any cell tagshasTagFor(rowIndex: number, columnIndex: number): boolean- Reports whether the cell with the specified (row, column) index has any tags (row, column, or cell)filterTags(predicate: (tag: Tag<TagValue, TagCoordinate>) => boolean): Array<Tag<TagValue, TagCoordinate>>- Returns an array of tags that meet the criteria specified in the predicatecellsTaggedWith(tag: Tag<TagValue, TagCoordinate>): Result<Array<CellValue<V>>, string>- Returns an array of cell values that are tagged with the specified tag
Tag Creation Helper Functions
newRowTag(name: string, value: T, coordinate: RowCoordinate): RowTag<T>- Creates a new row tagnewColumnTag(name: string, value: T, coordinate: ColumnCoordinate): ColumnTag<T>- Creates a new column tagnewCellTag(name: string, value: T, coordinate: CellCoordinate): CellTag<T>- Creates a new cell tag
Coordinate Types
The library includes several coordinate types for specifying tag locations:
RowCoordinate- Represents a coordinate for a tag on an entire rowRowCoordinate.of(row: number): RowCoordinate- Creates a row coordinatecoordinate(): [number, NaN]- Returns the row indexequals(other: TagCoordinate): boolean- Compares with another coordinate
ColumnCoordinate- Represents a coordinate for a tag on an entire columnColumnCoordinate.of(column: number): ColumnCoordinate- Creates a column coordinatecoordinate(): [NaN, number]- Returns the column indexequals(other: TagCoordinate): boolean- Compares with another coordinate
CellCoordinate- Represents a coordinate for a tag on a specific cellCellCoordinate.of(row: number, column: number): CellCoordinate- Creates a cell coordinatecoordinate(): [number, number]- Returns the row and column indicesequals(other: TagCoordinate): boolean- Compares with another coordinate
Tags Collection Management
The Tags class provides methods for managing collections of tags:
Tags.with<T>(...tag: Array<Tag<T, TagCoordinate>>)- Creates a Tags object with the specified tagsTags.empty<T, C>()- Creates an empty Tags objectadd(tag: Tag<T, C>)- Adds a new tag if it doesn't already existreplace(tag: Tag<T, C>)- Replaces an existing tagaddOrReplace(tag: Tag<T, C>)- Adds a new tag or replaces an existing oneremove(id: string)- Removes a tag by IDremoveFor(name: string, coordinate: C)- Removes tags with the specified name and coordinatesubset(start: Index, end: Index): Tags<T, C>- Returns tags that are part of the subset defined by the rangeinsertRow(rowIndex: number): Tags<T, C>- Update tag coordinates to accommodate a new rowinsertColumn(columnIndex: number): Tags<T, C>- Updates tag coordinates to accommodate a new columnremoveRow(rowIndex: number): Tags<T, C>- Updates tag coordinates after a row is removedremoveColumn(columnIndex: number): Tags<T, C>- Updates tag coordinates after a column is removed
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License-—see the LICENSE file for details.
