Skip to content

Commit 746c4cc

Browse files
authored
Merge pull request #414 from NeonSpork/diff-port
feat: Added diff() function to DataFrame
2 parents 554ddd8 + 8d4c911 commit 746c4cc

File tree

4 files changed

+175
-4
lines changed

4 files changed

+175
-4
lines changed

src/danfojs-base/core/frame.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,86 @@ export default class DataFrame extends NDframe implements DataFrameInterface {
13331333

13341334
}
13351335

1336+
/**
1337+
* Return difference of DataFrame with other.
1338+
* @param other DataFrame, Series, Array or Scalar number (positive numbers are preceding rows, negative are following rows) to compare difference with.
1339+
* @param options.axis 0 or 1. If 0, compute the difference column-wise, if 1, row-wise
1340+
* @param options.inplace Boolean indicating whether to perform the operation inplace or not. Defaults to false
1341+
* @example
1342+
* ```
1343+
* 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'] })
1344+
*
1345+
* // Difference with previous row
1346+
* const df0 = df.diff(1)
1347+
* console.log(df0)
1348+
*
1349+
* // Difference with previous column
1350+
* const df1 = df.diff(1, {axis: 0})
1351+
* console.log(df1)
1352+
*
1353+
* // Difference with previous 3rd previous row
1354+
* const df2 = df.diff(3)
1355+
* console.log(df2)
1356+
*
1357+
* // Difference with following row
1358+
* const df3 = df.diff(-1)
1359+
* console.log(df3)
1360+
*
1361+
* // Difference with another DataFrame
1362+
* const df4 = df.diff(df3)
1363+
* console.log(df4)
1364+
* ```
1365+
*/
1366+
diff(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame
1367+
diff(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void {
1368+
const { inplace, axis } = { inplace: false, axis: 1, ...options }
1369+
1370+
if (this.$frameIsNotCompactibleForArithmeticOperation()) {
1371+
throw Error("TypeError: diff operation is not supported for string dtypes");
1372+
}
1373+
1374+
if ([0, 1].indexOf(axis) === -1) {
1375+
throw Error("ParamError: Axis must be 0 or 1");
1376+
}
1377+
1378+
if (other === 0) {
1379+
return this;
1380+
}
1381+
1382+
if (typeof other === "number") {
1383+
let origDF = this.copy() as DataFrame;
1384+
if (axis === 0) {
1385+
origDF = origDF.T;
1386+
}
1387+
const originalTensor = origDF.tensor.clone();
1388+
const unit = new Array(originalTensor.shape[originalTensor.rank - 1]).fill(NaN);
1389+
let diffArray: any[] = originalTensor.arraySync();
1390+
if (other > 0) {
1391+
for (let i = 0; i < other; i++) {
1392+
diffArray.unshift(unit);
1393+
diffArray.pop();
1394+
}
1395+
}
1396+
else if (other < 0) {
1397+
for (let i = 0; i > other; i--) {
1398+
diffArray.push(unit);
1399+
diffArray.shift();
1400+
}
1401+
}
1402+
const diffTensor = tensorflow.tensor2d(diffArray, originalTensor.shape);
1403+
const diffDF = this.$MathOps([originalTensor, diffTensor], "sub", inplace) as DataFrame;
1404+
if (axis === 0) {
1405+
return diffDF.T;
1406+
}
1407+
return diffDF;
1408+
}
1409+
1410+
if (other instanceof DataFrame || other instanceof Series) {
1411+
const tensors = this.$getTensorsForArithmeticOperationByAxis(other, axis);
1412+
return this.$MathOps(tensors, "sub", inplace);
1413+
}
1414+
}
1415+
13361416
/**
13371417
* Return the absolute value of elements in a DataFrame.
13381418
* @param options.inplace Boolean indicating whether to perform the operation inplace or not. Defaults to false

src/danfojs-base/shared/types.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ export interface SeriesInterface extends NDframeInterface {
184184
toCSV(options?: CsvOutputOptionsBrowser): string | void
185185
toJSON(options?: JsonOutputOptionsBrowser): object | void
186186
toExcel(options?: ExcelOutputOptionsBrowser): void
187-
iat(index: number): number | string | boolean | undefined
188-
at(index: string | number): number | string | boolean | undefined
187+
iat(index: number): number | string | boolean | undefined
188+
at(index: string | number): number | string | boolean | undefined
189189
}
190190

191191
//Start of DataFrame class types
@@ -218,6 +218,7 @@ export interface DataFrameInterface extends NDframeInterface {
218218
div(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
219219
pow(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
220220
mod(other: DataFrame | Series | number | number[], options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
221+
diff(other: DataFrame | Series | number[] | number, options?: { axis?: 0 | 1, inplace?: boolean }): DataFrame | void
221222
mean(options?: { axis?: 0 | 1 }): Series
222223
median(options?: { axis?: 0 | 1 }): Series
223224
mode(options?: { axis?: 0 | 1, keep?: number }): Series
@@ -329,8 +330,8 @@ export interface DataFrameInterface extends NDframeInterface {
329330
toCSV(options?: CsvOutputOptionsBrowser): string | void
330331
toJSON(options?: JsonOutputOptionsBrowser): object | void
331332
toExcel(options?: ExcelOutputOptionsBrowser): void
332-
iat(row: number, column: number): number | string | boolean | undefined
333-
at(row: string | number, column: string): number | string | boolean | undefined
333+
iat(row: number, column: number): number | string | boolean | undefined
334+
at(row: string | number, column: string): number | string | boolean | undefined
334335
}
335336

336337
export interface DateTime {

src/danfojs-browser/tests/core/frame.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,51 @@ describe("DataFrame", function () {
10181018

10191019
});
10201020

1021+
describe("diff", function () {
1022+
it("Return same DataFrame if other === 0", function () {
1023+
const data = [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ];
1024+
const df = new dfd.DataFrame(data);
1025+
assert.deepEqual((df.diff(0)).values, [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ]);
1026+
});
1027+
it("Return difference of DataFrame with previous row", function () {
1028+
const data = [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ];
1029+
const df = new dfd.DataFrame(data);
1030+
assert.deepEqual((df.diff(1)).values, [ [ NaN, NaN, NaN ], [ 10, 8, 6 ], [ -9, -8, -7 ] ]);
1031+
});
1032+
it("Return difference of DataFrame with following row", function () {
1033+
const data = [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ];
1034+
const df = new dfd.DataFrame(data);
1035+
assert.deepEqual((df.diff(-1)).values, [ [ -10, -8, -6 ], [ 9, 8, 7 ], [ NaN, NaN, NaN ] ]);
1036+
});
1037+
it("Return difference of a DataFrame with a Series along default axis 1", function () {
1038+
const data = [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ];
1039+
const sf = new dfd.Series([ 1, 2, 1 ]);
1040+
const df = new dfd.DataFrame(data);
1041+
assert.deepEqual((df.diff(sf)).values, [ [ -1, 0, 3 ], [ 9, 8, 9 ], [ 0, 0, 2 ] ]);
1042+
});
1043+
it("Return difference of a DataFrame with along axis 0 (column-wise), previous column", function () {
1044+
const data = [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ];
1045+
const df = new dfd.DataFrame(data);
1046+
assert.deepEqual((df.diff(1, { axis: 0 })).values, [ [ NaN, 2, 2 ], [ NaN, 0, 0 ], [ NaN, 1, 1 ] ]);
1047+
});
1048+
it("Return difference of a DataFrame with along axis 0 (column-wise), following column", function () {
1049+
const data = [ [ 0, 2, 4 ], [ 10, 10, 10 ], [ 1, 2, 3 ] ];
1050+
const df = new dfd.DataFrame(data);
1051+
assert.deepEqual((df.diff(-1, { axis: 0 })).values, [ [ -2, -2, NaN ], [ 0, 0, NaN ], [ -1, -1, NaN ] ]);
1052+
});
1053+
it("Return difference of a DataFrame with another DataFrame along default axis 1", function () {
1054+
const df1 = new dfd.DataFrame([ [ 0, 2, 4 ], [ 3, 10, 4 ] ]);
1055+
const df2 = new dfd.DataFrame([ [ -1, -2, 4 ], [ 10, 5, 0 ] ]);
1056+
assert.deepEqual((df1.diff(df2)).values, [ [ 1, 4, 0 ], [ -7, 5, 4 ] ]);
1057+
});
1058+
it("Throw error if DataFrame for diff contains string", function () {
1059+
const df = new dfd.DataFrame([ [ "words", "words", "words" ], [ "words", "words", "words" ] ]);
1060+
assert.throws(() => {
1061+
df.diff(1);
1062+
}, Error, "TypeError: diff operation is not supported for string dtypes");
1063+
});
1064+
});
1065+
10211066
describe("mean", function () {
10221067
it("Returns the mean of a DataFrame (Default axis is [1:column])", function () {
10231068
const data = [ [ 0, 2, 4 ],

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,51 @@ describe("DataFrame", function () {
992992

993993
});
994994

995+
describe("diff", function () {
996+
it("Return same DataFrame if other === 0", function () {
997+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
998+
const df = new DataFrame(data);
999+
assert.deepEqual((df.diff(0) as DataFrame).values, [[0, 2, 4], [10, 10, 10], [1, 2, 3]]);
1000+
});
1001+
it("Return difference of DataFrame with previous row", function () {
1002+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1003+
const df = new DataFrame(data);
1004+
assert.deepEqual((df.diff(1) as DataFrame).values, [[NaN, NaN, NaN], [10, 8, 6], [-9, -8, -7]]);
1005+
});
1006+
it("Return difference of DataFrame with following row", function () {
1007+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1008+
const df = new DataFrame(data);
1009+
assert.deepEqual((df.diff(-1) as DataFrame).values, [[-10, -8, -6], [9, 8, 7], [NaN, NaN, NaN]]);
1010+
});
1011+
it("Return difference of a DataFrame with a Series along default axis 1", function () {
1012+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1013+
const sf = new Series([1, 2, 1]);
1014+
const df = new DataFrame(data);
1015+
assert.deepEqual((df.diff(sf) as DataFrame).values, [[-1, 0, 3], [9, 8, 9], [0, 0, 2]]);
1016+
});
1017+
it("Return difference of a DataFrame with along axis 0 (column-wise), previous column", function () {
1018+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1019+
const df = new DataFrame(data);
1020+
assert.deepEqual((df.diff(1, { axis: 0 }) as DataFrame).values, [[NaN, 2, 2], [NaN, 0, 0], [NaN, 1, 1]]);
1021+
});
1022+
it("Return difference of a DataFrame with along axis 0 (column-wise), following column", function () {
1023+
const data = [[0, 2, 4], [10, 10, 10], [1, 2, 3]];
1024+
const df = new DataFrame(data);
1025+
assert.deepEqual((df.diff(-1, { axis: 0 }) as DataFrame).values, [[-2, -2, NaN], [0, 0, NaN], [-1, -1, NaN]]);
1026+
});
1027+
it("Return difference of a DataFrame with another DataFrame along default axis 1", function () {
1028+
const df1 = new DataFrame([[0, 2, 4], [3, 10, 4]]);
1029+
const df2 = new DataFrame([[-1, -2, 4], [10, 5, 0]]);
1030+
assert.deepEqual((df1.diff(df2) as DataFrame).values, [[1, 4, 0], [-7, 5, 4]]);
1031+
});
1032+
it("Throw error if DataFrame for diff contains string", function () {
1033+
const df = new DataFrame([["words", "words", "words"], ["words", "words", "words"]]);
1034+
assert.throws(() => {
1035+
df.diff(1);
1036+
}, Error, "TypeError: diff operation is not supported for string dtypes");
1037+
});
1038+
});
1039+
9951040
describe("mod", function () {
9961041
it("Return modulus of DataFrame with a single Number", function () {
9971042
const data = [[0, 2, 4], [360, 180, 360]];

0 commit comments

Comments
 (0)