kiwis
Version:
A Pandas-inspired data wrangling toolkit in JavaScript
177 lines (156 loc) • 4.8 kB
JavaScript
;
const d3 = require('d3-dsv');
const DataFrame = require('./DataFrame.js');
const Series = require('./Series.js');
const Validator = require('./Validator.js');
/**
* @namespace
*/
class Kiwis {
/**
* Returns a new DataFrame from the given data
* @param {Object[]} data An array of objects
* @returns {DataFrame}
* @example
* const kw = require('kiwis');
*
* const df = kw.DataFrame([
* {
* name: 'Marvin',
* surname: '',
* occupation: 'Paranoid Android'
* },
* {
* name: 'Zaphod',
* surname: 'Beeblebrox',
* occupation: 'President of the Galaxy'
* },
* {
* name: 'Arthur',
* surname: 'Dent',
* occupation: null
* }
* ]);
*
* console.log(df.length) // 3
* console.log(df.columns) // ['name', 'surname', 'occupation']
* console.log(df.empty) // false
*/
static DataFrame(data) {
Validator.array('Kiwis.DataFrame()', 'data', data, { type: 'object' });
return new DataFrame(data);
}
/**
* Returns a new Series from the given data
* @param {*[]} data An array of values
* @returns {Series}
* @example
* const kw = require('kiwis');
*
* const series = kw.Series([1, 1, 2, 3, 5, 8, 13, 21, 34]);
*
* console.log(series.length) // 9
* console.log(series.empty) // false
*/
static Series(data) {
Validator.array('Kiwis.Series()', 'data', data);
return new Series(data);
}
/**
* Loads a CSV file into a DataFrame
* @param {string} path Path of the file to load
* @param {Object} [options] Options
* @param {string} [options.delimiter=','] Delimiter of the file
* @param {string} [options.encoding='utf8'] Encoding of the file
* @param {('none'|'camelCase'|'snake_case')} [options.prettify='none'] Prettify column names
* @returns {DataFrame}
* @example
* const kw = require('kiwis');
*
* // Loads a CSV file
* const df = kw.loadCSV('myAwesomeData.csv');
*
* // Loads a TSV file and prettify the columns in camelCase
* const df = kw.loadCSV('myAwesomeData.tsv', { delimiter: '\t', prettify; 'camelCase' });
*/
static loadCSV(path, options = {}) {
Validator.string('Kiwis.loadCSV()', 'path', path);
Validator.options('Kiwis.loadCSV()', options, [
{ key: 'delimiter', type: 'string' },
{ key: 'encoding', type: 'string' },
{ key: 'prettify', type: 'string', enum: ['none', 'camelCase', 'snake_case'] }
]);
const encoding = options.encoding || 'utf8';
const rawData = eval('require')('fs').readFileSync(path, { encoding });
return this.parseCSV(rawData, options);
}
/**
* Parses a CSV string into a DataFrame
* @param {string} csv CSV string to parse
* @param {Object} [options] Options
* @param {string} [options.delimiter=','] Delimiter of the file
* @param {('none'|'camelCase'|'snake_case')} [options.prettify='none'] Prettify column names
* @returns {DataFrame}
* @example
* const kw = require('kiwis');
*
* // Parses a CSV string
* const df = kw.parseCSV(`
* name,surname,occupation\n
* Marvin,,Paranoid Android\n
* Zaphod,Beeblebrox,President of the Galaxy\n
* Arthur,Dent,\n
* `);
*/
static parseCSV(csv, options = {}) {
Validator.string('Kiwis.parseCSV()', 'csv', csv);
Validator.options('Kiwis.parseCSV()', options, [
{ key: 'delimiter', type: 'string' },
{ key: 'prettify', type: 'string', enum: ['none', 'camelCase', 'snake_case'] }
]);
const delimiter = options.delimiter || ',';
const prettify = options.prettify || 'none';
const parser = d3.dsvFormat(delimiter);
const data = parser.parse(csv);
const df = new DataFrame(data);
switch (prettify) {
case 'camelCase':
df.columns = df.columns.map(column => {
return column.toLowerCase()
.split(' ').map((word, index) => index > 0 ? word[0].toUpperCase() + word.slice(1) : word)
.join('');
});
break;
case 'snake_case':
df.columns = df.columns.map(column => column.toLowerCase().replace(/ /g, '_'));
break;
}
return df;
}
/**
* Determines whether a value is N/A or not
* @param {*} value
* @param {Object} [options] Options
* @param {*[]} [options.keep=[0, false]] Array of falsy values not considered N/A
* @returns {boolean}
* @example
* Kiwis.isNA('kiwi'); // false
* Kiwis.isNA(''); // true
* Kiwis.isNA('', { keep: [0, false, ''] }); // false
*/
static isNA(value, options = {}) {
Validator.options('Kiwis.isNA()', options, [
{ key: 'keep', type: '*[]' },
]);
const keep = options.keep || [0, false];
return !value && !keep.includes(value);
}
static isEquivalent(obj1, obj2) {
if (obj1 == obj2) return true;
if (!obj1 || !obj2) return false;
return Object.entries(obj1).reduce((acc, [key, value]) => {
return acc && key in obj2 && value === obj2[key];
}, true);
}
}
module.exports = Kiwis;