Skip to content

Commit

Permalink
feat(nodejs): add ceil & clip (#2410)
Browse files Browse the repository at this point in the history
  • Loading branch information
universalmind303 committed Jan 17, 2022
1 parent 76a2cbd commit dcbd973
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 51 deletions.
2 changes: 1 addition & 1 deletion nodejs-polars/@types/jest.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {DataFrame} from "@polars/dataframe";
import {Series} from "@polars/series";
import {Series} from "@polars/series/series";

declare global {
namespace jest {
Expand Down
84 changes: 83 additions & 1 deletion nodejs-polars/__tests__/expr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ describe("expr", () => {
.as("abs")).getColumn("abs");
expect(actual).toSeriesEqual(expected);
});
test.todo("aggGroups");
test("alias", () => {
const name = "alias";
const actual = pl.select(lit("a").alias(name));
Expand Down Expand Up @@ -1992,3 +1991,86 @@ describe("arithmetic", () => {
expect(actual1).toEqual(expected);
});
});

describe("Round<T>", () => {

test("ceil", () => {
const df = pl.Series
.from([1.1, 2.2])
.as("ceil")
.toFrame();

const expected = pl.DataFrame({
"ceil": [2, 3]
});

const seriesActual = df
.getColumn("ceil")
.ceil()
.toFrame();

const actual = df.select(
col("ceil")
.ceil()
.as("ceil")
);

expect(actual).toFrameEqual(expected);
expect(seriesActual).toFrameEqual(expected);
});

test("clip", () => {
const df = pl.DataFrame({"a": [1, 2, 3, 4, 5]});
const expected = [2, 2, 3, 4, 4];
const exprActual = df
.select(pl.col("a").clip(2, 4))
.getColumn("a")
.toArray();

const seriesActual = pl.Series([1, 2, 3, 4, 5])
.clip(2, 4)
.toArray();

expect(exprActual).toEqual(expected);
expect(seriesActual).toEqual(expected);
});
test("floor", () => {
const df = pl.Series
.from([1.1, 2.2])
.as("floor")
.toFrame();

const expected = pl.DataFrame({
"floor": [1, 2]
});

const seriesActual = df
.getColumn("floor")
.floor()
.toFrame();

const actual = df.select(
col("floor")
.floor()
.as("floor")
);

expect(actual).toFrameEqual(expected);
expect(seriesActual).toFrameEqual(expected);
});
test("round", () => {});
test("round invalid dtype", () => {
const df = pl.DataFrame({a: ["1", "2"]});
const seriesFn = () => df["a"].round({decimals: 1});
const exprFn = () => df.select(col("a").round({decimals: 1}));
expect(seriesFn).toThrow();
expect(exprFn).toThrow();
});
test("clip invalid dtype", () => {
const df = pl.DataFrame({a: ["1", "2"]});
const seriesFn = () => df["a"].clip({min:1, max:2});
const exprFn = () => df.select(col("a").clip(1, 2));
expect(seriesFn).toThrow();
expect(exprFn).toThrow();
});
});
3 changes: 2 additions & 1 deletion nodejs-polars/polars/dataframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {GroupBy} from "./groupby";
import {LazyDataFrame} from "./lazy/dataframe";
import {concat} from "./functions";
import {Expr} from "./lazy/expr";
import {Series, seriesWrapper} from "./series";
import {Series, seriesWrapper} from "./series/series";
import {Stream, Writable} from "stream";
import {isExternal} from "util/types";

Expand Down Expand Up @@ -48,6 +48,7 @@ type WriteIPCOptions = {
compression?: "uncompressed" | "lz4" | "zstd"
};


/**
*
A DataFrame is a two-dimensional data structure that represents data as a table
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/functions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-redeclare */
import {jsTypeToPolarsType} from "./internals/construction";
import {Series, seriesWrapper} from "./series";
import {Series, seriesWrapper} from "./series/series";
import {DataFrame, dfWrapper} from "./dataframe";
import pli from "./internals/polars_internal";
import {isDataFrameArray, isSeriesArray} from "./utils";
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as series from "./series";
import * as series from "./series/series";
import * as df from "./dataframe";
import { DataType } from "./datatypes";
import * as func from "./functions";
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/internals/construction.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pli from "./polars_internal";
import { DataType, polarsTypeToConstructor } from "../datatypes";
import { isTypedArray } from "util/types";
import {Series} from "../series";
import {Series} from "../series/series";


export const jsTypeToPolarsType = (value: unknown): DataType => {
Expand Down
27 changes: 12 additions & 15 deletions nodejs-polars/polars/lazy/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ import {
INSPECT_SYMBOL
} from "../utils";
import {isExternal} from "util/types";
import {Series} from "../series";
import {Series} from "../series/series";

import * as expr from "./expr/";
import {Arithmetic, Comparison, Cumulative, Rolling} from "../shared_traits";
import {Arithmetic, Comparison, Cumulative, Rolling, Round} from "../shared_traits";

export interface Expr extends
Rolling<Expr>,
Arithmetic<Expr>,
Comparison<Expr>,
Cumulative<Expr> {
Cumulative<Expr>,
Round<Expr> {
/** @ignore */
_expr: any;
get date(): expr.Datetime;
Expand Down Expand Up @@ -180,12 +181,6 @@ export interface Expr extends
first(): Expr
/** @see {@link Expr.explode} */
flatten(): Expr
/**
* Floor underlying floating point array to the lowest integers smaller or equal to the float value.
*
* Only works on floating point Series
*/
floor(): Expr
/** Fill missing values with the latest seen values */
forwardFill(): Expr
/** Hash the Series. */
Expand Down Expand Up @@ -431,12 +426,6 @@ export interface Expr extends
repeatBy(by: Expr | string): Expr
/** Reverse the arrays in the list */
reverse(): Expr
/**
* Round underlying floating point data by `decimals` digits.
* @param decimals Number of decimals to round by.
*/
round(decimals?: number): Expr
round({decimals}: {decimals: number}): Expr
/**
* Shift the values by a given period and fill the parts that will be empty due to this operation
* @param periods number of places to shift (may be negative).
Expand Down Expand Up @@ -591,6 +580,14 @@ const _Expr = (_expr: any): Expr => {
cast(dtype, strict=false) {
return wrap("cast", {dtype, strict});
},
ceil: wrapNullArgs("ceil"),
clip(arg, max?){
if(typeof arg === "number") {
return wrap("clip", {min: arg, max});
} else {
return wrap("clip", arg);
}
},
count: wrapNullArgs("count"),
cumCount: wrapUnaryWithDefault("cumCount", "reverse", false),
cumMax: wrapUnaryWithDefault("cumMax", "reverse", false),
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/lazy/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Expr, exprToLitOrExpr} from "./expr";
import {Series} from "../series";
import {Series} from "../series/series";
import { DataFrame } from "../dataframe";
import { ExprOrString, range, selectionToExprList} from "../utils";
import pli from "../internals/polars_internal";
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/series/datetime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pli from "../internals/polars_internal";
import {JsSeries, Series, seriesWrapper} from "../series";
import {JsSeries, Series, seriesWrapper} from "./series";

export interface DateTimeFunctions {
/**
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/series/list.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {JsSeries, Series, seriesWrapper} from "../series";
import {JsSeries, Series, seriesWrapper} from "./series";
import {col} from "../lazy/functions";

export interface ListFunctions<T> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import pli from "./internals/polars_internal";
import {arrayToJsSeries} from "./internals/construction";
import {DataType, DtypeToPrimitive, DTYPE_TO_FFINAME, Optional} from "./datatypes";
import {DataFrame, dfWrapper} from "./dataframe";
import {StringFunctions} from "./series/string";
import {ListFunctions} from "./series/list";
import {DateTimeFunctions} from "./series/datetime";
import {InvalidOperationError, todo} from "./error";
import {RankMethod} from "./utils";
import {col} from "./lazy/functions";
import pli from "../internals/polars_internal";
import {arrayToJsSeries} from "../internals/construction";
import {DataType, DtypeToPrimitive, DTYPE_TO_FFINAME, Optional} from "../datatypes";
import {DataFrame, dfWrapper} from "../dataframe";
import {StringFunctions} from "./string";
import {ListFunctions} from "./list";
import {DateTimeFunctions} from "./datetime";
import {InvalidOperationError, todo} from "../error";
import {RankMethod} from "../utils";
import {col} from "../lazy/functions";
import {isExternal, isTypedArray} from "util/types";
import {Arithmetic, Comparison, Cumulative, Rolling} from "./shared_traits";
import {Arithmetic, Comparison, Cumulative, Rolling, Round} from "../shared_traits";

const inspect = Symbol.for("nodejs.util.inspect.custom");

Expand All @@ -21,13 +21,13 @@ type ArrayLikeOrDataType<T, U> = ArrayLike<DataTypeOrValue<T, U>>
/** @ignore */
export type JsSeries = any;


export interface Series<T> extends
ArrayLike<T>,
Rolling<Series<T>>,
Arithmetic<Series<T>>,
Comparison<Series<boolean>>,
Cumulative<Series<T>> {
Cumulative<Series<T>>,
Round<Series<T>> {
/** @ignore */
_series: JsSeries;
name: string
Expand Down Expand Up @@ -281,7 +281,6 @@ export interface Series<T> extends
*/
filter(predicate: Series<boolean>): Series<T>
filter({predicate}: {predicate: Series<boolean>}): Series<T>
floor(): Series<T>
get(index: number): T
getIndex(n: number): T
/**
Expand Down Expand Up @@ -721,15 +720,7 @@ export interface Series<T> extends
rename(name: string, inPlace: boolean): void
rename({name, inPlace}: {name: string, inPlace?: boolean}): void
rename({name, inPlace}: {name: string, inPlace: true}): void
/**
* Round underlying floating point data by `decimals` digits.
*
* Similar functionality to javascript `toFixed`
* @param decimals number of decimals to round by.
*
* */
round<T>(decimals: number): T extends number ? Series<number> : never
round(opt: {decimals: number}): T extends number ? Series<number> : never


sample(opts: {n: number, withReplacement?: boolean}): Series<T>
sample(opts: {frac: number, withReplacement?: boolean}): Series<T>
Expand Down Expand Up @@ -1249,7 +1240,6 @@ export const seriesWrapper = <T>(_s: JsSeries): Series<T> => {
wrap("filter", {filter: predicate._series}) :
wrap("filter", {filter: predicate.predicate._series});
},
floor: noArgWrap("floor"),
get(field) {
return dtypeAccessor(unwrap)("get", {field, key: "index"});
},
Expand Down Expand Up @@ -1421,6 +1411,9 @@ export const seriesWrapper = <T>(_s: JsSeries): Series<T> => {
.select(col(this.name).rollingSkew(windowSize, bias))
.getColumn(this.name);
},

floor: noArgWrap("floor"),
ceil: noArgWrap("ceil"),
round(opt): any {
if (this.isNumeric()) {
if (typeof opt === "number") {
Expand All @@ -1433,6 +1426,23 @@ export const seriesWrapper = <T>(_s: JsSeries): Series<T> => {
throw new InvalidOperationError("round", this.dtype);
}
},
clip(arg, max?) {
const dtypes = [
DataType.Int32,
DataType.Int64,
DataType.Float32,
DataType.Float64,
DataType.UInt32
];
if(dtypes.includes(this.dtype)) {
throw new InvalidOperationError("clip", this.dtype);
}
if(typeof arg === "number") {
return wrap("clip", {min: arg, max});
} else {
return wrap("clip", arg);
}
},
sample(opts?, frac?, withReplacement = false) {
if (opts?.n !== undefined || opts?.frac !== undefined) {
return this.sample(opts.n, opts.frac, opts.withReplacement);
Expand Down
2 changes: 1 addition & 1 deletion nodejs-polars/polars/series/string.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pli from "../internals/polars_internal";
import {DataType} from "../datatypes";
import {JsSeries, Series, seriesWrapper} from "../series";
import {JsSeries, Series, seriesWrapper} from "./series";
import {regexToString} from "../utils";
import {col} from "../lazy/functions";

Expand Down
32 changes: 32 additions & 0 deletions nodejs-polars/polars/shared_traits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,35 @@ export interface Rolling<T> {
rollingSkew(windowSize: number, bias?: boolean): T
rollingSkew({windowSize, bias}: {windowSize: number, bias?: boolean}): T
}


export interface Round<T> {
/**
* Round underlying floating point data by `decimals` digits.
*
* Similar functionality to javascript `toFixed`
* @param decimals number of decimals to round by.
*/
round(decimals: number): T
round(options: {decimals: number}): T
/**
* Floor underlying floating point array to the lowest integers smaller or equal to the float value.
* Only works on floating point Series
*/
floor(): T;
/**
* Ceil underlying floating point array to the heighest integers smaller or equal to the float value.
* Only works on floating point Series
*/
ceil(): T;

/**
* Clip (limit) the values in an array to any value that fits in 64 floating poitns range.
* Only works for the following dtypes: {Int32, Int64, Float32, Float64, UInt32}.
* If you want to clip other dtypes, consider writing a when -> then -> otherwise expression
* @param min Minimum value
* @param max Maximum value
*/
clip(min: number, max: number): T
clip(options: {min: number, max: number})
}
2 changes: 1 addition & 1 deletion nodejs-polars/polars/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Expr, exprToLitOrExpr} from "./lazy/expr";
import type {Series} from "./series";
import type {Series} from "./series/series";
import type {DataFrame} from "./dataframe";
import path from "path";
import {isExternal, isRegExp} from "util/types";
Expand Down

0 comments on commit dcbd973

Please sign in to comment.