als-statistics
Version:
Modular JS statistics toolkit for Node.js and the browser: descriptive stats, correlations (Pearson/Spearman/Kendall), t-tests & ANOVA (Student/Welch), reliability (Cronbach’s alpha), regression (linear/logistic), clustering (DBSCAN/HDBSCAN), and table/co
87 lines (79 loc) • 3.11 kB
JavaScript
import Column from '../../column/index.js'
export class TableData {
static Column = Column
#colValues = []; #columns = {}; #colNames = []; #n = 0;
constructor(data, options={}) {
this.options = options
if(data) this.#init(data)
}
#init(data) {
if (data instanceof TableData) data = data.columns
const entries = Array.isArray(data) && data.every(v => typeof v === 'string')
? data.map(name => ([name, []])) : Object.entries(data)
if(entries.length === 0) return
entries.forEach(([n, v]) => this.#addData(v, n))
const allExcludes = Array.from(new Set(this.#colValues.map(({ invalid }) => invalid).flat()))
this.#colValues.forEach(col => {
const indecesToRemove = allExcludes.filter(i => !col.invalid.includes(i))
col.values = col.values.filter((v, i) => !indecesToRemove.includes(i))
})
this.alignColumns()
}
get n() { return this.#n }
get k() { return this.#colValues.length }
get colValues() { return this.#colValues }
get columns() { return this.#columns }
get colNames() { return this.#colNames }
alignColumns() {
if(this.options.alignColumns === false) return
this.#n = Math.min(...this.#colValues.map(({ n }) => n))
this.#colValues.forEach(column => {
if (column.n !== this.#n) column.values = column.values.slice(0, this.#n)
})
}
#addData(values, name, labels) {
const column = new Column(values, name, labels)
const colName = name || column.name
this.#columns[colName] = column
this.#colValues.push(this.#columns[colName])
this.#colNames.push(colName)
}
#fixRow(row) {
const colValues = Array.isArray(row) ? row : this.colNames.map(n => row[n])
.filter(v => typeof v === 'number' && !isNaN(v) && isFinite(v))
if(colValues.length < this.k) throw new Error(`One of values is not acceptable`)
return colValues
}
addRow(row,index) {
const colValues = this.#fixRow(row)
this.colValues.forEach((col,i) => col.insertAt(index,colValues[i]))
this.#n++
return this
}
addRows(rows,index) { // TODO this method not tested
const columns = this.colNames.map(v => ([]))
rows.forEach(row => { this.#fixRow(row).forEach((v,i) => columns[i].push(v)) });
columns.forEach((col,i) => {
this.colValues[i].insertAt(index,...col)
this.#n++
})
return this
}
deleteRow(index) {
if(index > this.n-1) throw new Error(`index not exists`)
this.colValues.forEach(col => col.deleteValue(index))
this.#n--
}
addColumn(name, data, labels) {
this.#addData(data, name, labels);
this.alignColumns();
return this.#columns[name]
}
deleteColumn(name) {
if (!this.#columns[name]) return this
delete this.#columns[name];
this.#colNames = this.#colNames.filter(k => k !== name)
this.#colValues = this.#colValues.filter((s) => s.name !== name)
return this;
}
}