Skip to content

Commit 9683001

Browse files
committed
feat: pct_change function
1 parent 52ca0c4 commit 9683001

3 files changed

Lines changed: 182 additions & 0 deletions

File tree

src/danfojs-base/core/frame.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ export default class DataFrame extends NDframe implements DataFrameInterface {
329329
case 'div':
330330
tensorResult = tensors[0].div(tensors[1])
331331
break;
332+
case 'divNoNan':
333+
tensorResult = tensors[0].divNoNan(tensors[1])
334+
break;
332335
case 'mul':
333336
tensorResult = tensors[0].mul(tensors[1])
334337
break;
@@ -726,6 +729,36 @@ export default class DataFrame extends NDframe implements DataFrameInterface {
726729
return this.$MathOps(tensors, "div", inplace)
727730

728731

732+
}
733+
734+
/**
735+
* Return division of DataFrame with other, returns 0 if denominator is 0.
736+
* @param other DataFrame, Series, Array or Scalar number to divide with.
737+
* @param options.axis 0 or 1. If 0, compute the division column-wise, if 1, row-wise
738+
* @param options.inplace Boolean indicating whether to perform the operation inplace or not. Defaults to false
739+
* @example
740+
* ```
741+
* const df = new DataFrame([[1, 2], [3, 4]], { columns: ['A', 'B'] })
742+
* const df2 = df.divNoNan(2)
743+
* df2.print()
744+
* ```
745+
*/
746+
divNoNan(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame
747+
divNoNan(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void {
748+
const { inplace, axis } = { inplace: false, axis: 1, ...options }
749+
750+
if (this.$frameIsNotCompactibleForArithmeticOperation()) {
751+
throw Error("TypeError: div operation is not supported for string dtypes");
752+
}
753+
754+
if ([0, 1].indexOf(axis) === -1) {
755+
throw Error("ParamError: Axis must be 0 or 1");
756+
}
757+
758+
const tensors = this.$getTensorsForArithmeticOperationByAxis(other, axis);
759+
return this.$MathOps(tensors, "divNoNan", inplace)
760+
761+
729762
}
730763

731764
/**
@@ -1333,6 +1366,109 @@ export default class DataFrame extends NDframe implements DataFrameInterface {
13331366

13341367
}
13351368

1369+
/**
1370+
* Return percentage difference of DataFrame with other.
1371+
* @param other DataFrame, Series, Array or Scalar number (positive numbers are preceding rows, negative are following rows) to compare difference with.
1372+
* @param options.axis 0 or 1. If 0, compute the difference column-wise, if 1, row-wise
1373+
* @param options.inplace Boolean indicating whether to perform the operation inplace or not. Defaults to false
1374+
* @example
1375+
* ```
1376+
* const df = new DataFrame([[1, 2, 3, 4, 5, 6], [1, 1, 2, 3, 5, 8], [1, 4, 9, 16, 25, 36]], { columns: ['A', 'B', 'C'] })
1377+
*
1378+
* // Difference with previous row
1379+
* const df0 = df.pct_change(1)
1380+
* console.log(df0)
1381+
*
1382+
* // Difference with previous column
1383+
* const df1 = df.pct_change(1, {axis: 0})
1384+
* console.log(df1)
1385+
*
1386+
* // Difference with previous 3rd previous row
1387+
* const df2 = df.pct_change(3)
1388+
* console.log(df2)
1389+
*
1390+
* // Difference with following row
1391+
* const df3 = df.pct_change(-1)
1392+
* console.log(df3)
1393+
*
1394+
* // Difference with another DataFrame
1395+
* const df4 = df.pct_change(df3)
1396+
* console.log(df4)
1397+
* ```
1398+
*/
1399+
pct_change(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame
1400+
pct_change(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void {
1401+
const { inplace, axis } = { inplace: false, axis: 1, ...options }
1402+
1403+
if (this.$frameIsNotCompactibleForArithmeticOperation()) {
1404+
throw Error("TypeError: pct_change operation is not supported for string dtypes");
1405+
}
1406+
1407+
if ([0, 1].indexOf(axis) === -1) {
1408+
throw Error("ParamError: Axis must be 0 or 1");
1409+
}
1410+
1411+
if (other === 0) {
1412+
return this;
1413+
}
1414+
1415+
if (typeof other === "number" && axis === 1) {
1416+
const orig_tensor = this.tensor.clone();
1417+
let unit = [NaN];
1418+
for (let i = 1; i < orig_tensor.shape[orig_tensor.rank - 1]; i++) {
1419+
unit.push(NaN);
1420+
}
1421+
let pct_array: any[] = orig_tensor.arraySync();
1422+
if (other > 0) {
1423+
for (let i = 0; i < other; i++) {
1424+
pct_array.unshift(unit);
1425+
pct_array.pop();
1426+
}
1427+
}
1428+
else if (other < 0) {
1429+
for (let i = 0; i > other; i--) {
1430+
pct_array.push(unit);
1431+
pct_array.shift();
1432+
}
1433+
}
1434+
const pct_tensor = tensorflow.tensor2d(pct_array, orig_tensor.shape);
1435+
const pct_df = (this.$MathOps([orig_tensor, pct_tensor], "divNoNan", inplace) as DataFrame).sub(1);
1436+
return pct_df;
1437+
}
1438+
1439+
if (typeof other === "number" && axis === 0) {
1440+
const orig_df = new DataFrame(this.tensor.clone());
1441+
const orig_tensor = orig_df.T.tensor.clone();
1442+
let unit = [NaN];
1443+
for (let i = 1; i < orig_tensor.shape[orig_tensor.rank - 1]; i++) {
1444+
unit.push(NaN);
1445+
}
1446+
let pct_array: any[] = orig_tensor.arraySync();
1447+
if (other > 0) {
1448+
for (let i = 0; i < other; i++) {
1449+
pct_array.unshift(unit);
1450+
pct_array.pop();
1451+
}
1452+
}
1453+
else if (other < 0) {
1454+
for (let i = 0; i > other; i--) {
1455+
pct_array.push(unit);
1456+
pct_array.shift();
1457+
}
1458+
}
1459+
const pct_tensor = tensorflow.tensor2d(pct_array, orig_tensor.shape);
1460+
const pct_df_flipped = (this.$MathOps([orig_tensor, pct_tensor], "divNoNan", inplace) as DataFrame).sub(1);
1461+
const pct_df = pct_df_flipped.T;
1462+
return pct_df;
1463+
}
1464+
1465+
if (other instanceof DataFrame || other instanceof Series) {
1466+
const tensors = this.$getTensorsForArithmeticOperationByAxis(other, axis);
1467+
const pct_df = (this.$MathOps(tensors, "divNoNan", inplace) as DataFrame).sub(1);
1468+
return pct_df;
1469+
}
1470+
}
1471+
13361472
/**
13371473
* Return difference of DataFrame with other.
13381474
* @param other DataFrame, Series, Array or Scalar number (positive numbers are preceding rows, negative are following rows) to compare difference with.

src/danfojs-base/shared/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ export interface DataFrameInterface extends NDframeInterface {
216216
sub(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
217217
mul(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
218218
div(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
219+
divNoNan(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
219220
pow(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
220221
mod(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
221222
diff(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void

src/danfojs-node/test/core/frame.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,51 @@ describe("DataFrame", function () {
10371037
});
10381038
});
10391039

1040+
describe("pct_change", function () {
1041+
it("Return same DataFrame if other === 0", function () {
1042+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1043+
const df = new DataFrame(data);
1044+
assert.deepEqual((df.pct_change(0) as DataFrame).values, [[0, 2, 4], [10, 10, 10], [1, 2, 3]]);
1045+
});
1046+
it("Return difference in percentage of DataFrame with previous row", function () {
1047+
const data = [[90], [91], [85]];
1048+
const df = new DataFrame(data);
1049+
assert.deepEqual((df.pct_change(1) as DataFrame).values, [[NaN], [0.011111], [-0.065934]]);
1050+
});
1051+
it("Return difference in percentage of DataFrame with following row", function () {
1052+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1053+
const df = new DataFrame(data);
1054+
assert.deepEqual((df.pct_change(-1) as DataFrame).values, [[-1, -0.8, -0.6], [9, 4, 2.333333], [NaN, NaN, NaN]]);
1055+
});
1056+
it("Return difference in percentage of a DataFrame with a Series along default axis 1", function () {
1057+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1058+
const sf = new Series([1, 2, 1]);
1059+
const df = new DataFrame(data);
1060+
assert.deepEqual((df.pct_change(sf) as DataFrame).values, [[-1, 0, 3], [9, 4, 9], [0, 0, 2]]);
1061+
});
1062+
it("Return difference in percentage of a DataFrame with along axis 0 (column-wise), previous column", function () {
1063+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1064+
const df = new DataFrame(data);
1065+
assert.deepEqual((df.pct_change(1, { axis: 0 }) as DataFrame).values, [[NaN, -1, 1], [NaN, 0, 0], [NaN, 1, 0.5]]);
1066+
});
1067+
it("Return difference in percentage of a DataFrame with along axis 0 (column-wise), following column", function () {
1068+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1069+
const df = new DataFrame(data);
1070+
assert.deepEqual((df.pct_change(-1, { axis: 0 }) as DataFrame).values, [[-1, -0.5, NaN], [0, 0, NaN], [-0.5, -0.333333, NaN]]);
1071+
});
1072+
it("Return difference in percentage of a DataFrame with another DataFrame along default axis 1", function () {
1073+
const df1 = new DataFrame([[0, 2, 4], [3, 10, 4]]);
1074+
const df2 = new DataFrame([[-1, -2, 4], [10, 5, 0]]);
1075+
assert.deepEqual((df1.pct_change(df2) as DataFrame).values, [[-1, -2, 0], [-0.7, 1, -1]]);
1076+
});
1077+
it("Throw error if DataFrame for pct_change contains string", function () {
1078+
const df = new DataFrame([["words", "words", "words"], ["words", "words", "words"]]);
1079+
assert.throws(() => {
1080+
df.pct_change(1);
1081+
}, Error, "TypeError: pct_change operation is not supported for string dtypes");
1082+
});
1083+
});
1084+
10401085
describe("mod", function () {
10411086
it("Return modulus of DataFrame with a single Number", function () {
10421087
const data = [[0, 2, 4], [360, 180, 360]];

0 commit comments

Comments
 (0)