Skip to content
This repository was archived by the owner on Sep 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
to_excel writer with option to download in browser
  • Loading branch information
mjclawar committed Jan 13, 2017
commit 90970d86bd15c161e904cb65723b09787389e00a
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
],
"dependencies": {
"babel-plugin-transform-runtime": "^6.15.0",
"immutable": "^3.8.1"
"file-saver": "^1.3.3",
"immutable": "^3.8.1",
"xlsx": "^0.8.1"
},
"devDependencies": {
"babel-cli": "^6.18.0",
Expand Down
15 changes: 15 additions & 0 deletions src/es6/__tests__/core/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Immutable from 'immutable';

import DataFrame, {mergeDataFrame} from '../../core/frame';
import Series from '../../core/series';
import { Workbook } from '../../core/structs';
import {IndexMismatchError} from '../../core/exceptions';


Expand Down Expand Up @@ -330,6 +331,20 @@ describe('frame', () => {
});
});

describe('to_excel', () => {
it('converts a pandas DataFrame to a properly formatted Excel file', () => {
const df = new DataFrame(Immutable.Map({x: new Series([1, 2, 3]), y: new Series([2, 3, 4])}));

const originalURL = window.URL;
window.URL = {
createObjectURL: (blob) => { console.log(blob); return "something"; },
};
console.log(df.to_excel(new Workbook(), 'my test sheet', true));

window.URL = originalURL;
});
});

describe('where', () => {
it('checks for equality of a scalar and returns a DataFrame', () => {
const df = new DataFrame([{x: 1, y: 2}, {x: 2, y: 3}]);
Expand Down
45 changes: 45 additions & 0 deletions src/es6/core/frame.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@

import Immutable from 'immutable';
import { saveAs } from 'file-saver';

import { InvalidAxisError } from './exceptions';
import NDFrame from './generic';
import Series from './series';
import { Workbook, Sheet } from './structs';
import { enumerate, nonMergeColumns, intersectingColumns, parseIndex } from './utils';


Expand Down Expand Up @@ -562,6 +564,49 @@ export default class DataFrame extends NDFrame {
return csvString;
}

/**
* Write the `DataFrame` to a Workbook object
*
* @param {string|Workbook} excel_writer
* File path or existing Workbook object
* @param {string} sheetName
* Name of sheet which will contain DataFrame
* @param {boolean} download
* Download the excel file?
*
* @return {Workbook}
*
* @example
*
*/
to_excel(excel_writer, sheetName = 'Sheet1', download = false) {
let wb;
if (excel_writer instanceof Workbook) {
const sheet = new Sheet(this.values);

wb = excel_writer.copy();
wb.addSheet(sheetName, sheet);
} else if (typeof excel_writer === 'string') {
wb = new Workbook();
wb.addSheet(sheetName, new Sheet(this.values));
} else throw new Error('excel_writer must be a file path or Workbook object');

function s2ab(s) {
const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf);
for (let i = 0; i < s.length; i += 1) view[i] = s.charCodeAt(i) && 0xFF;
return buf;
}

if (download) {
saveAs(new Blob([s2ab(wb.writeWorkbook())],
{type: "application/octet-stream"}),
typeof excel_writer === 'string' ? excel_writer : 'StratoDem Download.xlsx');
}

return wb;
}

/**
* Return the sum of the values in the `DataFrame` along the axis
*
Expand Down
160 changes: 160 additions & 0 deletions src/es6/core/structs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* structs
*
* Description:
* Primary author(s):
* Secondary author(s):
*
* Notes:
*
* January 12, 2017
* StratoDem Analytics, LLC
*/

import Immutable from 'immutable';
import XLSX from 'xlsx';

// This does not match the Python pandas

export class Workbook {
/**
* @param {Sheets} sheets
*/
constructor(sheets = new Sheets({})) {
this._sheets = sheets;
}

/**
* @returns {Sheets}
*/
get sheets() {
return this._sheets;
}

get SheetNames() {
return this.sheets.sheetNames.toArray();
}

get Sheets() {
return this.sheets.sheets.toJS();
}

/**
* Create a copy of the Workbook
*
* @returns {Workbook}
*/
copy() {
return new Workbook(this._sheets);
}

/**
* Add a sheet to the Workbook
*
* @param {string} sheetName
* @param {Sheet} sheetContent
*/
addSheet(sheetName, sheetContent) {
this._sheets.addSheet(sheetName, sheetContent);
}

writeWorkbook() {
return XLSX.write(this, {bookType: 'xlsx', bookSST: true, type: 'binary'});
}
}


class Sheets {
constructor(initialSheets = {}) {
this._sheets = Immutable.Map(initialSheets);
this._sheetNames = this._sheets.keySeq().toList();
}

/**
* List of sheet names in the Sheets
*
* @returns {List<number|string>}
*/
get sheetNames() {
return this._sheetNames;
}

/**
* Map of sheetName, Sheet objects
*
* @returns {Map<string, Sheet>}
*/
get sheets() {
return this._sheets;
}

/**
*
* @param {string} sheetName
* @param {Sheet} sheetContent
*/
addSheet(sheetName, sheetContent) {
this._sheets = this._sheets.set(sheetName, sheetContent);
}
}


export class Sheet {
/**
* Construct a Sheet object from a List of Lists of data (from DataFrame.values, e.g.)
*
* @param {List<List>} data
*/
constructor(data) {
this._sheet = this._sheet_from_list_of_lists(data);
}

get sheet() {
return this._sheet;
}

/**
* Get a Sheet object's content from a List of Lists
* As inspiration: view-source:http://sheetjs.com/demos/writexlsx.html
*
* @param data
* @private
*/
_sheet_from_list_of_lists(data) {
const range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
const ws = {};

function datenum(v, date1904) {
const epoch = Date.parse(date1904 ? v + 1462 : v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}

data.forEach((row, idxRow) => {
row.forEach((v, idxCol) => {
if (v === null) return;

if (range.s.r > idxRow) range.s.r = idxRow;
if (range.s.c > idxCol) range.s.c = idxCol;
if (range.e.r < idxRow) range.e.r = idxRow;
if (range.e.c < idxCol) range.e.c = idxCol;

const cell = {v};
const cell_ref = XLSX.utils.encode_cell({c: idxCol, r: idxRow});

if (typeof cell.v === 'number')
cell.t = 'n';
else if (cell.v instanceof Date) {
cell.t = 'n'; cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
} else
cell.t = 's';

ws[cell_ref] = cell;
});
});

if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);

return ws;
}
}
2 changes: 1 addition & 1 deletion src/es6/io/parsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
*/
export const read_csv = (filepath, callback, args) => {
throw new Error('Not implemented');
};
};
18 changes: 18 additions & 0 deletions src/js/__tests__/core/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var _series = require('../../core/series');

var _series2 = _interopRequireDefault(_series);

var _structs = require('../../core/structs');

var _exceptions = require('../../core/exceptions');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Expand Down Expand Up @@ -372,6 +374,22 @@ describe('frame', function () {
});
});

describe('to_excel', function () {
it('converts a pandas DataFrame to a properly formatted Excel file', function () {
var df = new _frame2.default(_immutable2.default.Map({ x: new _series2.default([1, 2, 3]), y: new _series2.default([2, 3, 4]) }));

var originalURL = window.URL;
window.URL = {
createObjectURL: function createObjectURL(blob) {
console.log(blob);return "something";
}
};
console.log(df.to_excel(new _structs.Workbook(), 'my test sheet', true));

window.URL = originalURL;
});
});

describe('where', function () {
it('checks for equality of a scalar and returns a DataFrame', function () {
var df = new _frame2.default([{ x: 1, y: 2 }, { x: 2, y: 3 }]);
Expand Down
52 changes: 52 additions & 0 deletions src/js/core/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

var _fileSaver = require('file-saver');

var _exceptions = require('./exceptions');

var _generic = require('./generic');
Expand All @@ -51,6 +53,8 @@ var _series = require('./series');

var _series2 = _interopRequireDefault(_series);

var _structs = require('./structs');

var _utils = require('./utils');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Expand Down Expand Up @@ -581,6 +585,54 @@ var DataFrame = function (_NDFrame) {
return csvString;
}

/**
* Write the `DataFrame` to a Workbook object
*
* @param {string|Workbook} excel_writer
* File path or existing Workbook object
* @param {string} sheetName
* Name of sheet which will contain DataFrame
* @param {boolean} download
* Download the excel file?
*
* @return {Workbook}
*
* @example
*
*/

}, {
key: 'to_excel',
value: function to_excel(excel_writer) {
var sheetName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'Sheet1';
var download = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;

var wb = void 0;
if (excel_writer instanceof _structs.Workbook) {
var sheet = new _structs.Sheet(this.values);

wb = excel_writer.copy();
wb.addSheet(sheetName, sheet);
} else if (typeof excel_writer === 'string') {
wb = new _structs.Workbook();
wb.addSheet(sheetName, new _structs.Sheet(this.values));
} else throw new Error('excel_writer must be a file path or Workbook object');

function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i < s.length; i += 1) {
view[i] = s.charCodeAt(i) && 0xFF;
}return buf;
}

if (download) {
(0, _fileSaver.saveAs)(new Blob([s2ab(wb.writeWorkbook())], { type: "application/octet-stream" }), typeof excel_writer === 'string' ? excel_writer : 'StratoDem Download.xlsx');
}

return wb;
}

/**
* Return the sum of the values in the `DataFrame` along the axis
*
Expand Down
Loading