diff --git a/.flowconfig b/.flowconfig index 404d39b..4010e54 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,7 +1,7 @@ [options] unsafe.enable_getters_and_setters=true suppress_comment=.*\\$FlowIssue -module.system.node.resolve_dirname=src +module.system.node.resolve_dirname=es6 module.system.node.resolve_dirname=node_modules [ignore] diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2d4c7..6aec1de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +0.2.0 +### Added +- `concat` to concatenate multiple `DataFrame`s or `Series` +- `append` for `DataFrame` +- `transpose` for DataFrame` + 0.1.28 ### Added - `rename` for `DataFrame` and `Series` diff --git a/dist/__tests__/core/frame.js b/dist/__tests__/core/frame.js index d618f62..0ad975f 100644 --- a/dist/__tests__/core/frame.js +++ b/dist/__tests__/core/frame.js @@ -978,13 +978,6 @@ describe('frame', function () { }); }); - describe('pivot_table', function () { - it('pivots', function () { - var df = new _frame2.default([{ a: 1, b: 1, c: 1, d: 3 }, { a: 1, b: 1, c: 2, d: 8 }, { a: 1, b: 2, c: 1, d: 9 }, { a: 1, b: 2, c: 2, d: 10 }, { a: 2, b: 1, c: 1, d: 1 }, { a: 2, b: 1, c: 2, d: 4 }, { a: 2, b: 2, c: 1, d: 1 }, { a: 2, b: 2, c: 2, d: 3 }, { a: 2, b: 2, c: 2, d: 3 }]); - - console.log(df.pivot_table(['a', 'b'], 'c', 'd')); - }); - }); describe('rename', function () { it('renames one Series in the DataFrame', function () { @@ -1013,4 +1006,52 @@ describe('frame', function () { expect(df.length).toEqual(5); }); }); + + describe('append', function () { + it('Appends a DataFrame to another when ignore_index is false', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = new _frame2.default([{ x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 4 }], { index: [2, 3, 4] }); + + var df3 = df1.append(df2); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4, 2, 3, 4]); + expect(df3.index.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + }); + + it('Appends a DataFrame to another when ignore_index is true', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = new _frame2.default([{ x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 4 }], { index: [2, 3, 4] }); + + var df3 = df1.append(df2, true); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4, 2, 3, 4]); + expect(df3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('Appends an empty DataFrame to another', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = new _frame2.default([]); + + var df3 = df1.append(df2); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4]); + expect(df3.index.toArray()).toEqual([1, 2, 3]); + }); + }); + + describe('transpose', function () { + it('Tranposes a DataFrame by flipping indexes and columns', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = df1.transpose(); + + expect(df2.columns.toArray()).toEqual([1, 2, 3]); + expect(df2.index.toArray()).toEqual(['x', 'y']); + expect(df2.get(1).index.toArray()).toEqual(['x', 'y']); + + var df3 = df2.transpose(); + expect(df3.columns.toArray()).toEqual(['x', 'y']); + expect(df3.index.toArray()).toEqual([1, 2, 3]); + expect(df3.get('x').index.toArray()).toEqual([1, 2, 3]); + }); + }); }); \ No newline at end of file diff --git a/dist/__tests__/core/reshape/concat.js b/dist/__tests__/core/reshape/concat.js new file mode 100644 index 0000000..411b7ac --- /dev/null +++ b/dist/__tests__/core/reshape/concat.js @@ -0,0 +1,86 @@ +'use strict'; + +var _immutable = require('immutable'); + +var _immutable2 = _interopRequireDefault(_immutable); + +var _frame = require('../../../core/frame'); + +var _frame2 = _interopRequireDefault(_frame); + +var _series = require('../../../core/series'); + +var _series2 = _interopRequireDefault(_series); + +var _concat = require('../../../core/reshape/concat'); + +var _concat2 = _interopRequireDefault(_concat); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +describe('concat', function () { + describe('concat Series', function () { + it('Concatenates two Series without ignoring index', function () { + var series1 = new _series2.default([1, 2, 3, 4]); + var series2 = new _series2.default([2, 3, 4, 5]); + + var series3 = (0, _concat2.default)([series1, series2]); + expect(series3.values.toArray()).toEqual([1, 2, 3, 4, 2, 3, 4, 5]); + expect(series3.index.toArray()).toEqual([0, 1, 2, 3, 0, 1, 2, 3]); + }); + + it('Concatenates two Series with ignore index', function () { + var series1 = new _series2.default([1, 2, 3, 4]); + var series2 = new _series2.default([2, 3, 4, 5]); + + var series3 = (0, _concat2.default)([series1, series2], { ignore_index: true }); + expect(series3.values.toArray()).toEqual([1, 2, 3, 4, 2, 3, 4, 5]); + expect(series3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + + it('Throws an error if the first object in concat is a Series ' + 'and any of the rest are not', function () { + var series1 = new _series2.default([1, 2, 3, 4]); + var series2 = new _series2.default([2, 3, 4, 5]); + + expect(function () { + return (0, _concat2.default)([series1, series2, []], { ignore_index: true }); + }).toThrow(); + expect(function () { + return (0, _concat2.default)(_immutable2.default.List([series1, series2, []]), { ignore_index: true }); + }).toThrow(); + }); + }); + + describe('concat DataFrame', function () { + it('Concatenates two DataFrames without ignoring index', function () { + var frame1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }]); + var frame2 = new _frame2.default([{ x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }]); + + var frame3 = (0, _concat2.default)([frame1, frame2]); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(frame3.get('x').index.toArray()).toEqual([0, 1, 2, 0, 1, 2]); + }); + + it('Concatenates two DataFrames with index ignored', function () { + var frame1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }]); + var frame2 = new _frame2.default([{ x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }]); + + var frame3 = (0, _concat2.default)([frame1, frame2], { ignore_index: true }); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(frame3.get('x').index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('Concatenates two DataFrames along axis = 1 without ignoring index', function () { + var frame1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }]); + var frame2 = new _frame2.default([{ x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }]); + + var frame3 = (0, _concat2.default)([frame1, frame2], { axis: 1 }); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3]); + expect(frame3.get('y').values.toArray()).toEqual([2, 3, 4]); + expect(frame3.get('x.x').values.toArray()).toEqual([2, 3, 4]); + expect(frame3.get('x.x').name).toEqual('x.x'); + expect(frame3.get('y.x').values.toArray()).toEqual([3, 4, 5]); + expect(frame3.get('y.x').name).toEqual('y.x'); + }); + }); +}); \ No newline at end of file diff --git a/dist/__tests__/core/series.js b/dist/__tests__/core/series.js index 2695249..2a0cdd9 100644 --- a/dist/__tests__/core/series.js +++ b/dist/__tests__/core/series.js @@ -658,4 +658,22 @@ describe('series', function () { }); }); }); + + describe('append', function () { + it('Appends a Series to another when ignore_index is false', function () { + var ds1 = new _series2.default([1, 2, 3], { index: [1, 2, 3] }); + var ds2 = new _series2.default([2, 3, 4], { index: [2, 3, 4] }); + var ds3 = ds1.append(ds2); + expect(ds3.values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(ds3.index.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + }); + + it('Appends a Series to another when ignore_index is true', function () { + var ds1 = new _series2.default([1, 2, 3], { index: [1, 2, 3] }); + var ds2 = new _series2.default([2, 3, 4], { index: [2, 3, 4] }); + var ds3 = ds1.append(ds2, true); + expect(ds3.values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(ds3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + }); }); \ No newline at end of file diff --git a/dist/core/frame.js b/dist/core/frame.js index 789b88e..2766912 100644 --- a/dist/core/frame.js +++ b/dist/core/frame.js @@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.mergeDataFrame = undefined; +exports._concatDataFrame = exports.mergeDataFrame = undefined; var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); @@ -55,6 +55,10 @@ var _series2 = _interopRequireDefault(_series); var _utils = require('./utils'); +var _concat = require('./reshape/concat'); + +var _concat2 = _interopRequireDefault(_concat); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var parseArrayToSeriesMap = function parseArrayToSeriesMap(array, index) { @@ -105,6 +109,7 @@ var DataFrame = function (_NDFrame) { if (data instanceof _immutable2.default.Map && !(data.get(k) instanceof _series2.default)) throw new Error('Map must have [column, series] key-value pairs'); if (data instanceof _immutable2.default.Map) return [k, data.get(k).copy()]; + throw new Error('Data is not Map'); })); _this.set_axis(1, _this._data.keySeq()); @@ -553,19 +558,13 @@ var DataFrame = function (_NDFrame) { return _this12.get(k).variance(); }), { index: this.columns.toArray() }); } else if (axis === 1) { - var _ret = function () { - var means = _this12.mean(axis).values; - return { - v: new _series2.default(_immutable2.default.Range(0, _this12.length).map(function (idx) { - return _this12.values.get(idx).reduce(function (s, k) { - var diff = k - means.get(idx); - return s + diff * diff / (_this12.columns.size - 1); - }, 0); - }).toArray(), { index: _this12.index }) - }; - }(); - - if ((typeof _ret === 'undefined' ? 'undefined' : (0, _typeof3.default)(_ret)) === "object") return _ret.v; + var means = this.mean(axis).values; + return new _series2.default(_immutable2.default.Range(0, this.length).map(function (idx) { + return _this12.values.get(idx).reduce(function (s, k) { + var diff = k - means.get(idx); + return s + diff * diff / (_this12.columns.size - 1); + }, 0); + }).toArray(), { index: this.index }); } throw new _exceptions.InvalidAxisError(); @@ -715,92 +714,28 @@ var DataFrame = function (_NDFrame) { }, { key: 'pivot_table', value: function pivot_table(index, columns, values) { - var _this16 = this; - var aggfunc = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'sum'; throw new Error('Not implemented'); - var validateCols = function validateCols(cols) { - if (Array.isArray(cols)) { - cols.forEach(function (c) { - return _this16._assertColumnExists(c); - }); - return _immutable2.default.List(cols); - } else if (cols instanceof _immutable2.default.List) { - cols.forEach(function (c) { - return _this16._assertColumnExists(c); - }); - return cols; - } else if (typeof cols === 'string') { - _this16._assertColumnExists(cols); - return _immutable2.default.List.of(cols); - } - - throw new TypeError('cols must be Array, Immutable.List, or string'); - }; - - var indexCols = validateCols(index); - var columnCols = validateCols(columns); - var valuesCols = validateCols(values); - - var pivotMap = _immutable2.default.Map({}); - - this.index.map(function (indexVal, idx) { - var key = indexCols.map(function (c) { - return _this16.get(c).iloc(idx); - }).concat(columnCols.map(function (c) { - return _this16.get(c).iloc(idx); - })); - var val = _this16.get(valuesCols.get(0)).iloc(idx); - if (pivotMap.has(key)) { - switch (aggfunc) { - case 'sum': - val += pivotMap.get(key); - break; - default: - throw new Error('not implemented for aggs'); - } - } - - pivotMap = pivotMap.set(key, val); - }); - - var indexMap = _immutable2.default.OrderedMap({}); - var columnsMap = _immutable2.default.OrderedMap({}); - - pivotMap.entrySeq().forEach(function (_ref9) { - var _ref10 = (0, _slicedToArray3.default)(_ref9, 2), - k = _ref10[0], - v = _ref10[1]; - - var indexKey = k.slice(0, indexCols.size - 1); - console.log(k); - console.log(indexKey); - if (indexMap.hasIn(indexKey)) indexMap = indexMap.setIn(indexKey, indexMap.getIn(indexKey).concat([k[indexCols.size - 1]]));else indexMap = indexMap.setIn(indexKey, _immutable2.default.List.of(k[indexCols.size - 1])); - columnsMap = columnsMap.setIn(k.slice(indexCols.size, k.length)); - }); - - console.log(indexMap); - console.log(columnsMap); - return pivotMap; } }, { key: '_cumulativeHelper', value: function _cumulativeHelper() { - var _this17 = this; + var _this16 = this; var operation = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _utils.OP_CUMSUM; var axis = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; if (axis === 0) { return new DataFrame(_immutable2.default.Map(this.columns.map(function (c) { - return [c, _this17.get(c)._cumulativeHelper(operation)]; + return [c, _this16.get(c)._cumulativeHelper(operation)]; })), this.kwargs); } else if (axis === 1) { return new DataFrame(this.values.map(function (row) { return (0, _utils.generateCumulativeFunc)(operation)(row); }), this.kwargs); - } else throw new Error('invalid axis'); + } + throw new Error('invalid axis'); } }, { key: 'cumsum', @@ -832,17 +767,33 @@ var DataFrame = function (_NDFrame) { } }, { key: 'rename', - value: function rename(_ref11) { - var _this18 = this; + value: function rename(_ref9) { + var _this17 = this; - var columns = _ref11.columns; + var columns = _ref9.columns; return new DataFrame(_immutable2.default.OrderedMap(this.columns.map(function (prevCol) { var nextCol = columns.get(prevCol); - if (typeof nextCol === 'undefined') return [prevCol, _this18._data.get(prevCol)]; - return [nextCol, _this18._data.get(prevCol).rename(nextCol)]; + if (typeof nextCol === 'undefined') return [prevCol, _this17._data.get(prevCol)]; + return [nextCol, _this17._data.get(prevCol).rename(nextCol)]; })), { index: this.index }); } + }, { + key: 'append', + value: function append(other) { + var ignore_index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + return _concatDataFrame([this, other], { ignore_index: ignore_index }); + } + }, { + key: 'transpose', + value: function transpose() { + var _this18 = this; + + return new DataFrame(_immutable2.default.OrderedMap(this.index.map(function (index, idx) { + return [index, new _series2.default(_this18.values.get(idx), { index: _this18.columns.toList() })]; + }))); + } }, { key: 'kwargs', get: function get() { @@ -901,10 +852,10 @@ var DataFrame = function (_NDFrame) { this.set_axis(0, (0, _utils.parseIndex)(index, this._data.get(this.columns.get(0)).values)); - this._data.mapEntries(function (_ref12) { - var _ref13 = (0, _slicedToArray3.default)(_ref12, 2), - k = _ref13[0], - v = _ref13[1]; + this._data.mapEntries(function (_ref10) { + var _ref11 = (0, _slicedToArray3.default)(_ref10, 2), + k = _ref11[0], + v = _ref11[1]; v.index = _this21.index; }); @@ -962,8 +913,7 @@ var innerMerge = function innerMerge(df1, df2, on) { row2 = _step2$value[0], _2 = _step2$value[1]; - var match = true; - var _iteratorNormalCompletion3 = true; + var match = true;var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; @@ -992,23 +942,21 @@ var innerMerge = function innerMerge(df1, df2, on) { } if (match) { - (function () { - var rowData = {}; + var rowData = {}; - on.forEach(function (k) { - rowData[k] = row1.get(k); - }); + on.forEach(function (k) { + rowData[k] = row1.get(k); + }); - cols1.forEach(function (k, idx) { - rowData[cols1Rename.get(idx)] = row1.get(k); - }); + cols1.forEach(function (k, idx) { + rowData[cols1Rename.get(idx)] = row1.get(k); + }); - cols2.forEach(function (k, idx) { - rowData[cols2Rename.get(idx)] = row2.get(k); - }); + cols2.forEach(function (k, idx) { + rowData[cols2Rename.get(idx)] = row2.get(k); + }); - data.push(rowData); - })(); + data.push(rowData); } }; @@ -1084,8 +1032,7 @@ var outerMerge = function outerMerge(df1, df2, on) { row2 = _step5$value[0], idx_2 = _step5$value[1]; - var match = true; - var _iteratorNormalCompletion6 = true; + var match = true;var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; @@ -1174,45 +1121,41 @@ var outerMerge = function outerMerge(df1, df2, on) { matched1.forEach(function (m, idx) { if (!m) { - (function () { - var rowData = {}; - on.forEach(function (k) { - rowData[k] = df1.get(k).iloc(idx); - }); + var rowData = {}; + on.forEach(function (k) { + rowData[k] = df1.get(k).iloc(idx); + }); - cols1.forEach(function (k) { - var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_x' : k; - rowData[nextColName] = df1.get(k).iloc(idx); - }); + cols1.forEach(function (k) { + var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_x' : k; + rowData[nextColName] = df1.get(k).iloc(idx); + }); - cols2.forEach(function (k) { - var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_y' : k; - rowData[nextColName] = null; - }); - data.push(rowData); - })(); + cols2.forEach(function (k) { + var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_y' : k; + rowData[nextColName] = null; + }); + data.push(rowData); } }); matched2.forEach(function (m, idx) { if (!m) { - (function () { - var rowData = {}; - on.forEach(function (k) { - rowData[k] = df2.get(k).iloc(idx); - }); + var rowData = {}; + on.forEach(function (k) { + rowData[k] = df2.get(k).iloc(idx); + }); - cols1.forEach(function (k) { - var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_x' : k; - rowData[nextColName] = null; - }); + cols1.forEach(function (k) { + var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_x' : k; + rowData[nextColName] = null; + }); - cols2.forEach(function (k) { - var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_y' : k; - rowData[nextColName] = df2.get(k).iloc(idx); - }); - data.push(rowData); - })(); + cols2.forEach(function (k) { + var nextColName = intersectCols.size > 0 && intersectCols.indexOf(k) >= 0 ? k + '_y' : k; + rowData[nextColName] = df2.get(k).iloc(idx); + }); + data.push(rowData); } }); @@ -1245,4 +1188,46 @@ var mergeDataFrame = exports.mergeDataFrame = function mergeDataFrame(df1, df2, default: throw new Error('MergeError: ' + how + ' not a supported merge type'); } +}; + +var _concatDataFrame = exports._concatDataFrame = function _concatDataFrame(objs, kwargs) { + if (!(objs instanceof _immutable2.default.List || Array.isArray(objs))) throw new Error('objs must be List or Array'); + + if (objs instanceof _immutable2.default.List && objs.filter(function (frame) { + return frame instanceof DataFrame; + }).size !== objs.size) throw new Error('Objects must all be DataFrame');else if (Array.isArray(objs) && objs.filter(function (frame) { + return frame instanceof DataFrame; + }).length !== objs.length) throw new Error('Objects must all be DataFrame'); + + if (Array.isArray(objs) && objs.length === 1) return objs[0];else if (objs instanceof _immutable2.default.List && objs.size === 1) return objs.get(0); + + var seriesOrderedMap = _immutable2.default.OrderedMap({}); + if (kwargs.axis === 1) { + objs.forEach(function (df) { + df.columns.forEach(function (column) { + var columnExists = seriesOrderedMap.has(column); + seriesOrderedMap = seriesOrderedMap.set(columnExists ? column + '.x' : column, columnExists ? df.get(column).rename(column + '.x') : df.get(column)); + }); + }); + } else { + objs.forEach(function (df) { + var lenSeriesInMap = seriesOrderedMap.keySeq().size === 0 ? 0 : seriesOrderedMap.first().length; + var nextLength = df.length + lenSeriesInMap; + + seriesOrderedMap = _immutable2.default.OrderedMap(seriesOrderedMap.entrySeq().map(function (_ref12) { + var _ref13 = (0, _slicedToArray3.default)(_ref12, 2), + column = _ref13[0], + series = _ref13[1]; + + if (df.columnExists(column)) return [column, (0, _series._concatSeries)([series, df.get(column)], kwargs)]; + return [column, (0, _series._concatSeries)([series, new _series2.default(_immutable2.default.Repeat(NaN, df.length).toList(), { index: df.index })], kwargs)]; + })).merge(_immutable2.default.OrderedMap(df.columns.filter(function (column) { + return !seriesOrderedMap.has(column); + }).map(function (column) { + return [column, lenSeriesInMap === 0 ? df.get(column) : (0, _series._concatSeries)([new _series2.default(_immutable2.default.Repeat(NaN, nextLength)), df.get(column)], kwargs)]; + }))); + }); + } + + return new DataFrame(seriesOrderedMap); }; \ No newline at end of file diff --git a/dist/core/index.js b/dist/core/index.js index 7295a1f..da5485f 100644 --- a/dist/core/index.js +++ b/dist/core/index.js @@ -22,4 +22,13 @@ Object.defineProperty(exports, 'DataFrame', { } }); +var _concat = require('./reshape/concat'); + +Object.defineProperty(exports, 'concat', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_concat).default; + } +}); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file diff --git a/dist/core/reshape/concat.js b/dist/core/reshape/concat.js new file mode 100644 index 0000000..f7dae02 --- /dev/null +++ b/dist/core/reshape/concat.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _immutable = require('immutable'); + +var _immutable2 = _interopRequireDefault(_immutable); + +var _frame = require('../frame'); + +var _frame2 = _interopRequireDefault(_frame); + +var _series = require('../series'); + +var _series2 = _interopRequireDefault(_series); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var concat = function concat(objs) { + var kwargs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { ignore_index: false, axis: 0 }; + + if (Array.isArray(objs) && objs[0] instanceof _series2.default || objs instanceof _immutable2.default.List && objs.get(0) instanceof _series2.default) return (0, _series._concatSeries)(objs, { ignore_index: kwargs.ignore_index });else if (Array.isArray(objs) && objs[0] instanceof _frame2.default || objs instanceof _immutable2.default.List && objs.get(0) instanceof _frame2.default) return (0, _frame._concatDataFrame)(objs, { ignore_index: kwargs.ignore_index, axis: kwargs.axis }); + throw new Error('Not supported'); +}; + +exports.default = concat; \ No newline at end of file diff --git a/dist/core/series.js b/dist/core/series.js index 94fb26b..2e52b2a 100644 --- a/dist/core/series.js +++ b/dist/core/series.js @@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); +exports._concatSeries = undefined; + +var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); + +var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); @@ -98,9 +103,9 @@ var Series = function (_NDFrame) { for (var _iterator = (0, _utils.enumerate)(this)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = (0, _slicedToArray3.default)(_step.value, 2), val = _step$value[0], - idx = _step$value[1]; + _idx = _step$value[1]; - array.push(func(val, idx)); + array.push(func(val, _idx)); } } catch (err) { _didIteratorError = true; @@ -238,74 +243,59 @@ var Series = function (_NDFrame) { }), { name: this.name, index: this.index }); } }, { - key: 'add', - value: function add(val) { - if (typeof val === 'number') return this.map(function (v) { - return v + val; - });else if (val instanceof Series) return this.map(function (v, idx) { - return v + val.iloc(idx); - });else if (Array.isArray(val)) return this.map(function (v, idx) { - return v + val[idx]; - });else if (val instanceof _immutable2.default.List) return this.map(function (v, idx) { - return v + val.get(idx); - }); + key: '_combineOp', + value: function _combineOp(other, op) { + var opName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + + if (typeof other === 'number') return this.map(function (val) { + return op(val, other); + });else if (other instanceof Series) return this.map(function (val, idx) { + return op(val, other.iloc(idx)); + });else if (Array.isArray(other)) return this.map(function (val, idx) { + return op(val, other[idx]); + });else if (other instanceof _immutable2.default.List) return this.map(function (val, idx) { + return op(val, other.get(idx)); + }); - throw new Error('add only supports numbers, Arrays, Immutable List and pandas.Series'); + throw new Error(opName + ' only supports numbers, Arrays, Immutable List and pandas.Series'); + } + }, { + key: 'add', + value: function add(other) { + return this._combineOp(other, function (a, b) { + return a + b; + }, 'add'); } }, { key: 'sub', - value: function sub(val) { - if (typeof val === 'number') return this.map(function (v) { - return v - val; - });else if (val instanceof Series) return this.map(function (v, idx) { - return v - val.iloc(idx); - });else if (Array.isArray(val)) return this.map(function (v, idx) { - return v - val[idx]; - });else if (val instanceof _immutable2.default.List) return this.map(function (v, idx) { - return v - val.get(idx); - }); - - throw new Error('sub only supports numbers, Arrays, Immutable List and pandas.Series'); + value: function sub(other) { + return this._combineOp(other, function (a, b) { + return a - b; + }, 'sub'); } }, { key: 'mul', - value: function mul(val) { - if (typeof val === 'number') return this.map(function (v) { - return v * val; - });else if (val instanceof Series) return this.map(function (v, idx) { - return v * val.iloc(idx); - });else if (Array.isArray(val)) return this.map(function (v, idx) { - return v * val[idx]; - });else if (val instanceof _immutable2.default.List) return this.map(function (v, idx) { - return v * val.get(idx); - }); - - throw new Error('mul only supports numbers, Arrays, Immutable List and pandas.Series'); + value: function mul(other) { + return this._combineOp(other, function (a, b) { + return a * b; + }); } }, { key: 'multiply', - value: function multiply(val) { - return this.mul(val); + value: function multiply(other) { + return this.mul(other); } }, { key: 'div', - value: function div(val) { - if (typeof val === 'number') return this.map(function (v) { - return v / val; - });else if (val instanceof Series) return this.map(function (v, idx) { - return v / val.iloc(idx); - });else if (Array.isArray(val)) return this.map(function (v, idx) { - return v / val[idx]; - });else if (val instanceof _immutable2.default.List) return this.map(function (v, idx) { - return v / val.get(idx); - }); - - throw new Error('div only supports numbers, Arrays, Immutable List and pandas.Series'); + value: function div(other) { + return this._combineOp(other, function (a, b) { + return a / b; + }, 'div'); } }, { key: 'divide', - value: function divide(val) { - return this.div(val); + value: function divide(other) { + return this.div(other); } }, { key: 'cov', @@ -632,6 +622,13 @@ var Series = function (_NDFrame) { value: function rename(name) { return new Series(this._values, { name: name, index: this.index }); } + }, { + key: 'append', + value: function append(other) { + var ignore_index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + return _concatSeries([this, other], { ignore_index: ignore_index }); + } }, { key: 'kwargs', get: function get() { @@ -672,4 +669,35 @@ var Series = function (_NDFrame) { return Series; }(_generic2.default); -exports.default = Series; \ No newline at end of file +exports.default = Series; + +var _concatSeriesValues = function _concatSeriesValues(objs) { + var _Immutable$List; + + return (_Immutable$List = _immutable2.default.List([])).concat.apply(_Immutable$List, (0, _toConsumableArray3.default)(objs.map(function (series) { + return series.values; + }))); +}; +var _concatSeriesIndices = function _concatSeriesIndices(objs) { + var _Immutable$List2; + + return (_Immutable$List2 = _immutable2.default.List([])).concat.apply(_Immutable$List2, (0, _toConsumableArray3.default)(objs.map(function (series) { + return series.index; + }))); +}; + +var _concatSeries = exports._concatSeries = function _concatSeries(objs, kwargs) { + if (objs instanceof _immutable2.default.List && objs.filter(function (series) { + return series instanceof Series; + }).size !== objs.size) throw new Error('Objects must all be Series');else if (Array.isArray(objs) && objs.filter(function (series) { + return series instanceof Series; + }).length !== objs.length) throw new Error('Objects must all be Series'); + + if (!kwargs.ignore_index) return new Series(_concatSeriesValues(objs), { index: _concatSeriesIndices(objs) });else if (kwargs.ignore_index) { + return new Series(_concatSeriesValues(objs), { index: _immutable2.default.Range(0, objs.reduce(function (a, b) { + return a + b.length; + }, 0)).toList() }); + } + + throw new Error('Not supported'); +}; \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index c4d1524..3d5db18 100644 --- a/dist/index.js +++ b/dist/index.js @@ -18,6 +18,12 @@ Object.defineProperty(exports, 'DataFrame', { return _index.DataFrame; } }); +Object.defineProperty(exports, 'concat', { + enumerable: true, + get: function get() { + return _index.concat; + } +}); var _tools = require('./tseries/tools'); diff --git a/jest-config.json b/jest-config.json deleted file mode 100644 index b64414b..0000000 --- a/jest-config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "modulePaths": [ - "/shared/vendor/modules" - ], - "moduleFileExtensions": [ - "js" - ], - "moduleDirectories": [ - "node_modules", - "bower_components", - "shared" - ], - "roots": [ - "src/js" - ] -} diff --git a/package-lock.json b/package-lock.json index a86d3d0..ddd9950 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pandas-js", - "version": "0.1.28", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3826,14 +3826,14 @@ } }, "eslint-plugin-react": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.1.0.tgz", - "integrity": "sha1-J3cKzzn1/UnNCvQIPOWBBOs5DUw=", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.2.0.tgz", + "integrity": "sha512-GhgT80V8R3Xz/Rusosd/j7UuvSHakriJlcLHkAYaF0ENLUwFirWU3xCfBSbI5KdNURqHOhy7xtzZ4nC4npC3Kw==", "dev": true, "requires": { "doctrine": "2.0.0", "has": "1.0.1", - "jsx-ast-utils": "1.4.1" + "jsx-ast-utils": "2.0.0" }, "dependencies": { "doctrine": { @@ -3847,10 +3847,13 @@ } }, "jsx-ast-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", - "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz", + "integrity": "sha1-7Aaj1gzzB+XhGdrHutgeifCW8Pg=", + "dev": true, + "requires": { + "array-includes": "3.0.3" + } } } }, diff --git a/package.json b/package.json index 1469fa3..b4e2878 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pandas-js", - "version": "0.1.28", + "version": "0.2.0", "description": "Pandas for JavaScript", "author": "StratoDem Analytics tech@stratodem.com", "contributors": [ @@ -36,7 +36,7 @@ "eslint-plugin-flowtype": "^2.35.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-jsx-a11y": "^6.0.2", - "eslint-plugin-react": "^7.1.0", + "eslint-plugin-react": "^7.2.0", "flow-bin": "^0.52.0", "flow-remove-types": "^1.2.1", "jest": "^20.0.4", @@ -51,7 +51,8 @@ }, "jest": { "modulePaths": [ - "/shared/vendor/modules" + "/shared/vendor/modules", + "/src/es6" ], "moduleFileExtensions": [ "js" diff --git a/src/es6/__tests__/core/frame.js b/src/es6/__tests__/core/frame.js index be398ec..59a882e 100644 --- a/src/es6/__tests__/core/frame.js +++ b/src/es6/__tests__/core/frame.js @@ -981,25 +981,25 @@ describe('frame', () => { expect(df2.index.toArray()).toEqual([1, 2, 3]); }); }); - - describe('pivot_table', () => { - it('pivots', () => { - const df = new DataFrame([ - {a: 1, b: 1, c: 1, d: 3}, - // {a: 1, b: 1, c: 1, d: 4}, - {a: 1, b: 1, c: 2, d: 8}, - {a: 1, b: 2, c: 1, d: 9}, - {a: 1, b: 2, c: 2, d: 10}, - {a: 2, b: 1, c: 1, d: 1}, - {a: 2, b: 1, c: 2, d: 4}, - {a: 2, b: 2, c: 1, d: 1}, - {a: 2, b: 2, c: 2, d: 3}, - {a: 2, b: 2, c: 2, d: 3}, - ]); - - console.log(df.pivot_table(['a', 'b'], 'c', 'd')); - }); - }); + // + // describe('pivot_table', () => { + // it('pivots', () => { + // const df = new DataFrame([ + // {a: 1, b: 1, c: 1, d: 3}, + // // {a: 1, b: 1, c: 1, d: 4}, + // {a: 1, b: 1, c: 2, d: 8}, + // {a: 1, b: 2, c: 1, d: 9}, + // {a: 1, b: 2, c: 2, d: 10}, + // {a: 2, b: 1, c: 1, d: 1}, + // {a: 2, b: 1, c: 2, d: 4}, + // {a: 2, b: 2, c: 1, d: 1}, + // {a: 2, b: 2, c: 2, d: 3}, + // {a: 2, b: 2, c: 2, d: 3}, + // ]); + // + // console.log(df.pivot_table(['a', 'b'], 'c', 'd')); + // }); + // }); describe('rename', () => { it('renames one Series in the DataFrame', () => { @@ -1029,4 +1029,58 @@ describe('frame', () => { expect(df.length).toEqual(5); }); }); + + describe('append', () => { + it('Appends a DataFrame to another when ignore_index is false', () => { + const df1 = new DataFrame( + [{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}], {index: [1, 2, 3]}); + const df2 = new DataFrame( + [{x: 2, y: 2}, {x: 3, y: 3}, {x: 4, y: 4}], {index: [2, 3, 4]}); + + const df3 = df1.append(df2); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4, 2, 3, 4]); + expect(df3.index.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + }); + + it('Appends a DataFrame to another when ignore_index is true', () => { + const df1 = new DataFrame( + [{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}], {index: [1, 2, 3]}); + const df2 = new DataFrame( + [{x: 2, y: 2}, {x: 3, y: 3}, {x: 4, y: 4}], {index: [2, 3, 4]}); + + const df3 = df1.append(df2, true); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4, 2, 3, 4]); + expect(df3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('Appends an empty DataFrame to another', () => { + const df1 = new DataFrame( + [{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}], {index: [1, 2, 3]}); + const df2 = new DataFrame([]); + + const df3 = df1.append(df2); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4]); + expect(df3.index.toArray()).toEqual([1, 2, 3]); + }); + }); + + describe('transpose', () => { + it('Tranposes a DataFrame by flipping indexes and columns', () => { + const df1 = new DataFrame( + [{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}], {index: [1, 2, 3]}); + const df2 = df1.transpose(); + + expect(df2.columns.toArray()).toEqual([1, 2, 3]); + expect(df2.index.toArray()).toEqual(['x', 'y']); + expect(df2.get(1).index.toArray()).toEqual(['x', 'y']); + + const df3 = df2.transpose(); + expect(df3.columns.toArray()).toEqual(['x', 'y']); + expect(df3.index.toArray()).toEqual([1, 2, 3]); + expect(df3.get('x').index.toArray()).toEqual([1, 2, 3]); + }); + }); }); diff --git a/src/es6/__tests__/core/reshape/concat.js b/src/es6/__tests__/core/reshape/concat.js new file mode 100644 index 0000000..82c67dc --- /dev/null +++ b/src/es6/__tests__/core/reshape/concat.js @@ -0,0 +1,79 @@ +/** @flow + * StratoDem Analytics : concat + * Principal Author(s) : Michael Clawar + * Secondary Author(s) : + * Description : + * + * (c) 2016- StratoDem Analytics, LLC + * All Rights Reserved + */ + +import Immutable from 'immutable'; +import DataFrame from '../../../core/frame'; +import Series from '../../../core/series'; + +import concat from '../../../core/reshape/concat'; + +describe('concat', () => { + describe('concat Series', () => { + it('Concatenates two Series without ignoring index', () => { + const series1 = new Series([1, 2, 3, 4]); + const series2 = new Series([2, 3, 4, 5]); + + const series3 = concat([series1, series2]); + expect(series3.values.toArray()).toEqual([1, 2, 3, 4, 2, 3, 4, 5]); + expect(series3.index.toArray()).toEqual([0, 1, 2, 3, 0, 1, 2, 3]); + }); + + it('Concatenates two Series with ignore index', () => { + const series1 = new Series([1, 2, 3, 4]); + const series2 = new Series([2, 3, 4, 5]); + + const series3 = concat([series1, series2], {ignore_index: true}); + expect(series3.values.toArray()).toEqual([1, 2, 3, 4, 2, 3, 4, 5]); + expect(series3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + + it('Throws an error if the first object in concat is a Series ' + + 'and any of the rest are not', () => { + const series1 = new Series([1, 2, 3, 4]); + const series2 = new Series([2, 3, 4, 5]); + + expect(() => concat([series1, series2, []], {ignore_index: true})).toThrow(); + expect(() => concat(Immutable.List([series1, series2, []]), {ignore_index: true})).toThrow(); + }); + }); + + describe('concat DataFrame', () => { + it('Concatenates two DataFrames without ignoring index', () => { + const frame1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}]); + const frame2 = new DataFrame([{x: 2, y: 3}, {x: 3, y: 4}, {x: 4, y: 5}]); + + const frame3 = concat([frame1, frame2]); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(frame3.get('x').index.toArray()).toEqual([0, 1, 2, 0, 1, 2]); + }); + + it('Concatenates two DataFrames with index ignored', () => { + const frame1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}]); + const frame2 = new DataFrame([{x: 2, y: 3}, {x: 3, y: 4}, {x: 4, y: 5}]); + + const frame3 = concat([frame1, frame2], {ignore_index: true}); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(frame3.get('x').index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('Concatenates two DataFrames along axis = 1 without ignoring index', () => { + const frame1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}]); + const frame2 = new DataFrame([{x: 2, y: 3}, {x: 3, y: 4}, {x: 4, y: 5}]); + + const frame3 = concat([frame1, frame2], {axis: 1}); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3]); + expect(frame3.get('y').values.toArray()).toEqual([2, 3, 4]); + expect(frame3.get('x.x').values.toArray()).toEqual([2, 3, 4]); + expect(frame3.get('x.x').name).toEqual('x.x'); + expect(frame3.get('y.x').values.toArray()).toEqual([3, 4, 5]); + expect(frame3.get('y.x').name).toEqual('y.x'); + }); + }); +}); diff --git a/src/es6/__tests__/core/series.js b/src/es6/__tests__/core/series.js index 732237f..8dd58ba 100644 --- a/src/es6/__tests__/core/series.js +++ b/src/es6/__tests__/core/series.js @@ -629,4 +629,22 @@ describe('series', () => { }); }); }); + + describe('append', () => { + it('Appends a Series to another when ignore_index is false', () => { + const ds1 = new Series([1, 2, 3], {index: [1, 2, 3]}); + const ds2 = new Series([2, 3, 4], {index: [2, 3, 4]}); + const ds3 = ds1.append(ds2); + expect(ds3.values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(ds3.index.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + }); + + it('Appends a Series to another when ignore_index is true', () => { + const ds1 = new Series([1, 2, 3], {index: [1, 2, 3]}); + const ds2 = new Series([2, 3, 4], {index: [2, 3, 4]}); + const ds3 = ds1.append(ds2, true); + expect(ds3.values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(ds3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + }); }); diff --git a/src/es6/core/frame.js b/src/es6/core/frame.js index 5abac5b..62f3e20 100644 --- a/src/es6/core/frame.js +++ b/src/es6/core/frame.js @@ -9,10 +9,11 @@ import Immutable from 'immutable'; import { InvalidAxisError } from './exceptions'; import NDFrame from './generic'; import { MultiIndex } from './multiindex'; -import Series from './series'; +import Series, { _concatSeries } from './series'; // import { Workbook, Sheet } from './structs'; TODO import { enumerate, nonMergeColumns, intersectingColumns, parseIndex, OP_CUMSUM, OP_CUMMUL, OP_CUMMIN, OP_CUMMAX, generateCumulativeFunc } from './utils'; +import concat from './reshape/concat'; declare type T_LIST = Immutable.List @@ -1571,6 +1572,57 @@ export default class DataFrame extends NDFrame { return [nextCol, this._data.get(prevCol).rename(nextCol)]; })), {index: this.index}); } + + /** + * Append another DataFrame to this and return a new DataFrame + * + * pandas equivalent: [DataFrame.append](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.append.html) + * + * @param {DataFrame} other + * @param {boolean} ignore_index + * @returns {DataFrame} + * + * @example + * const df1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}], {index: [1, 2]}); + * const df2 = new DataFrame([{x: 2, y: 2}, {x: 3, y: 3}], {index: [2, 3]}); + * + * // Returns DataFrame( + * [{x: 1, y: 2}, {x: 2, y: 3}, {x: 2, y: 2}, {x: 3, y: 3}], + * {index: [1, 2, 2, 3]}); + * df1.append(df2); + * + * // Returns DataFrame( + * [{x: 1, y: 2}, {x: 2, y: 3}, {x: 2, y: 2}, {x: 3, y: 3}], + * {index: [0, 1, 2, 3]}); + * df1.append(df2, true); + */ + append(other: DataFrame, ignore_index: boolean = false): DataFrame { + // eslint-disable-next-line + return _concatDataFrame(// $FlowIssue + [this, other], + {ignore_index}); + } + + /** + * Transpose the DataFrame by switching the index and columns + * + * pandas equivalent: [DataFrame.transpose](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.transpose.html) + * + * @returns {DataFrame} + * + * @example + * const df1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}], {index: [1, 2, 3]}); + * + * // Returns DataFrame( + * [{1: 1, 2: 2, 3: 3}, {1: 2, 2: 3, 3: 4}], {index: ['x', 'y']}); + * df1.transpose(); + */ + transpose(): DataFrame { + return new DataFrame( + Immutable.OrderedMap( + this.index.map((index, idx) => + ([index, new Series(this.values.get(idx), {index: this.columns.toList()})])))); + } } const innerMerge = (df1: DataFrame, df2: DataFrame, on: Array): DataFrame => { @@ -1750,3 +1802,66 @@ export const mergeDataFrame = (df1: DataFrame, df2: DataFrame, on: Array | Immutable.List, + kwargs: T_KWARGS): DataFrame => { + if (!(objs instanceof Immutable.List || Array.isArray(objs))) + throw new Error('objs must be List or Array'); + + if (objs instanceof Immutable.List + && objs.filter(frame => frame instanceof DataFrame).size !== objs.size) + throw new Error('Objects must all be DataFrame'); + else if (Array.isArray(objs) + && objs.filter(frame => frame instanceof DataFrame).length !== objs.length) + throw new Error('Objects must all be DataFrame'); + + if (Array.isArray(objs) && objs.length === 1) + return objs[0]; + else if (objs instanceof Immutable.List && objs.size === 1) + return objs.get(0); + + let seriesOrderedMap = Immutable.OrderedMap({}); + if (kwargs.axis === 1) { + objs.forEach((df: DataFrame) => { + df.columns.forEach((column: string) => { + const columnExists = seriesOrderedMap.has(column); + seriesOrderedMap = seriesOrderedMap.set( + columnExists ? `${column}.x` : column, // $FlowIssue + columnExists ? df.get(column).rename(`${column}.x`) : df.get(column)); + }); + }); + } else { + objs.forEach((df: DataFrame) => { + const lenSeriesInMap = seriesOrderedMap.keySeq().size === 0 + ? 0 + : seriesOrderedMap.first().length; + const nextLength = df.length + lenSeriesInMap; + + seriesOrderedMap = Immutable.OrderedMap( + // Get entries already concated (already in seriesOrderedMap) + seriesOrderedMap.entrySeq().map(([column, series]) => { + if (df.columnExists(column)) + return [ + column, // $FlowIssue + _concatSeries([series, df.get(column)], kwargs)]; + return [ + column, // $FlowIssue + _concatSeries([ + series, + new Series(Immutable.Repeat(NaN, df.length).toList(), {index: df.index})], + kwargs)]; // Now merge with columns only in the "right" DataFrame + })).merge(Immutable.OrderedMap( + df.columns + .filter(column => !seriesOrderedMap.has(column)) + .map(column => // $FlowIssue + ([column, lenSeriesInMap === 0 ? df.get(column) : _concatSeries([ + new Series(Immutable.Repeat(NaN, nextLength)), + df.get(column)], + kwargs)])))); + }); + } + + return new DataFrame(seriesOrderedMap); +}; diff --git a/src/es6/core/index.js b/src/es6/core/index.js index cf294be..0740604 100644 --- a/src/es6/core/index.js +++ b/src/es6/core/index.js @@ -13,3 +13,4 @@ export { default as Series } from './series'; export { default as DataFrame } from './frame'; +export { default as concat } from './reshape/concat'; \ No newline at end of file diff --git a/src/es6/core/reshape/concat.js b/src/es6/core/reshape/concat.js new file mode 100644 index 0000000..33657ba --- /dev/null +++ b/src/es6/core/reshape/concat.js @@ -0,0 +1,51 @@ +/** @flow + * StratoDem Analytics : concat.js + * Principal Author(s) : Michael Clawar + * Secondary Author(s) : + * Description : + * + * (c) 2016- StratoDem Analytics, LLC + * All Rights Reserved + */ + +import Immutable from 'immutable'; + +import DataFrame, { _concatDataFrame } from '../frame'; +import Series, { _concatSeries } from '../series'; + +type T_CONCAT = DataFrame | Series; +type T_OBJS = Array | Immutable.List; + +type T_KWARGS = { + ignore_index: boolean, + axis?: 0 | 1, +} + +/** + * Concatenate pandas objects along a particular axis. + * + * pandas equivalent: [pandas.concat](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.concat.html) + * + * @returns {Series | DataFrame} + * + * @example + * const series1 = new Series([1, 2, 3, 4]); + * const series2 = new Series([2, 3, 4, 5]); + * + * // Returns Series([1, 2, 3, 4, 2, 3, 4, 5], {index: [0, 1, 2, 3, 0, 1, 2, 3]}) + * concat([series1, series2], {ignore_index: false}); + * + * // Returns Series([1, 2, 3, 4, 2, 3, 4, 5], {index: [0, 1, 2, 3, 4, 5, 6, 7]}) + * concat([series1, series2], {ignore_index: true}); + */ +const concat = (objs: T_OBJS, kwargs: T_KWARGS = {ignore_index: false, axis: 0}): T_CONCAT => { + if ((Array.isArray(objs) && objs[0] instanceof Series) + || (objs instanceof Immutable.List && objs.get(0) instanceof Series)) + return _concatSeries(objs, {ignore_index: kwargs.ignore_index}); + else if ((Array.isArray(objs) && objs[0] instanceof DataFrame) + || (objs instanceof Immutable.List && objs.get(0) instanceof DataFrame)) + return _concatDataFrame(objs, {ignore_index: kwargs.ignore_index, axis: kwargs.axis}); + throw new Error('Not supported'); +}; + +export default concat; diff --git a/src/es6/core/series.js b/src/es6/core/series.js index a4dff05..30cf3d7 100644 --- a/src/es6/core/series.js +++ b/src/es6/core/series.js @@ -11,7 +11,6 @@ import {enumerate, sum, parseIndex, round10, OP_CUMSUM, OP_CUMMUL, OP_CUMMAX, OP_CUMMIN, generateCumulativeFunc} from './utils'; import {DType, arrayToDType} from './dtype'; - declare type T_MF = (value: number, idx: number) => number | string; declare type T_BF = (value1: number, value2: number) => boolean; @@ -42,7 +41,7 @@ export default class Series extends NDFrame { * // 5 4 * // Name: My test name, dtype: dtype(int) */ - constructor(data: Array|Object, kwargs: Object = {}) { + constructor(data: Array | Object, kwargs: Object = {}) { super(data, kwargs); if (Array.isArray(data)) { @@ -98,6 +97,7 @@ export default class Series extends NDFrame { */ map(func: T_MF): Series { const array = []; + // eslint-disable-next-line for (const [val, idx] of enumerate(this)) { // $FlowIssue TODO array.push(func(val, idx)); @@ -246,7 +246,7 @@ export default class Series extends NDFrame { * // Returns List [1, 2, 3] * ds.index; */ - set index(index: Immutable.List|Array) { + set index(index: Immutable.List | Array) { this.set_axis(0, parseIndex(index, this.values)); } @@ -719,7 +719,7 @@ export default class Series extends NDFrame { return new Series( Immutable.Repeat(null, periods).toList().concat( Immutable.Range(periods, this.length).map(idx => - (this.values.get(idx) - this.values.get(idx - periods))).toList()), + (this.values.get(idx) - this.values.get(idx - periods))).toList()), {index: this.index, name: this.name}); } @@ -748,7 +748,7 @@ export default class Series extends NDFrame { return new Series( Immutable.Repeat(null, periods).toList().concat( Immutable.Range(periods, this.length).map(idx => - (this.values.get(idx) / this.values.get(idx - periods)) - 1).toList()), + (this.values.get(idx) / this.values.get(idx - periods)) - 1).toList()), {index: this.index, name: this.name}); } @@ -1136,7 +1136,7 @@ export default class Series extends NDFrame { * // Returns Series([2, 3]); * ds.filter(ds.gte(2)); */ - filter(iterBool: Series|Array|Immutable.List): Series { + filter(iterBool: Series | Array | Immutable.List): Series { if (!Array.isArray(iterBool) && !(iterBool instanceof Immutable.List) && !(iterBool instanceof Series)) @@ -1233,7 +1233,7 @@ export default class Series extends NDFrame { cummin(): Series { return this._cumulativeHelper(OP_CUMMIN); } - + /** * Convert the Series to a json object * @@ -1300,4 +1300,58 @@ export default class Series extends NDFrame { rename(name: string | number): Series { return new Series(this._values, {name, index: this.index}); } + + /** + * Append another Series to this and return a new Series + * + * pandas equivalent: [Series.append](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.append.html) + * + * @param {Series} other + * @param {boolean} ignore_index + * @returns {Series} + * + * @example + * const ds1 = new Series([1, 2, 3], {index: [1, 2, 3]}); + * const ds2 = new Series([2, 3, 4], {index: [3, 4, 5]}); + * + * // Returns Series([1, 2, 3, 2, 3, 4], {index: [1, 2, 3, 3, 4, 5]}); + * ds1.append(ds2); + * + * // Returns Series([1, 2, 3, 2, 3, 4], {index: [0, 1, 2, 3, 4, 5]}); + * ds1.append(ds2, true); + */ + append(other: Series, ignore_index: boolean = false): Series { + // eslint-disable-next-line + return _concatSeries( // $FlowIssue + [this, other], + {ignore_index}); + } } + + +type T_ITER_SERIES = Array | Immutable.List; +type T_KWARGS = {ignore_index: boolean, axis?: 0 | 1}; +const _concatSeriesValues = (objs: T_ITER_SERIES) => + Immutable.List([]).concat(...objs.map(series => series.values)); +const _concatSeriesIndices = (objs: T_ITER_SERIES) => + Immutable.List([]).concat(...objs.map(series => series.index)); + +export const _concatSeries = (objs: Array | Immutable.List, + kwargs: T_KWARGS): Series => { + if (objs instanceof Immutable.List + && objs.filter(series => series instanceof Series).size !== objs.size) + throw new Error('Objects must all be Series'); + else if (Array.isArray(objs) + && objs.filter(series => series instanceof Series).length !== objs.length) + throw new Error('Objects must all be Series'); + + if (!kwargs.ignore_index) + return new Series(_concatSeriesValues(objs), {index: _concatSeriesIndices(objs)}); + else if (kwargs.ignore_index) { + return new Series( + _concatSeriesValues(objs), + {index: Immutable.Range(0, objs.reduce((a, b: Series) => a + b.length, 0)).toList()}); + } + + throw new Error('Not supported'); +}; diff --git a/src/es6/index.js b/src/es6/index.js index e3059d2..7579d46 100644 --- a/src/es6/index.js +++ b/src/es6/index.js @@ -1,3 +1,3 @@ -export { Series, DataFrame } from './core/index'; +export { Series, DataFrame, concat } from './core/index'; export { to_datetime } from './tseries/tools'; diff --git a/src/js/__tests__/core/frame.js b/src/js/__tests__/core/frame.js index 539f6cb..cf2e5b2 100644 --- a/src/js/__tests__/core/frame.js +++ b/src/js/__tests__/core/frame.js @@ -989,16 +989,25 @@ describe('frame', function () { expect(df2.index.toArray()).toEqual([1, 2, 3]); }); }); - - describe('pivot_table', function () { - it('pivots', function () { - var df = new _frame2.default([{ a: 1, b: 1, c: 1, d: 3 }, - // {a: 1, b: 1, c: 1, d: 4}, - { a: 1, b: 1, c: 2, d: 8 }, { a: 1, b: 2, c: 1, d: 9 }, { a: 1, b: 2, c: 2, d: 10 }, { a: 2, b: 1, c: 1, d: 1 }, { a: 2, b: 1, c: 2, d: 4 }, { a: 2, b: 2, c: 1, d: 1 }, { a: 2, b: 2, c: 2, d: 3 }, { a: 2, b: 2, c: 2, d: 3 }]); - - console.log(df.pivot_table(['a', 'b'], 'c', 'd')); - }); - }); + // + // describe('pivot_table', () => { + // it('pivots', () => { + // const df = new DataFrame([ + // {a: 1, b: 1, c: 1, d: 3}, + // // {a: 1, b: 1, c: 1, d: 4}, + // {a: 1, b: 1, c: 2, d: 8}, + // {a: 1, b: 2, c: 1, d: 9}, + // {a: 1, b: 2, c: 2, d: 10}, + // {a: 2, b: 1, c: 1, d: 1}, + // {a: 2, b: 1, c: 2, d: 4}, + // {a: 2, b: 2, c: 1, d: 1}, + // {a: 2, b: 2, c: 2, d: 3}, + // {a: 2, b: 2, c: 2, d: 3}, + // ]); + // + // console.log(df.pivot_table(['a', 'b'], 'c', 'd')); + // }); + // }); describe('rename', function () { it('renames one Series in the DataFrame', function () { @@ -1027,6 +1036,54 @@ describe('frame', function () { expect(df.length).toEqual(5); }); }); + + describe('append', function () { + it('Appends a DataFrame to another when ignore_index is false', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = new _frame2.default([{ x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 4 }], { index: [2, 3, 4] }); + + var df3 = df1.append(df2); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4, 2, 3, 4]); + expect(df3.index.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + }); + + it('Appends a DataFrame to another when ignore_index is true', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = new _frame2.default([{ x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 4 }], { index: [2, 3, 4] }); + + var df3 = df1.append(df2, true); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4, 2, 3, 4]); + expect(df3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('Appends an empty DataFrame to another', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = new _frame2.default([]); + + var df3 = df1.append(df2); + expect(df3.get('x').values.toArray()).toEqual([1, 2, 3]); + expect(df3.get('y').values.toArray()).toEqual([2, 3, 4]); + expect(df3.index.toArray()).toEqual([1, 2, 3]); + }); + }); + + describe('transpose', function () { + it('Tranposes a DataFrame by flipping indexes and columns', function () { + var df1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }], { index: [1, 2, 3] }); + var df2 = df1.transpose(); + + expect(df2.columns.toArray()).toEqual([1, 2, 3]); + expect(df2.index.toArray()).toEqual(['x', 'y']); + expect(df2.get(1).index.toArray()).toEqual(['x', 'y']); + + var df3 = df2.transpose(); + expect(df3.columns.toArray()).toEqual(['x', 'y']); + expect(df3.index.toArray()).toEqual([1, 2, 3]); + expect(df3.get('x').index.toArray()).toEqual([1, 2, 3]); + }); + }); }); //# sourceMappingURL=frame.js.map \ No newline at end of file diff --git a/src/js/__tests__/core/reshape/concat.js b/src/js/__tests__/core/reshape/concat.js new file mode 100644 index 0000000..7ecb74c --- /dev/null +++ b/src/js/__tests__/core/reshape/concat.js @@ -0,0 +1,98 @@ +'use strict'; + +var _immutable = require('immutable'); + +var _immutable2 = _interopRequireDefault(_immutable); + +var _frame = require('../../../core/frame'); + +var _frame2 = _interopRequireDefault(_frame); + +var _series = require('../../../core/series'); + +var _series2 = _interopRequireDefault(_series); + +var _concat = require('../../../core/reshape/concat'); + +var _concat2 = _interopRequireDefault(_concat); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * StratoDem Analytics : concat + * Principal Author(s) : Michael Clawar + * Secondary Author(s) : + * Description : + * + * (c) 2016- StratoDem Analytics, LLC + * All Rights Reserved + */ + +describe('concat', function () { + describe('concat Series', function () { + it('Concatenates two Series without ignoring index', function () { + var series1 = new _series2.default([1, 2, 3, 4]); + var series2 = new _series2.default([2, 3, 4, 5]); + + var series3 = (0, _concat2.default)([series1, series2]); + expect(series3.values.toArray()).toEqual([1, 2, 3, 4, 2, 3, 4, 5]); + expect(series3.index.toArray()).toEqual([0, 1, 2, 3, 0, 1, 2, 3]); + }); + + it('Concatenates two Series with ignore index', function () { + var series1 = new _series2.default([1, 2, 3, 4]); + var series2 = new _series2.default([2, 3, 4, 5]); + + var series3 = (0, _concat2.default)([series1, series2], { ignore_index: true }); + expect(series3.values.toArray()).toEqual([1, 2, 3, 4, 2, 3, 4, 5]); + expect(series3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + + it('Throws an error if the first object in concat is a Series ' + 'and any of the rest are not', function () { + var series1 = new _series2.default([1, 2, 3, 4]); + var series2 = new _series2.default([2, 3, 4, 5]); + + expect(function () { + return (0, _concat2.default)([series1, series2, []], { ignore_index: true }); + }).toThrow(); + expect(function () { + return (0, _concat2.default)(_immutable2.default.List([series1, series2, []]), { ignore_index: true }); + }).toThrow(); + }); + }); + + describe('concat DataFrame', function () { + it('Concatenates two DataFrames without ignoring index', function () { + var frame1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }]); + var frame2 = new _frame2.default([{ x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }]); + + var frame3 = (0, _concat2.default)([frame1, frame2]); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(frame3.get('x').index.toArray()).toEqual([0, 1, 2, 0, 1, 2]); + }); + + it('Concatenates two DataFrames with index ignored', function () { + var frame1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }]); + var frame2 = new _frame2.default([{ x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }]); + + var frame3 = (0, _concat2.default)([frame1, frame2], { ignore_index: true }); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(frame3.get('x').index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('Concatenates two DataFrames along axis = 1 without ignoring index', function () { + var frame1 = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }]); + var frame2 = new _frame2.default([{ x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }]); + + var frame3 = (0, _concat2.default)([frame1, frame2], { axis: 1 }); + expect(frame3.get('x').values.toArray()).toEqual([1, 2, 3]); + expect(frame3.get('y').values.toArray()).toEqual([2, 3, 4]); + expect(frame3.get('x.x').values.toArray()).toEqual([2, 3, 4]); + expect(frame3.get('x.x').name).toEqual('x.x'); + expect(frame3.get('y.x').values.toArray()).toEqual([3, 4, 5]); + expect(frame3.get('y.x').name).toEqual('y.x'); + }); + }); +}); + +//# sourceMappingURL=concat.js.map \ No newline at end of file diff --git a/src/js/__tests__/core/series.js b/src/js/__tests__/core/series.js index e139ce6..de69864 100644 --- a/src/js/__tests__/core/series.js +++ b/src/js/__tests__/core/series.js @@ -658,6 +658,24 @@ describe('series', function () { }); }); }); + + describe('append', function () { + it('Appends a Series to another when ignore_index is false', function () { + var ds1 = new _series2.default([1, 2, 3], { index: [1, 2, 3] }); + var ds2 = new _series2.default([2, 3, 4], { index: [2, 3, 4] }); + var ds3 = ds1.append(ds2); + expect(ds3.values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(ds3.index.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + }); + + it('Appends a Series to another when ignore_index is true', function () { + var ds1 = new _series2.default([1, 2, 3], { index: [1, 2, 3] }); + var ds2 = new _series2.default([2, 3, 4], { index: [2, 3, 4] }); + var ds3 = ds1.append(ds2, true); + expect(ds3.values.toArray()).toEqual([1, 2, 3, 2, 3, 4]); + expect(ds3.index.toArray()).toEqual([0, 1, 2, 3, 4, 5]); + }); + }); }); //# sourceMappingURL=series.js.map \ No newline at end of file diff --git a/src/js/core/frame.js b/src/js/core/frame.js index 70cc423..fa29e59 100644 --- a/src/js/core/frame.js +++ b/src/js/core/frame.js @@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.mergeDataFrame = undefined; +exports._concatDataFrame = exports.mergeDataFrame = undefined; var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); @@ -55,13 +55,17 @@ var _series2 = _interopRequireDefault(_series); var _utils = require('./utils'); +var _concat = require('./reshape/concat'); + +var _concat2 = _interopRequireDefault(_concat); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // eslint-disable-next-line -/** - * DataFrame object - */ +// import { Workbook, Sheet } from './structs'; TODO + +// import { saveAs } from 'file-saver'; TODO figure out if best way var parseArrayToSeriesMap = function parseArrayToSeriesMap(array, index) { var dataMap = _immutable2.default.Map({}); @@ -92,9 +96,9 @@ var parseArrayToSeriesMap = function parseArrayToSeriesMap(array, index) { return _immutable2.default.Map(dataMap); }; -// import { Workbook, Sheet } from './structs'; TODO - -// import { saveAs } from 'file-saver'; TODO figure out if best way +/** + * DataFrame object + */ var DataFrame = function (_NDFrame) { (0, _inherits3.default)(DataFrame, _NDFrame); @@ -1653,6 +1657,65 @@ var DataFrame = function (_NDFrame) { return [nextCol, _this17._data.get(prevCol).rename(nextCol)]; })), { index: this.index }); } + + /** + * Append another DataFrame to this and return a new DataFrame + * + * pandas equivalent: [DataFrame.append](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.append.html) + * + * @param {DataFrame} other + * @param {boolean} ignore_index + * @returns {DataFrame} + * + * @example + * const df1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}], {index: [1, 2]}); + * const df2 = new DataFrame([{x: 2, y: 2}, {x: 3, y: 3}], {index: [2, 3]}); + * + * // Returns DataFrame( + * [{x: 1, y: 2}, {x: 2, y: 3}, {x: 2, y: 2}, {x: 3, y: 3}], + * {index: [1, 2, 2, 3]}); + * df1.append(df2); + * + * // Returns DataFrame( + * [{x: 1, y: 2}, {x: 2, y: 3}, {x: 2, y: 2}, {x: 3, y: 3}], + * {index: [0, 1, 2, 3]}); + * df1.append(df2, true); + */ + + }, { + key: 'append', + value: function append(other) { + var ignore_index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + // eslint-disable-next-line + return _concatDataFrame( // $FlowIssue + [this, other], { ignore_index: ignore_index }); + } + + /** + * Transpose the DataFrame by switching the index and columns + * + * pandas equivalent: [DataFrame.transpose](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.transpose.html) + * + * @returns {DataFrame} + * + * @example + * const df1 = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 4}], {index: [1, 2, 3]}); + * + * // Returns DataFrame( + * [{1: 1, 2: 2, 3: 3}, {1: 2, 2: 3, 3: 4}], {index: ['x', 'y']}); + * df1.transpose(); + */ + + }, { + key: 'transpose', + value: function transpose() { + var _this18 = this; + + return new DataFrame(_immutable2.default.OrderedMap(this.index.map(function (index, idx) { + return [index, new _series2.default(_this18.values.get(idx), { index: _this18.columns.toList() })]; + }))); + } }, { key: 'kwargs', get: function get() { @@ -1676,15 +1739,15 @@ var DataFrame = function (_NDFrame) { }, { key: 'values', get: function get() { - var _this18 = this; + var _this19 = this; if (this._values instanceof _immutable2.default.List) return (0, _get3.default)(DataFrame.prototype.__proto__ || Object.getPrototypeOf(DataFrame.prototype), 'values', this); var valuesList = _immutable2.default.List([]); var _loop = function _loop(idx) { - valuesList = valuesList.concat([_immutable2.default.List(_this18.columns.map(function (k) { - return _this18._data.get(k).iloc(idx); + valuesList = valuesList.concat([_immutable2.default.List(_this19.columns.map(function (k) { + return _this19._data.get(k).iloc(idx); }))]); }; @@ -1734,14 +1797,14 @@ var DataFrame = function (_NDFrame) { */ , set: function set(columns) { - var _this19 = this; + var _this20 = this; if (!Array.isArray(columns) || columns.length !== this.columns.size) throw new Error('Columns must be array of same dimension'); var nextData = {}; columns.forEach(function (k, idx) { - var prevColumn = _this19.columns.get(idx); - var prevSeries = _this19.get(prevColumn); + var prevColumn = _this20.columns.get(idx); + var prevSeries = _this20.get(prevColumn); nextData[k] = prevSeries.rename(k); }); @@ -1786,7 +1849,7 @@ var DataFrame = function (_NDFrame) { */ , set: function set(index) { - var _this20 = this; + var _this21 = this; this.set_axis(0, (0, _utils.parseIndex)(index, this._data.get(this.columns.get(0)).values)); @@ -1797,7 +1860,7 @@ var DataFrame = function (_NDFrame) { v = _ref11[1]; // noinspection Eslint - v.index = _this20.index; + v.index = _this21.index; }); } @@ -1818,10 +1881,10 @@ var DataFrame = function (_NDFrame) { }, { key: 'length', get: function get() { - var _this21 = this; + var _this22 = this; return Math.max.apply(Math, [0].concat((0, _toConsumableArray3.default)(this.columns.map(function (k) { - return _this21.get(k).length; + return _this22.get(k).length; }).toArray()))); } }]); @@ -2151,4 +2214,54 @@ var mergeDataFrame = exports.mergeDataFrame = function mergeDataFrame(df1, df2, } }; +// Concat +var _concatDataFrame = exports._concatDataFrame = function _concatDataFrame(objs, kwargs) { + if (!(objs instanceof _immutable2.default.List || Array.isArray(objs))) throw new Error('objs must be List or Array'); + + if (objs instanceof _immutable2.default.List && objs.filter(function (frame) { + return frame instanceof DataFrame; + }).size !== objs.size) throw new Error('Objects must all be DataFrame');else if (Array.isArray(objs) && objs.filter(function (frame) { + return frame instanceof DataFrame; + }).length !== objs.length) throw new Error('Objects must all be DataFrame'); + + if (Array.isArray(objs) && objs.length === 1) return objs[0];else if (objs instanceof _immutable2.default.List && objs.size === 1) return objs.get(0); + + var seriesOrderedMap = _immutable2.default.OrderedMap({}); + if (kwargs.axis === 1) { + objs.forEach(function (df) { + df.columns.forEach(function (column) { + var columnExists = seriesOrderedMap.has(column); + seriesOrderedMap = seriesOrderedMap.set(columnExists ? column + '.x' : column, // $FlowIssue + columnExists ? df.get(column).rename(column + '.x') : df.get(column)); + }); + }); + } else { + objs.forEach(function (df) { + var lenSeriesInMap = seriesOrderedMap.keySeq().size === 0 ? 0 : seriesOrderedMap.first().length; + var nextLength = df.length + lenSeriesInMap; + + seriesOrderedMap = _immutable2.default.OrderedMap( + // Get entries already concated (already in seriesOrderedMap) + seriesOrderedMap.entrySeq().map(function (_ref12) { + var _ref13 = (0, _slicedToArray3.default)(_ref12, 2), + column = _ref13[0], + series = _ref13[1]; + + if (df.columnExists(column)) return [column, // $FlowIssue + (0, _series._concatSeries)([series, df.get(column)], kwargs)]; + return [column, // $FlowIssue + (0, _series._concatSeries)([series, new _series2.default(_immutable2.default.Repeat(NaN, df.length).toList(), { index: df.index })], kwargs)]; // Now merge with columns only in the "right" DataFrame + })).merge(_immutable2.default.OrderedMap(df.columns.filter(function (column) { + return !seriesOrderedMap.has(column); + }).map(function (column) { + return (// $FlowIssue + [column, lenSeriesInMap === 0 ? df.get(column) : (0, _series._concatSeries)([new _series2.default(_immutable2.default.Repeat(NaN, nextLength)), df.get(column)], kwargs)] + ); + }))); + }); + } + + return new DataFrame(seriesOrderedMap); +}; + //# sourceMappingURL=frame.js.map \ No newline at end of file diff --git a/src/js/core/index.js b/src/js/core/index.js index b012fa0..1ddee90 100644 --- a/src/js/core/index.js +++ b/src/js/core/index.js @@ -22,6 +22,15 @@ Object.defineProperty(exports, 'DataFrame', { } }); +var _concat = require('./reshape/concat'); + +Object.defineProperty(exports, 'concat', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_concat).default; + } +}); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/src/js/core/reshape/concat.js b/src/js/core/reshape/concat.js new file mode 100644 index 0000000..7003790 --- /dev/null +++ b/src/js/core/reshape/concat.js @@ -0,0 +1,55 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _immutable = require('immutable'); + +var _immutable2 = _interopRequireDefault(_immutable); + +var _frame = require('../frame'); + +var _frame2 = _interopRequireDefault(_frame); + +var _series = require('../series'); + +var _series2 = _interopRequireDefault(_series); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Concatenate pandas objects along a particular axis. + * + * pandas equivalent: [pandas.concat](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.concat.html) + * + * @returns {Series | DataFrame} + * + * @example + * const series1 = new Series([1, 2, 3, 4]); + * const series2 = new Series([2, 3, 4, 5]); + * + * // Returns Series([1, 2, 3, 4, 2, 3, 4, 5], {index: [0, 1, 2, 3, 0, 1, 2, 3]}) + * concat([series1, series2], {ignore_index: false}); + * + * // Returns Series([1, 2, 3, 4, 2, 3, 4, 5], {index: [0, 1, 2, 3, 4, 5, 6, 7]}) + * concat([series1, series2], {ignore_index: true}); + */ +var concat = function concat(objs) { + var kwargs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { ignore_index: false, axis: 0 }; + + if (Array.isArray(objs) && objs[0] instanceof _series2.default || objs instanceof _immutable2.default.List && objs.get(0) instanceof _series2.default) return (0, _series._concatSeries)(objs, { ignore_index: kwargs.ignore_index });else if (Array.isArray(objs) && objs[0] instanceof _frame2.default || objs instanceof _immutable2.default.List && objs.get(0) instanceof _frame2.default) return (0, _frame._concatDataFrame)(objs, { ignore_index: kwargs.ignore_index, axis: kwargs.axis }); + throw new Error('Not supported'); +}; /** + * StratoDem Analytics : concat.js + * Principal Author(s) : Michael Clawar + * Secondary Author(s) : + * Description : + * + * (c) 2016- StratoDem Analytics, LLC + * All Rights Reserved + */ + +exports.default = concat; + +//# sourceMappingURL=concat.js.map \ No newline at end of file diff --git a/src/js/core/series.js b/src/js/core/series.js index 7e5652e..f76ed31 100644 --- a/src/js/core/series.js +++ b/src/js/core/series.js @@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); +exports._concatSeries = undefined; + +var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); + +var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); @@ -143,6 +148,7 @@ var Series = function (_NDFrame) { key: 'map', value: function map(func) { var array = []; + // eslint-disable-next-line var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; @@ -1449,6 +1455,36 @@ var Series = function (_NDFrame) { value: function rename(name) { return new Series(this._values, { name: name, index: this.index }); } + + /** + * Append another Series to this and return a new Series + * + * pandas equivalent: [Series.append](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.append.html) + * + * @param {Series} other + * @param {boolean} ignore_index + * @returns {Series} + * + * @example + * const ds1 = new Series([1, 2, 3], {index: [1, 2, 3]}); + * const ds2 = new Series([2, 3, 4], {index: [3, 4, 5]}); + * + * // Returns Series([1, 2, 3, 2, 3, 4], {index: [1, 2, 3, 3, 4, 5]}); + * ds1.append(ds2); + * + * // Returns Series([1, 2, 3, 2, 3, 4], {index: [0, 1, 2, 3, 4, 5]}); + * ds1.append(ds2, true); + */ + + }, { + key: 'append', + value: function append(other) { + var ignore_index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + // eslint-disable-next-line + return _concatSeries( // $FlowIssue + [this, other], { ignore_index: ignore_index }); + } }, { key: 'kwargs', get: function get() { @@ -1569,4 +1605,35 @@ var Series = function (_NDFrame) { exports.default = Series; +var _concatSeriesValues = function _concatSeriesValues(objs) { + var _Immutable$List; + + return (_Immutable$List = _immutable2.default.List([])).concat.apply(_Immutable$List, (0, _toConsumableArray3.default)(objs.map(function (series) { + return series.values; + }))); +}; +var _concatSeriesIndices = function _concatSeriesIndices(objs) { + var _Immutable$List2; + + return (_Immutable$List2 = _immutable2.default.List([])).concat.apply(_Immutable$List2, (0, _toConsumableArray3.default)(objs.map(function (series) { + return series.index; + }))); +}; + +var _concatSeries = exports._concatSeries = function _concatSeries(objs, kwargs) { + if (objs instanceof _immutable2.default.List && objs.filter(function (series) { + return series instanceof Series; + }).size !== objs.size) throw new Error('Objects must all be Series');else if (Array.isArray(objs) && objs.filter(function (series) { + return series instanceof Series; + }).length !== objs.length) throw new Error('Objects must all be Series'); + + if (!kwargs.ignore_index) return new Series(_concatSeriesValues(objs), { index: _concatSeriesIndices(objs) });else if (kwargs.ignore_index) { + return new Series(_concatSeriesValues(objs), { index: _immutable2.default.Range(0, objs.reduce(function (a, b) { + return a + b.length; + }, 0)).toList() }); + } + + throw new Error('Not supported'); +}; + //# sourceMappingURL=series.js.map \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js index 2fabba2..51ce111 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -18,6 +18,12 @@ Object.defineProperty(exports, 'DataFrame', { return _index.DataFrame; } }); +Object.defineProperty(exports, 'concat', { + enumerable: true, + get: function get() { + return _index.concat; + } +}); var _tools = require('./tseries/tools');