UNPKG

dataship-frame

Version:

A Data Frame for Javascript. Crunch numbers in node and the browser.

1,414 lines (1,200 loc) 35.8 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Frame = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ var reducers = require('./stream-reducers'); var BitArray = require('bit-array'); function isarray(obj){ return Object.prototype.toString.call(obj) === "[object Array]";} function isobject(obj){ return Object.prototype.toString.call(obj) === "[object Object]";} function isnumber(obj){ return Object.prototype.toString.call(obj) === "[object Number]";} function isinteger(num){ return num % 1 === 0;} function isstring(obj){ return Object.prototype.toString.call(obj) === "[object String]";} function isfunction(obj){ return Object.prototype.toString.call(obj) === "[object Function]"; } function isdate(obj){ return Object.prototype.toString.call(obj) === "[object Date]";} var typed_array_constructors = { "[object Int32Array]" : true, "[object Uint32Array]" : true, "[object Float32Array]" : true, "[object Int8Array]" : true, "[object Uint8Array]" : true, "[object Int16Array]" : true, "[object Uint16Array]" : true, "[object Float64Array]" : true } function istypedarray(obj){ var tag = Object.prototype.toString.call(obj); return tag in typed_array_constructors; } function shallowcopy(obj){ if(obj == null) return obj; // null or undefined var copy = {}; for(var key in obj){ copy[key] = obj[key]; } return copy; } //function isframe(obj){ return isarray(obj) && (obj.length == 0 || isobject(obj[0])); } /* A lightweight, high performance Columnar Data Store disguised as a Data Frame * * Interface similarity targets and inspiration: * pandas, R, Linq, rethinkDB, Matlab * * column names: * columns.values.tolist(), colnames(f), * * aggregation: * groupby, , , * * filtering: * * # References * https://github.com/StanfordHCI/datavore * http://vincentarelbundock.github.io/Rdatasets/datasets.html * https://galeascience.wordpress.com/2016/08/10/top-10-pandas-numpy-and-scipy-functions-on-github/ * https://github.com/visualfabriq/bquery/blob/master/bquery/khash.h * ## R * http://www.r-tutor.com/r-introduction/data-frame * https://www.datacamp.com/community/tutorials/15-easy-solutions-data-frame-problems-r#gs.ArNaS44 * ## Pandas * http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.groupby.html * http://chrisalbon.com/python/pandas_index_select_and_filter.html * ## Linq * https://msdn.microsoft.com/en-us/library/bb534304(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1 */ /* Create a data frame object from some data, like the Pandas and R objects * of similar name. * * @examples * * // an array of row objects, like the output from babyparse and papaparse * * rows = [ { "name" : "Finn", "age" : 16, "title" : "Finn the Human"}, { "name" : "Jake", "age" : 32 , "title" : "Jake the Dog"}, { "name" : "Simon", "age" : 1043, "title" : "Ice King"}, { "name" : "Bonnibel", "age" : 827, "title" : "Princess Bubblegum"}, { "name" : "Marceline", "age" : 1004, "title" : "Marceline the Vampire Queen"} ]; * df = Frame(rows); * * // an object (dict) mapping column names to arrays of values * * columns = * { * "name" : ["Finn", "Jake", "Simon", "Bonnibel", "Marceline"], * "age" : [16, 32, 1043, 827, 1004], * "title" : ["Finn the Human", "Jake the Dog", "Ice King", "Princess Bubblegum", "Marceline the Vampire Queen"] * }; * * df = Frame(columns); * * // an optional keys argument allows string columns to be more compactly * // represented when duplicates are present * * columns = * { * "name" : [0, 1, 2, 3, 4], * "age" : [16, 32, 1043, 827, 1004], * "title" : [0, 1, 2, 3, 4] * }; * * keys = { * "name" : ["Finn", "Jake", "Simon", "Bonnibel", "Marceline"], * "title" : ["Finn the Human", "Jake the Dog", "Ice King", "Princess Bubblegum", "Marceline the Vampire Queen"] * } * * df = Frame(columns, keys); * */ function Frame(data, keys, index, groups, filters){ // f.constructor.name return "Frame" if(!(this instanceof Frame)) return new Frame(data, keys, index, groups, filters); if(Symbol && Symbol.toStringTag) this[Symbol.toStringTag] = 'Frame'; // TODO: deep copy index if(index){ Object.defineProperty(this, "_index", { "enumerable" : false, "value" : index }); } // was a filters argument provided? if(filters){ // yes, construct a single filter from the values var filter; for(key in filters){ if(filter == null){ filter = filters[key].copy(); } else { filter.and(filters[key]); } } // copy of all defined filters Object.defineProperty(this, "_filters", { "enumerable" : false, "value" : filters }); // single filter produced from combining all filters Object.defineProperty(this, "_filter", { "enumerable" : false, "value" : filter }); Object.defineProperty(this, "_count", { "enumerable" : false, "value" : filter.count() }); } if(groups){ Object.defineProperty(this, "_groups", { "enumerable" : false, "value" : groups.slice(0) }); } // do we have input? if(data == null){ // no, just return an empty Frame return; } // what type of data input do we have? if(isobject(data)){ // object, check it's values var column, length; for(var key in data){ column = data[key]; // are the items arrays? if(isarray(column) || istypedarray(column)){ // yes, check for consistent lengths if(length == null){ length = column.length; } else if(length !== column.length){ throw new Error("Invalid data, arrays in object must be of equal length"); } } else { // no, invalid data throw new Error("Invalid data, must be array of rows or dict of columns"); } } Object.defineProperty(this, "length", { "enumerable" : false, "value" : length }); // all checks pass use data as columns Object.defineProperty(this, "_cols", { "enumerable" : false, "value" : shallowcopy(data) }); // do we also have a key/decoding object? if(keys && isobject(keys)){ // check validity for(var key in keys){ if(!(key in this._cols)) throw new Error("Invalid data, keys object doesn't match columns"); } Object.defineProperty(this, "_keys", { "enumerable" : false, "value" : shallowcopy(keys) }); } } else if(isarray(data)) { // array, check it's elements if(data.length == 0){ return; } Object.defineProperty(this, "length", { "enumerable" : false, "value" : data.length }); // all checks pass use data as columns Object.defineProperty(this, "_cols", { "enumerable" : false, "value" : {} }); var row; for(key in data[0]){ this._cols[key] = []; } for(var i = 0; i < data.length; i++){ row = data[i]; // are the rows objects? if(isobject(row)){ // yes for(key in this._cols){ if(key in row) this._cols[key][i] = row[key]; else this._cols[key][i] = null; } } else { // no, invalid data throw new Error("Invalid data, must be array of rows or dict of columns"); } } } // expose columns as properties for(name in this._cols){ addColumn(this, name); } } Object.defineProperty(Frame.prototype, "add", { enumerable: false, value : function(name, values){ if(this.length !== values.length) throw new Error("Invalid data, arrays in object must be of equal length"); this._cols[name] = values; addColumn(this, name); } }); // internal function for exposing a data column as a property on the Frame function addColumn(frame, name){ Object.defineProperty(frame, name, { enumerable : true, configurable: true, get: function(){ // decode? var result = []; if(frame._keys && name in frame._keys){ // yes, get keys var keys = frame._keys[name]; // map data column onto decoded column // data column should be an array of indices into // the keys array var column = frame._cols[name]; result = new Array(column.length); for(var i = 0; i < column.length;i++){ result[i] = keys[column[i]]; } } else { // no, just return the column result = frame._cols[name]; } if(frame._filter){ return result.filter(function(item, i){ return frame._filter.get(i);}); } else { return result; } }, set : function(data){ if(!isarray(data)) throw new Error("data must be an array"); if(data.length != frame.length) throw new Error("array must match length"); if(frame._keys && name in frame._keys){ throw new Error("setting keyed column not supported yet"); } else { frame._cols[name] = data.slice(0); } } }); } /* // alternate syntax for toStringTag get [Symbol.toStringTag]() { return 'Validator'; } */ module.exports = Frame; /* Get column names */ Object.defineProperty(Frame.prototype, "columns", { enumerable: false, get : function(){ return Object.keys(this._cols); } }); Object.defineProperty(Frame.prototype, "rename", { enumerable: false, value : function(old_name, new_name){ if(!(old_name in this._cols)) throw new Error("Couldn't find a column named '" + selector + "'"); // copy column to new name var column = this._cols[old_name]; this._cols[new_name] = column; // delete old column delete this._cols[old_name]; delete this[old_name]; // rename any decode key if(this._keys && old_name in this._keys){ this._keys[new_name] = this._keys[old_name]; delete this._keys[old_name] } addColumn(this, new_name); } }) Object.defineProperty(Frame.prototype, "distinct", {"enumerable": false, "value" : distinct}); function distinct(selector){ if(!(selector in this._cols)) throw new Error("Couldn't find a column named '" + selector + "'"); var key; if(this._keys) key = this._keys[selector]; var column = this._cols[selector]; var set = {}; var value; for(var i = 0; i < column.length; i++){ if(key) value = key[column[i]]; else value = column[i]; if(this._filter){ if(this._filter.get(i)) set[value] = value; } else { set[value] = value; } } // this step enables non-string values var vals = []; for(key in set) vals.push(set[key]); return vals; }; Object.defineProperty(Frame.prototype, "where", {"enumerable" : false, "value" : where}); /* element of, takes an array as an argument create and return a function that takes a single argument and returns true if that argument is contained in the given array NOTE: null and undefined may both be present in arr, and will be distinct from one another */ function el(arr){ var set = {}; for (var i = 0; i < arr.length; i++) set[arr[i]] = true; return function(v){ return set[v] != null;}; } function eq(a){ return function(v){ return v == a; }; } function where(selector, condition){ if(!(selector in this._cols)) throw new Error("Couldn't find a column named '" + selector + "'"); var column = this._cols[selector]; var filter = new BitArray(this.length); var bits = filter.wordArray; var index = 0; var word = 0|0; var offset; var max = column.length - 1; if(isnumber(condition) || isstring(condition)){ // keyed selector column? if(isstring(condition) && this._keys && selector in this._keys){ // yes, encode condition var keys = this._keys[selector]; condition = keys.indexOf(condition); } for(var i = 0; i < bits.length; i++){ word = 0|0; offset = i * 32; var j = 31 + offset; if(j > max) j = max; for(; j >= offset; j--){ if(column[j] === condition) word |= 1; if(j > offset) word <<= 1; } bits[i] = word; } } else { if(isarray(condition) || istypedarray(condition)){ condition = el(condition); } if(this._keys && selector in this._keys){ // yes, encode condition var keys = this._keys[selector]; } var value; for(var i = 0; i < bits.length; i++){ word = 0|0; offset = i * 32; var j = 31 + offset; if(j > max) j = max; for(; j >= offset; j--){ if(keys) value = keys[column[j]]; else value = column[j]; if(condition(value)) word |= 1; if(j > offset) word <<= 1; } bits[i] = word; } } // create and return a new Frame with the new filter var filters = {}; if(this._filters){ Object.assign(filters, this._filters); } filters[selector] = filter; return new Frame(this._cols, this._keys, this._index, this._groups, filters); } Object.defineProperty(Frame.prototype, "join", {"enumerable" : false, "value" : join}); Object.defineProperty(Frame.prototype, "groupby", {"enumerable" : false, "value" : groupby}); Object.defineProperty(Frame.prototype, "ungroup", {"enumerable" : false, "value" : ungroup}); Object.defineProperty(Frame.prototype, "count", {"enumerable" : false, "value" : count}); Object.defineProperty(Frame.prototype, "argmax", {"enumerable" : false, "value": argmax}); Object.defineProperty(Frame.prototype, "argmin", {"enumerable" : false, "value": argmin}); Object.defineProperty(Frame.prototype, "min", {"enumerable" : false, "value": min}); Object.defineProperty(Frame.prototype, "max", {"enumerable" : false, "value": max}); Object.defineProperty(Frame.prototype, "sum", {"enumerable" : false, "value": sum}); Object.defineProperty(Frame.prototype, "mean", {"enumerable" : false, "value": mean}); Object.defineProperty(Frame.prototype, "median", {"enumerable" : false, "value": median}); Object.defineProperty(Frame.prototype, "reduce", {"enumerable" : false, "value": reduce}); /* use the partition method to find the median */ function median(selector){ var column = this._cols[selector]; var key = selector && this._keys ? this._keys[selector] : null; if (column.length == 0) return null; var p, m; middle = column.length / 2 | 0; var low = 0, high = column.length - 1; var i = 0; // partition the array while(p != middle && i < column.length){ i++; p = partition(column, low, high); if( p < middle) low = p + 1; else high = p - 1; } if(i == column.length){ console.error("Maximum partition reached"); } if(key) return key[column[p]]; else return column[p]; } /* partition an array, in place */ function partition(arr, low, high){ if (low >= high) return high; // choose a random index for the pivot var pivot = randint(low, high); // swap pivot into last location swap(arr, high, pivot); pivot = low; // location of pivot in result // scan array and swap elements less than pivot into low end for(var i = low; i < high; i++){ if (arr[i] < arr[high]){ swap(arr, i, pivot); pivot++; } } swap(arr, high, pivot); return pivot; } /* get random integer in the inclusive interval [a, b] a and b must be integers for correct performance */ function randint(a, b){ r = Math.random(); //[0, 1) return a + Math.floor((b - a + 1)*r); } function swap(arr, i, j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function join(frame, link){ // verify length of link column if(link.length !== this.length) throw new Error("Length of link column must match frame."); if(!("_cols" in frame)) throw new Error("First argument must be a frame."); // duplicate columns and keys var columns = shallowcopy(this._cols), keys = shallowcopy(this._keys) || {}; // add virtual columns for each column in the joining frame for(name in frame._cols){ // skip columns with duplicate names if(name in columns) continue; // don't join encoded columns if(frame._keys && name in frame._keys) continue; // add link column as encoded column data columns[name] = link; // add joining frame column as key column keys[name] = frame._cols[name]; } return new Frame(columns, keys, this._index, this._groups, this._filters); } /* * group the data in the frame by a selector or set of selectors */ function groupby(){ if(arguments.length == 0) throw new Error("No arguments provided"); // collect arguments into list of selectors var selectors = [], arg; if(arguments.length === 1){ arg = arguments[0]; if(isstring(arg)) selectors = [arg]; else if(isarray(arg)) selectors = arg; } else { for(var i = 0; i < arguments.length; i++){ arg = arguments[i]; if(!isstring(arg)) throw new Error("Invalid arguments"); selectors.push(arg); } } var index = {}; if(this._index){ index = this._index; selectors = this._groups.concat(selectors); } // get references to all the columns involved in groups var columns = Array(selectors.length); var keys = {}; for (var m = 0; m < selectors.length; m++){ selector = selectors[m]; if(!(selector in this._cols)) throw new Error("Couldn't find a column named '" + selector + "'"); columns[m] = this._cols[selector]; if(this._keys && selector in this._keys) keys[m] = this._keys[selector]; } var N = columns[0].length; var path = Array(columns.length); // iterate through rows for(var i = 0; i < N; i++){ // compute distinct values for group columns describing the bin for // the current row for (var m = 0; m < columns.length; m++){ var column = columns[m]; if(m in keys) path[m] = keys[m][column[i]]; else path[m] = column[i]; } // add this row to the index using the group column values // by descending the hierarchy to the correct leaf var level = index; for(var j = 0; j < path.length - 1; j++){ key = path[j]; next = level[key]; if(next == null || isarray(next)){ next = {}; level[key] = next; } level = next; } // update array of row indices stored in leaf key = path[path.length - 1]; var arr = level[key]; if(arr == null){ level[key] = [i]; } else { arr[arr.length] = i; } } /* this._index = index; this._groups = selectors.slice(0); return this; */ return new Frame(this._cols, this._keys, index, selectors, this._filters); } /* remove the grouping created by the last remaining groupby selector */ function ungroup(){ if(this._index == null || this._groups.length < 1) throw new Error("Not enough groups") var frame = new Frame(this._cols, this._keys, null, null, this._filters); // handle special case of single group if(this._groups.length == 1) return frame; // for other cases do new groupby with one fewer groups return frame.groupby(this._groups.slice(0, -1)); } function count(){ if(this._index) return this.reduce(); if(this._filter) return this._count; return this.length; } function min(selector){ return this.reduce(selector, reducers.min); } function max(selector){ return this.reduce(selector, reducers.max); } function sum(selector){ return this.reduce(selector, reducers.sum); } function mean(selector){ return this.reduce(selector, reducers.mean); } function argmax(selector){ return this.reduce(selector, reducers.argmax); } function argmin(selector){ return this.reduce(selector, reducers.argmin); } function reduce(selector, reducer, initial){ var column = selector ? this._cols[selector] : null; var key = selector && this._keys ? this._keys[selector] : null; // choose default reduce, if none was supplied var is_numeric = column && column.length > 0 && Object.prototype.toString.call(column[0]) == "[object Number]"; reducer = reducer || (is_numeric ? reducers.sum : reducers.max); if(this._index){ return treereduce(column, key, this._index, this._keys, this._groups, this._filter, reducer, initial); } else if(this._filter) { return filterreduce(column, key, this._filter, reducer, initial); } else { return fullreduce(column, key, reducer, initial); } } function treereduce(column, rkey, index, keys, groups, filter, reducer, initial){ var reduced = {}; var parents = {}; // depth first traversal var todo = [[index, null, 0]]; var leaves = []; var result, pkey, level, n; while (todo.length > 0){ n = todo.pop();// object index = n[0]; pkey = n[1]; level = n[2]; result = {}; // container for this subtree in result var c, name; for(key in index){ // keys in object c = index[key]; group = groups[level]; // decode the key, if possible /* if(keys && group in keys){ decoder = keys[group]; key = decoder[key]; }*/ ckey = pkey ? pkey + "@" + key : key; if(isobject(c)){ todo.push([c, ckey, level + 1]); } else { var indices = c; var filtered = filterindices(indices, filter); if(filtered.length != 0){ var value; if(column){ value = subsetreduce(column, rkey, filtered, reducer, initial); } else { value = filtered.length; // default to count } leaves.push([ckey, value]); } } parents[ckey] = [pkey, result]; } } var root; while (leaves.length > 0){ n = leaves.pop(); ckey = n[0]; // composite key, parent + child value = n[1]; p = parents[ckey]; pkey = p[0]; index = p[1]; key = pkey ? ckey.slice(pkey.length + 1) : ckey; index[key] = value; if(pkey == null){ root = index; } else { leaves.push([pkey, index]); } } return root; }; function empty (obj){ for (var key in obj) { if (obj.hasOwnProperty(key)) { return false } } return true } function filterindices(indices, filter){ if(!filter) return indices; result = []; for(var i = 0; i < indices.length; i++){ index = indices[i]; if(filter.get(index)){ result.push(index); } } return result; } /* reduce a subset of an array given by a set of indices using a supplied reducing function. Extracting this code into a function produces an order of magnitude speedup. I don't know why. */ function subsetreduce(column, key, indices, reducer, initial){ var value = null; if(initial) value = initial; if(key){ for(var i = 0; i < indices.length; i++){ index = indices[i]; if(value === null) value = key[column[index]]; else value = reducer(value, key[column[index]], i); } } else { for(var i = 0; i < indices.length; i++){ index = indices[i]; if(value === null) value = column[index]; else value = reducer(value, column[index], i); } } return value || 0; } function filterreduce(column, key, filter, reducer, initial){ var value = null; if(initial) value = initial; var word, mask, cutoff; var bits = filter.wordArray; var total = 0; var max = column.length; if(key){ for(var i = 0; i < bits.length; i++){ word = bits[i]; if(word !== 0){ cutoff = (i + 1) * 32; if(cutoff > max) cutoff = max; mask = 1; for(var j = i * 32; j < cutoff; j++){ if((word & mask) !== 0) { if(value === null) value = key[column[j]]; else value = reducer(value, key[column[j]], total); total++; } mask <<= 1; } } } } else { for(var i = 0; i < bits.length; i++){ word = bits[i]; if(word !== 0){ cutoff = (i + 1) * 32; if(cutoff > max) cutoff = max; mask = 1; for(var j = i * 32; j < cutoff; j++){ if((word & mask) !== 0) { if(value === null) value = column[j]; else value = reducer(value, column[j], total); total++; } mask <<= 1; } } } } return value || 0; } function fullreduce(column, key, reducer, initial){ var start, value; // chose initial values and start of loop based on number of inputs and // supplied initial value if(initial !== void(0)){ start = 0; value = initial; } else if(column.length > 0) { start = 1; value = key ? key[column[0]] : column[0]; } else { start = 0; value = 0; } if(key){ for(var i = start; i < column.length; i++){ value = reducer(value, key[column[i]], i); } } else { for(var i = start; i < column.length; i++){ value = reducer(value, column[i], i); } } return value; } },{"./stream-reducers":2,"bit-array":3}],2:[function(require,module,exports){ module.exports = { "count" : count, "sum" : sum, "max" : max, "min" : min, "mean" : mean, "mode" : mode, "median" : median, "argmax" : argmax, "argmin" : argmin }; /* Array.prototype.reduce style function for finding the maximum * @examples * [1, 1, 1].reduce(ds.reduce.max); // => 1 * [3, 1, 3, 5].reduce(ds.reduce.max); // => 5 * reduce({"a" : 1, "b" : 0, "c" : 2}, ds.reduce.max); // => 2 */ function max(agg, val) { return agg > val ? agg : val; }; /* Array.prototype.reduce style function for finding the minimum * @examples * [1, 1, 1].reduce(ds.reduce.min); // => 1 * [3, 1, 3, 5].reduce(ds.reduce.min); // => 1 * reduce({"a" : 1, "b" : 0, "c" : 2}, ds.reduce.min); // => 0 */ function min(agg, val) { return agg < val ? agg : val; }; /* Array.prototype.reduce style function for finding the most common value * @examples * [1, 1, 1].reduce(ds.reduce.mode); // => 1 * [1, 3, 3, 7].reduce(ds.reduce.mode); // => 3 * reduce({"a" : 1, "b" : 0, "c" : 2}, ds.reduce.mode); // => 1 */ function mode(agg, val, n) { if(n === 0) return val; var self; if(n === 1){ // internal state hack (compatible with groupby) self = mode.state = {}; self.values = {}; self.values[agg] = 1; self.argmax = agg; } else { self = mode.state; } if(val in self.values) self.values[val] += 1; else self.values[val] = 1; if(self.values[val] > self.values[agg]) self.argmax = val; return self.argmax; } function argmax(agg, val, n){ var self; if(n === 0){ // internal state hack (compatible with groupby) self = argmax.state = {}; self.max = val; return 0; } if(n === 1){ if(argmax.state == null) self = argmax.state = {}; else self = argmax.state; // is this the first time we've called this function on this array? if(self.max != null && self.argmax == null){ // no } else { // yes self.max = agg; } self.argmax = 0; } else { self = argmax.state; } if(val > self.max){ self.max = val; self.argmax = n; } return self.argmax; } function argmin(agg, val, n){ var self; if(n === 0){ // internal state hack (compatible with groupby) self = argmin.state = {}; self.min = val; return 0; } if(n === 1){ if(argmin.state == null) self = argmin.state = {}; else self = argmin.state; // is this the first time we've called this function on this array? if(self.min != null && self.argmin == null){ // no } else { // yes self.min = agg; } self.argmin = 0; } else { self = argmin.state; } if(val < self.min){ self.min = val; self.argmin = n; } return self.argmin; } /* Array.prototype.reduce style function for finding the middle value * @examples * [1, 1, 1].reduce(ds.reduce.median); // => 1 * [1, 3, 3, 7].reduce(ds.reduce.median); // => 3 * [4, 1, 7].reduce(ds.reduce.median); // => 4 * reduce({"a" : 4, "b" : 1, "c" : 7}, ds.reduce.median); // => 4 DON'T USE THIS FUNCTION, IT'S VERY SLOW */ function median(agg, val, n) { if(n === 0) return val; if(n === 1){ // internal state hack (compatible with groupby) self = median.state = {}; self.values = [agg]; } else { self = median.state; } // insert the new value into the sorted array insert(self.values, val); var middle = self.values.length / 2 | 0; // even number of elements? if(self.values.length % 2 !== 0){ // no, return the middle one return self.values[middle]; } else { // yes, return the average of the middle two return (self.values[middle - 1] + self.values[middle]) / 2; } } /* Array.prototype.reduce style function for counting number of elements * @examples * [1, 1, 1].reduce(ds.reduce.count); // => 3 * [3, 1, 3, 5].reduce(ds.reduce.count); // => 4 * reduce({"a" : 1, "b" : 0, "c" : 2}, ds.reduce.count); // => 3 */ function count(agg, val, n){ return n + 1; }; /* Array.prototype.reduce style function for finding the sum * @examples * [1, 1, 1].reduce(ds.reduce.sum); // => 3 * [3, 1, 3, 5].reduce(ds.reduce.sum); // => 12 * reduce({"a" : 1, "b" : 0, "c" : 2}, ds.reduce.sum); // => 3 */ function sum(agg, val){ return agg + val; }; /* Array.prototype.reduce style function for finding the arithmetic mean * @examples * [1, 1, 1].reduce(ds.reduce.mean); // => 1 * [3, 1, 3, 5].reduce(ds.reduce.mean); // => 3 * reduce({"a" : 1, "b" : 0, "c" : 2}, ds.reduce.mean); // => 1 */ function mean(agg, val, n){ return (agg + ((val - agg)/(n + 1))); }; var d = function(a, b){ return a > b ? 1 : a < b ? -1 : 0;}; function insert(arr, el){ var index = binarySearch(arr, el, d); arr.splice(index, 0, el); return arr; }; var binarySearch = function binarySearch(arr, el, comparator) { var m = 0; var n = arr.length - 1; while (m <= n) { var k = (n + m) >> 1; var cmp = comparator(el, arr[k]); // comparator(arr[k], el); if (cmp > 0) { m = k + 1; } else if(cmp < 0) { n = k - 1; } else { return k; } } return m; } },{}],3:[function(require,module,exports){ /** * JavaScript BitArray - v0.2.0 * * Licensed under the revised BSD License. * Copyright 2010-2012 Bram Stein * All rights reserved. */ /** * Creates a new empty BitArray with the given length or initialises the BitArray with the given hex representation. */ var BitArray = function (size, hex) { this.length = size; this.buffer = new ArrayBuffer(Math.ceil(this.length / 32) * 4); this.wordArray = new Uint32Array(this.buffer); if (hex) { hex = hex.slice(/^0x/.exec(hex) ? 2 : 0); if (hex.length * 4 > this.length) { throw 'Hex value is too large for this bit array.' } else if (hex.length * 4 < this.length) { // pad it while(hex.length * 4 < this.length) { hex = '0' + hex; } } for (var i = 0; i < (hex.length / 8); i++) { var slice = hex.slice(i * 8, i * 8 + 8); this.wordArray[i] = parseInt(slice, 16); } } }; /** * Returns the total number of bits in this BitArray. */ BitArray.prototype.size = function() { return this.length; }; /** * Sets the bit at index to a value (boolean.) */ BitArray.prototype.set = function(index, value) { if (arguments.length !== 2) { throw 'Index and value are required arguments.'; } if (index > this.length - 1) { throw 'Index too large.' + index + ' ' + this.length; } var wordOffset = Math.floor(index / 32); // The underlying byte buffer will be initialized to zeros. var bitOffset = index - wordOffset * 32; if (value) { this.wordArray[wordOffset] |= (1 << bitOffset); } else { this.wordArray[wordOffset] &= ~(1 << bitOffset); } return this; }; /** * Toggles the bit at index. If the bit is on, it is turned off. Likewise, if the bit is off it is turned on. */ BitArray.prototype.toggle = function(index) { if (index > this.length - 1) { throw 'Index too large.'; } var wordOffset = Math.floor(index / 32); var bitOffset = index - wordOffset * 32; this.wordArray[wordOffset] ^= 1 << bitOffset; return this; }; /** * Returns the value of the bit at index (boolean.) */ BitArray.prototype.get = function(index) { if (index > this.length - 1) { throw 'Index too large.'; } var wordOffset = Math.floor(index / 32); var bitOffset = index - wordOffset * 32; return !! (this.wordArray[wordOffset] & (1 << bitOffset)); }; /** * Resets the BitArray so that it is empty and can be re-used. */ BitArray.prototype.reset = function() { this.buffer = new ArrayBuffer(Math.ceil(this.length / 32) * 4); this.wordArray = new Uint32Array(this.buffer); return this; }; /** * Returns a copy of this BitArray. */ BitArray.prototype.copy = function() { var cp = new BitArray(this.length); for (var i = 0; i < this.wordArray.length; i++) { cp.wordArray[i] = this.wordArray[i]; } return cp; }; /** * Returns true if this BitArray equals another. Two BitArrays are considered * equal if both have the same length and bit pattern. */ BitArray.prototype.equals = function(x) { if (this.length !== x.length) { return false; } for (var i = 0; i < this.wordArray.length; i++) { if (this.wordArray[i] !== x.wordArray[i]) { return false; } } return true; }; /** * Returns the JSON representation of this BitArray. */ BitArray.prototype.toJSON = function() { return JSON.stringify(this.toArray()); }; /** * Returns a string representation of the BitArray with bits * in mathemetical order. */ BitArray.prototype.toBinaryString = function () { return this.toArray().map(function (value) { return value ? '1' : '0'; }).reverse().join(''); }; /** * Returns a hexadecimal string representation of the BitArray * with bits in logical order. */ BitArray.prototype.toHexString = function () { var result = []; for (var i = 0; i < this.wordArray.length; i += 1) { //result.push(this.wordArray[i].toString(16)); result.push(('00000000' + (this.wordArray[i] >>> 0).toString(16)).slice(-8)); } return result.join(''); }; /** * Returns a string representation of the BitArray with bits * in logical order. */ BitArray.prototype.toString = function() { return this.toArray().map(function(value) { return value ? '1': '0'; }).join(''); }; /** * Convert the BitArray to an Array of boolean values (slow). */ BitArray.prototype.toArray = function() { var result = []; for (var i = 0; i < this.length; i++) { result.push(Boolean(this.get(i))); } return result; }; /** * Returns the total number of bits set to one in this BitArray. */ BitArray.prototype.count = function() { var total = 0; for (var i = 0; i < this.wordArray.length; i++) { x = this.wordArray[i]; // count bits of each 2-bit chunk x = x - ((x >> 1) & 0x55555555); // count bits of each 4-bit chunk x = (x & 0x33333333) + ((x >> 2) & 0x33333333); // count bits of each 8-bit chunk x = x + (x >> 4); // mask out junk x &= 0xF0F0F0F; // add all four 8-bit chunks total += (x * 0x01010101) >> 24; } return total; }; /** * Inverts this BitArray. */ BitArray.prototype.not = function() { for (var i = 0; i < this.wordArray.length; i++) { this.wordArray[i] = ~(this.wordArray[i]); } return this; }; /** * Bitwise OR on the values of this BitArray using BitArray x. */ BitArray.prototype.or = function(x) { if (this.length !== x.length) { throw 'Arguments must be of the same length.'; } for (var i = 0; i < this.wordArray.length; i++) { this.wordArray[i] |= x.wordArray[i]; } return this; }; /** * Bitwise AND on the values of this BitArray using BitArray x. */ BitArray.prototype.and = function(x) { if (this.length !== x.length) { throw 'Arguments must be of the same length.'; } for (var i = 0; i < this.wordArray.length; i++) { this.wordArray[i] &= x.wordArray[i]; } return this; }; /** * Bitwise XOR on the values of this BitArray using BitArray x. */ BitArray.prototype.xor = function(x) { if (this.length !== x.length) { throw 'Arguments must be of the same length.'; } for (var i = 0; i < this.wordArray.length; i++) { this.wordArray[i] ^= x.wordArray[i]; } return this; }; module.exports = BitArray; },{}]},{},[1])(1) });