From e021cc0c8b233bb7c77925f6f205847e8745b0af Mon Sep 17 00:00:00 2001 From: Cory Grinstead Date: Mon, 27 Mar 2023 18:26:15 -0500 Subject: [PATCH] docs: improve doc structure (#33) * fix build errors * fix build errors * docs: wip - improve doc structure * update tsconfig * docs: add CONTRIBUTING.md * use rome instead of eslint * update yarn lock --- .eslintrc.json | 82 -- .yarn/install-state.gz | Bin 0 -> 427845 bytes @types/jest.d.ts | 10 +- CONTRIBUTING.md | 71 + README.md | 22 +- __tests__/complex_types.test.ts | 5 +- __tests__/dataframe.test.ts | 1844 ++++++++++++----------- __tests__/datelike.test.ts | 151 +- __tests__/expr.test.ts | 3 +- __tests__/functions.test.ts | 43 +- __tests__/groupby.test.ts | 267 ++-- __tests__/io.test.ts | 200 +-- __tests__/lazy_functions.test.ts | 289 ++-- __tests__/lazyframe.test.ts | 1222 +++++++++------- __tests__/serde.test.ts | 6 +- __tests__/series.test.ts | 644 ++++---- __tests__/setup.ts | 71 +- __tests__/struct.test.ts | 32 +- __tests__/whenthen.test.ts | 37 +- package.json | 25 +- polars/cfg.ts | 28 +- polars/dataframe.ts | 1219 ++++++++-------- polars/datatypes/datatype.ts | 115 +- polars/datatypes/field.ts | 14 +- polars/datatypes/index.ts | 73 +- polars/error.ts | 2 - polars/functions.ts | 53 +- polars/groupby.ts | 194 +-- polars/index.ts | 108 +- polars/internals/construction.ts | 225 ++- polars/io.ts | 36 +- polars/lazy/dataframe.ts | 142 +- polars/lazy/expr.ts | 1093 -------------- polars/lazy/expr/datetime.ts | 101 +- polars/lazy/expr/index.ts | 1141 ++++++++++++++- polars/lazy/expr/list.ts | 6 +- polars/lazy/expr/string.ts | 46 +- polars/lazy/expr/struct.ts | 19 +- polars/lazy/functions.ts | 161 +- polars/lazy/groupby.ts | 49 +- polars/lazy/index.ts | 17 +- polars/lazy/whenthen.ts | 43 +- polars/series/datetime.ts | 5 +- polars/series/{series.ts => index.ts} | 661 +++++---- polars/series/list.ts | 7 +- polars/series/string.ts | 141 +- polars/series/struct.ts | 19 +- polars/shared_traits.ts | 554 +++++-- polars/types.ts | 215 +++ polars/utils.ts | 65 +- rome.json | 30 + rust-toolchain | 1 - rust-toolchain.toml | 2 + tsconfig.json | 22 +- yarn.lock | 1939 ++----------------------- 55 files changed, 6408 insertions(+), 7162 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 .yarn/install-state.gz create mode 100644 CONTRIBUTING.md delete mode 100644 polars/lazy/expr.ts rename polars/series/{series.ts => index.ts} (76%) create mode 100644 polars/types.ts create mode 100644 rome.json delete mode 100644 rust-toolchain create mode 100644 rust-toolchain.toml diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 4726b079a..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "ignorePatterns": [ - "**/*.js" - ], - "env": { - "browser": true, - "es2021": true, - "jest/globals": true - }, - "globals": { - "Buffer": "readonly", - "process": "writable" - }, - "extends": [ - "eslint:recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint", - "jest" - ], - "rules": { - "arrow-spacing": "error", - "eol-last": "error", - "no-inner-declarations": "off", - "func-call-spacing": "off", - "no-duplicate-imports": "off", - "comma-spacing": "off", - "quotes": [ - "error", - "double", - { - "allowTemplateLiterals": true, - "avoidEscape": true - } - ], - "no-redeclare": "off", - "no-trailing-spaces": "error", - "semi": [ - "error", - "always" - ], - "indent": [ - "error", - 2, - { - "MemberExpression": 1 - } - ], - "no-dupe-class-members": "off", - "no-unused-vars": "off", - "newline-per-chained-call": [ - "error", - { - "ignoreChainWithDepth": 2 - } - ], - "no-multiple-empty-lines": [ - "error", - { - "max": 2, - "maxEOF": 0 - } - ], - "padding-line-between-statements": [ - "error", - { - "blankLine": "always", - "prev": "*", - "next": "return" - } - ], - "@typescript-eslint/func-call-spacing": "error", - "@typescript-eslint/no-duplicate-imports": "error", - "@typescript-eslint/comma-spacing": "error", - "@typescript-eslint/type-annotation-spacing": "error" - } -} \ No newline at end of file diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz new file mode 100644 index 0000000000000000000000000000000000000000..05b9da4a376108e68dd569e7caf3e352c9f2dfa1 GIT binary patch literal 427845 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 SJ0 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 UsUbbe^6d5E8{qYrc>QD(04+O1)c^nh literal 0 HcmV?d00001 diff --git a/@types/jest.d.ts b/@types/jest.d.ts index 28058d8c2..e66c70e3a 100644 --- a/@types/jest.d.ts +++ b/@types/jest.d.ts @@ -1,5 +1,5 @@ import {DataFrame} from "@polars/dataframe"; -import {Series} from "@polars/series/series"; +import {Series} from "@polars/series"; declare global { namespace jest { @@ -12,11 +12,11 @@ declare global { * * @example * ``` - * >>> df = pl.Dataframe([pl.Series("int32": [1,2], pl.Int32)]) - * >>> other = pl.Dataframe([pl.Series("int32": [1,2], pl.UInt32)]) + * > df = pl.Dataframe([pl.Series("int32": [1,2], pl.Int32)]) + * > other = pl.Dataframe([pl.Series("int32": [1,2], pl.UInt32)]) * - * >>> expect(df).toFrameEqual(other) // passes - * >>> expect(df).toFrameStrictEqual(other) // fails + * > expect(df).toFrameEqual(other) // passes + * > expect(df).toFrameStrictEqual(other) // fails * ``` */ toFrameStrictEqual(b: DataFrame): R; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..1251bfc5c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Contributing to nodejs-polars + +First of all, thank you for considering contributing to nodejs-polars! + +The following is a set of guidelines for contributing to nodejs-polars. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. + +## How Can I Contribute? + +### Reporting Bugs + +- **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/pola-rs/nodejs-polars/issues). + +- If you're unable to find an open issue addressing the problem, open a new one. Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. + +- If possible, use the relevant bug report templates to create the issue. + +### Suggesting Enhancements + +- Open a new GitHub issue in the [Issues](https://github.com/pola-rs/nodejs-polars/issues) with a **clear title** and **description**. + +- If possible, use the relevant enhancement request templates to create the issue. + + +### Pull Requests + +- Fill in the required template +- Use a descriptive title. *(This will end up in the changelog)* +- In the pull request description, link to the issue you were working on. +- Add any relevant information to the description that you think may help the maintainers review your code. +- Make sure your branch is [rebased](https://docs.github.com/en/get-started/using-git/about-git-rebase) against the latest version of the `main` branch. +- Make sure all GitHub Actions checks pass. + + + +## Development +### Vscode +If using VScode, it is recommended to install the following extensions +- rust-analyzer +- rome +--- + +- Fork the repository, then clone it from your fork +``` +git clone https://github.com//nodejs-polars.git +``` + +- Install dependencies +``` +yarn install +``` + +- Build the binary +``` +yarn build:debug +``` + +- Run the tests +``` +yarn jest +``` + +- Make your changes +- Test your changes +- +You can run the `precommit` command to make sure all of your tests pass & code is formatted correctly. +``` +yarn precommit +``` + +- Update the documentation if necessary +- Create a new pull request diff --git a/README.md b/README.md index b35968e88..337e2a191 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,14 @@ const pl = require('nodejs-polars'); ### Series ```js ->>> const fooSeries = pl.Series("foo", [1, 2, 3]) ->>> fooSeries.sum() +> const fooSeries = pl.Series("foo", [1, 2, 3]) +> fooSeries.sum() 6 // a lot operations support both positional and named arguments // you can see the full specs in the docs or the type definitions ->>> fooSeries.sort(true) ->>> fooSeries.sort({reverse: true}) +> fooSeries.sort(true) +> fooSeries.sort({reverse: true}) shape: (3,) Series: 'foo' [f64] [ @@ -36,14 +36,14 @@ Series: 'foo' [f64] 2 1 ] ->>> fooSeries.toArray() +> fooSeries.toArray() [1, 2, 3] // Series are 'Iterables' so you can use javascript iterable syntax on them ->>> [...fooSeries] +> [...fooSeries] [1, 2, 3] ->>> fooSeries[0] +> fooSeries[0] 1 ``` @@ -51,7 +51,7 @@ Series: 'foo' [f64] ### DataFrame ```js ->>> const df = pl.DataFrame( +>const df = pl.DataFrame( ... { ... A: [1, 2, 3, 4, 5], ... fruits: ["banana", "banana", "apple", "apple", "banana"], @@ -59,9 +59,7 @@ Series: 'foo' [f64] ... cars: ["beetle", "audi", "beetle", "beetle", "beetle"], ... } ... ) ->>> df -... .sort("fruits") -... .select( +> df.sort("fruits").select( ... "fruits", ... "cars", ... pl.lit("fruits").alias("literal_string_fruits"), @@ -90,7 +88,7 @@ shape: (5, 8) ``` ```js ->>> df["cars"] // or df.getColumn("cars") +> df["cars"] // or df.getColumn("cars") shape: (5,) Series: 'cars' [str] [ diff --git a/__tests__/complex_types.test.ts b/__tests__/complex_types.test.ts index 87797c6b1..58f52c951 100644 --- a/__tests__/complex_types.test.ts +++ b/__tests__/complex_types.test.ts @@ -1,8 +1,6 @@ import pl from "@polars"; - describe("complex types", () => { - test.skip("nested arrays round trip", () => { const arr = [[["foo"]], [], null]; const s = pl.Series("", arr); @@ -10,10 +8,9 @@ describe("complex types", () => { expect(actual).toEqual(arr); }); test.skip("struct arrays round trip", () => { - const arr = [{foo: "a", bar: 1}, null, null]; + const arr = [{ foo: "a", bar: 1 }, null, null]; const s = pl.Series("", arr); const actual = s.toArray(); expect(actual).toEqual(arr); }); - }); diff --git a/__tests__/dataframe.test.ts b/__tests__/dataframe.test.ts index d0d44f71c..3796050a5 100644 --- a/__tests__/dataframe.test.ts +++ b/__tests__/dataframe.test.ts @@ -1,6 +1,6 @@ /* eslint-disable newline-per-chained-call */ import pl from "@polars"; -import {Stream} from "stream"; +import { Stream } from "stream"; import fs from "fs"; describe("dataframe", () => { const df = pl.DataFrame([ @@ -10,248 +10,275 @@ describe("dataframe", () => { test("dtypes", () => { const expected = [pl.Float64, pl.Utf8]; - const actual = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}).dtypes; + const actual = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }).dtypes; expect(actual).toEqual(expected); }); test("height", () => { const expected = 3; - const actual = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}).height; + const actual = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }).height; expect(actual).toEqual(expected); }); test("width", () => { const expected = 2; - const actual = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}).width; + const actual = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }).width; expect(actual).toEqual(expected); }); test("shape", () => { - const expected = {height: 3, width: 2}; - const actual = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}).shape; + const expected = { height: 3, width: 2 }; + const actual = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }).shape; expect(actual).toEqual(expected); }); test("get columns", () => { const expected = ["a", "b"]; - const actual = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}).columns; + const actual = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }).columns; expect(actual).toEqual(expected); }); test("set columns", () => { const expected = ["d", "e"]; - const df = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}); + const df = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }); df.columns = expected; expect(df.columns).toEqual(expected); }); test("clone", () => { - const expected = pl.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]}); + const expected = pl.DataFrame({ a: [1, 2, 3], b: ["a", "b", "c"] }); const actual = expected.clone(); expect(actual).toFrameEqual(expected); }); test.skip("describe", () => { - const actual = pl.DataFrame({ - "a": [1, 2, 3], - "b": ["a", "b", "c"], - "c": [true, true, false] - }).describe(); + const actual = pl + .DataFrame({ + a: [1, 2, 3], + b: ["a", "b", "c"], + c: [true, true, false], + }) + .describe(); const expected = pl.DataFrame({ - "describe": ["mean", "std", "min", "max", "median"], - "a": [2, 1, 1, 3, 2], - "b": [null, null, null, null, null], - "c": [null, null, 0, 1, null] + describe: ["mean", "std", "min", "max", "median"], + a: [2, 1, 1, 3, 2], + b: [null, null, null, null, null], + c: [null, null, 0, 1, null], }); expect(actual).toFrameEqual(expected); }); test("drop", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], - "apple": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + apple: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const actual = df.drop("apple"); expect(actual).toFrameEqual(expected); }); test("drop: array", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], - "apple": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + apple: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], }); const actual = df.drop(["apple", "ham"]); expect(actual).toFrameEqual(expected); }); test("drop: ...rest", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], - "apple": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + apple: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], }); const actual = df.drop("apple", "ham"); expect(actual).toFrameEqual(expected); }); test("unique", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 2, 3], - "bar": [1, 2, 2, 4], - "ham": ["a", "d", "d", "c"], - }).unique(); + const actual = pl + .DataFrame({ + foo: [1, 2, 2, 3], + bar: [1, 2, 2, 4], + ham: ["a", "d", "d", "c"], + }) + .unique(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [1, 2, 4], - "ham": ["a", "d", "c"], + foo: [1, 2, 3], + bar: [1, 2, 4], + ham: ["a", "d", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("unique:subset", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 2, 2], - "bar": [1, 2, 2, 2], - "ham": ["a", "b", "c", "c"], - }).unique({subset: ["foo", "ham"]}); + const actual = pl + .DataFrame({ + foo: [1, 2, 2, 2], + bar: [1, 2, 2, 2], + ham: ["a", "b", "c", "c"], + }) + .unique({ subset: ["foo", "ham"] }); const expected = pl.DataFrame({ - "foo": [1, 2, 2], - "bar": [1, 2, 2], - "ham": ["a", "b", "c"], + foo: [1, 2, 2], + bar: [1, 2, 2], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); // run this test 100 times to make sure it is deterministic. test("unique:maintainOrder", () => { - Array.from({length:100}).forEach(() => { - const actual = pl.DataFrame({ - "foo": [0, 1, 2, 2, 2], - "bar": [0, 1, 2, 2, 2], - "ham": ["0", "a", "b", "b", "b"], - }).unique({maintainOrder: true}); + Array.from({ length: 100 }).forEach(() => { + const actual = pl + .DataFrame({ + foo: [0, 1, 2, 2, 2], + bar: [0, 1, 2, 2, 2], + ham: ["0", "a", "b", "b", "b"], + }) + .unique({ maintainOrder: true }); const expected = pl.DataFrame({ - "foo": [0, 1, 2], - "bar": [0, 1, 2], - "ham": ["0", "a", "b"], + foo: [0, 1, 2], + bar: [0, 1, 2], + ham: ["0", "a", "b"], }); expect(actual).toFrameEqual(expected); }); }); // run this test 100 times to make sure it is deterministic. test("unique:maintainOrder:single subset", () => { - Array.from({length:100}).forEach(() => { - const actual = pl.DataFrame({ - "foo": [0, 1, 2, 2, 2], - "bar": [0, 1, 2, 2, 2], - "ham": ["0", "a", "b", "c", "d"], - }).unique({maintainOrder: true, subset: "foo"}); + Array.from({ length: 100 }).forEach(() => { + const actual = pl + .DataFrame({ + foo: [0, 1, 2, 2, 2], + bar: [0, 1, 2, 2, 2], + ham: ["0", "a", "b", "c", "d"], + }) + .unique({ maintainOrder: true, subset: "foo" }); const expected = pl.DataFrame({ - "foo": [0, 1, 2], - "bar": [0, 1, 2], - "ham": ["0", "a", "b"], + foo: [0, 1, 2], + bar: [0, 1, 2], + ham: ["0", "a", "b"], }); expect(actual).toFrameEqual(expected); }); }); // run this test 100 times to make sure it is deterministic. test("unique:maintainOrder:multi subset", () => { - Array.from({length:100}).forEach(() => { - const actual = pl.DataFrame({ - "foo": [0, 1, 2, 2, 2], - "bar": [0, 1, 2, 2, 2], - "ham": ["0", "a", "b", "c", "c"], - }).unique({maintainOrder: true, subset: ["foo", "ham"]}); + Array.from({ length: 100 }).forEach(() => { + const actual = pl + .DataFrame({ + foo: [0, 1, 2, 2, 2], + bar: [0, 1, 2, 2, 2], + ham: ["0", "a", "b", "c", "c"], + }) + .unique({ maintainOrder: true, subset: ["foo", "ham"] }); const expected = pl.DataFrame({ - "foo": [0, 1, 2, 2], - "bar": [0, 1, 2, 2], - "ham": ["0", "a", "b", "c"], + foo: [0, 1, 2, 2], + bar: [0, 1, 2, 2], + ham: ["0", "a", "b", "c"], }); expect(actual).toFrameEqual(expected); }); }); test("dropNulls", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).dropNulls(); + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .dropNulls(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqual(expected); }); test("dropNulls subset", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).dropNulls("foo"); + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .dropNulls("foo"); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqual(expected); }); test("explode", () => { - const actual = pl.DataFrame({ - "letters": ["c", "a"], - "nrs": [[1, 2], [1, 3]] - }).explode("nrs"); + const actual = pl + .DataFrame({ + letters: ["c", "a"], + nrs: [ + [1, 2], + [1, 3], + ], + }) + .explode("nrs"); const expected = pl.DataFrame({ - "letters": ["c", "c", "a", "a"], - "nrs": [1, 2, 1, 3] + letters: ["c", "c", "a", "a"], + nrs: [1, 2, 1, 3], }); expect(actual).toFrameEqual(expected); }); test("fillNull:zero", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).fillNull("zero"); + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .fillNull("zero"); const expected = pl.DataFrame({ - "foo": [1, 0, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], + foo: [1, 0, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], }); expect(actual).toFrameEqual(expected); }); test("fillNull:one", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).fillNull("one"); + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .fillNull("one"); const expected = pl.DataFrame({ - "foo": [1, 1, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], + foo: [1, 1, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], }); expect(actual).toFrameEqual(expected); }); // test.todo("filter"); test("findIdxByName", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).findIdxByName("ham"); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .findIdxByName("ham"); const expected = 2; expect(actual).toEqual(expected); }); @@ -280,174 +307,191 @@ describe("dataframe", () => { // expect(actual).toSeriesEqual(expected); // }); test("frameEqual:true", () => { - const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], + const df = pl.DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], }); - const other = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], + const other = pl.DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], }); const actual = df.frameEqual(other); expect(actual).toStrictEqual(true); }); test("frameEqual:false", () => { - const df = pl.DataFrame({ - "foo": [3, 2, 22], - "baz": [0, 7, 8], + const df = pl.DataFrame({ + foo: [3, 2, 22], + baz: [0, 7, 8], }); - const other = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], + const other = pl.DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], }); const actual = df.frameEqual(other); expect(actual).toStrictEqual(false); }); test("frameEqual:nullEq:false", () => { - const df = pl.DataFrame({ - "foo": [1, 2, null], - "bar": [6, 7, 8], + const df = pl.DataFrame({ + foo: [1, 2, null], + bar: [6, 7, 8], }); - const other = pl.DataFrame({ - "foo": [1, 2, null], - "bar": [6, 7, 8], + const other = pl.DataFrame({ + foo: [1, 2, null], + bar: [6, 7, 8], }); const actual = df.frameEqual(other, false); expect(actual).toStrictEqual(false); }); test("frameEqual:nullEq:true", () => { - const df = pl.DataFrame({ - "foo": [1, 2, null], - "bar": [6, 7, 8], + const df = pl.DataFrame({ + foo: [1, 2, null], + bar: [6, 7, 8], }); - const other = pl.DataFrame({ - "foo": [1, 2, null], - "bar": [6, 7, 8], + const other = pl.DataFrame({ + foo: [1, 2, null], + bar: [6, 7, 8], }); const actual = df.frameEqual(other, true); expect(actual).toStrictEqual(true); }); test("getColumn", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).getColumn("ham"); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .getColumn("ham"); const expected = pl.Series("ham", ["a", "b", "c"]); expect(actual).toSeriesEqual(expected); }); test("getColumns", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).getColumns(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .getColumns(); const expected = [ pl.Series("foo", [1, 2, 3]), - pl.Series("ham", ["a", "b", "c"]) + pl.Series("ham", ["a", "b", "c"]), ]; actual.forEach((a, idx) => { expect(a).toSeriesEqual(expected[idx]); }); - }); test("groupBy", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).groupBy("foo"); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .groupBy("foo"); expect(actual.toString()).toEqual("GroupBy"); }); test("hashRows", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).hashRows(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .hashRows(); expect(actual.dtype).toEqual(pl.UInt64); }); + test.each([[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]])( + "hashRows:positional", + (...args: any[]) => { + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .hashRows(...args); + expect(actual.dtype).toEqual(pl.UInt64); + }, + ); test.each([ - [1], - [1, 2], - [1, 2, 3], - [1, 2, 3, 4], - ])("hashRows:positional", (...args: any[]) => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).hashRows(...args); - expect(actual.dtype).toEqual(pl.UInt64); - }); - test.each([ - [{k0: 1}], - [{k0: 1, k1: 2}], - [{k0: 1, k1: 2, k2:3}], - [{k0: 1, k1: 2, k2:3, k3:4}], + [{ k0: 1 }], + [{ k0: 1, k1: 2 }], + [{ k0: 1, k1: 2, k2: 3 }], + [{ k0: 1, k1: 2, k2: 3, k3: 4 }], ])("hashRows:named", (opts) => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).hashRows(opts); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .hashRows(opts); expect(actual.dtype).toEqual(pl.UInt64); }); test("head", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).head(1); - const expected = pl.DataFrame({ - "foo": [1], - "ham": ["a"] - }).head(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .head(1); + const expected = pl + .DataFrame({ + foo: [1], + ham: ["a"], + }) + .head(1); expect(actual).toFrameEqual(expected); }); test("hstack:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).hstack([pl.Series("apple", [10, 20, 30])]); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .hstack([pl.Series("apple", [10, 20, 30])]); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], - "apple": [10, 20, 30] + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + apple: [10, 20, 30], }); expect(actual).toFrameEqual(expected); }); test("hstack:df", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).hstack(pl.DataFrame([pl.Series("apple", [10, 20, 30])])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .hstack(pl.DataFrame([pl.Series("apple", [10, 20, 30])])); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], - "apple": [10, 20, 30] + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + apple: [10, 20, 30], }); expect(actual).toFrameEqual(expected); }); test("hstack:df", () => { const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], }); actual.insertAtIdx(0, pl.Series("apple", [10, 20, 30])); const expected = pl.DataFrame({ - "apple": [10, 20, 30], - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], + apple: [10, 20, 30], + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqual(expected); }); test("interpolate", () => { const df = pl.DataFrame({ - a: [1, null, 3] + a: [1, null, 3], }); const expected = pl.DataFrame({ - a: [1, 2, 3] + a: [1, 2, 3], }); const actual = df.interpolate(); expect(actual).toFrameEqual(expected); @@ -455,7 +499,7 @@ describe("dataframe", () => { test("isDuplicated", () => { const df = pl.DataFrame({ a: [1, 2, 2], - b: [1, 2, 2] + b: [1, 2, 2], }); const expected = pl.Series([false, true, true]); const actual = df.isDuplicated(); @@ -468,7 +512,7 @@ describe("dataframe", () => { test("isUnique", () => { const df = pl.DataFrame({ a: [1, 2, 2], - b: [1, 2, 2] + b: [1, 2, 2], }); const expected = pl.Series([true, false, false]); const actual = df.isUnique(); @@ -476,130 +520,155 @@ describe("dataframe", () => { }); test("lazy", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] - }).lazy().collectSync(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + }) + .lazy() + .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqual(expected); }); test("limit", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).limit(1); - const expected = pl.DataFrame({ - "foo": [1], - "ham": ["a"] + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .limit(1); + const expected = pl.DataFrame({ + foo: [1], + ham: ["a"], }); expect(actual).toFrameEqual(expected); }); test("max:axis:0", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).max(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .max(); expect(actual.row(0)).toEqual([3, 8, "c"]); }); test("max:axis:1", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).max(1); - const expected = pl.Series("foo", [6, 2, 9]); + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .max(1); + const expected = pl.Series("foo", [6, 2, 9]); expect(actual).toSeriesEqual(expected); }); test("mean:axis:0", () => { - const actual = pl.DataFrame({ - "foo": [4, 4, 4], - "bar": [1, 1, 10], - "ham": ["a", "b", "a"] - }).mean(); + const actual = pl + .DataFrame({ + foo: [4, 4, 4], + bar: [1, 1, 10], + ham: ["a", "b", "a"], + }) + .mean(); expect(actual.row(0)).toEqual([4, 4, null]); }); test("mean:axis:1", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 6], - "bar": [6, 2, 8], - }).mean(1, "ignore"); - const expected = pl.Series("foo", [3.5, 2, 7]); + const actual = pl + .DataFrame({ + foo: [1, null, 6], + bar: [6, 2, 8], + }) + .mean(1, "ignore"); + const expected = pl.Series("foo", [3.5, 2, 7]); expect(actual).toSeriesEqual(expected); }); test("median", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).median(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .median(); expect(actual.row(0)).toEqual([2, 7, null]); }); test.todo("melt"); test("min:axis:0", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).min(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .min(); expect(actual.row(0)).toEqual([1, 6, "a"]); }); test("min:axis:1", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).min(1); - const expected = pl.Series("foo", [1, 2, 8]); + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .min(1); + const expected = pl.Series("foo", [1, 2, 8]); expect(actual).toSeriesEqual(expected); }); test("nChunks", () => { const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], + foo: [1, 2, 9], + bar: [6, 2, 8], }); expect(actual.nChunks()).toEqual(1); }); test("nullCount", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, null], - "bar": [6, 2, 8], - "apple": [6, 2, 8], - "pizza": [null, null, 8], - }).nullCount(); + const actual = pl + .DataFrame({ + foo: [1, 2, null], + bar: [6, 2, 8], + apple: [6, 2, 8], + pizza: [null, null, 8], + }) + .nullCount(); expect(actual.row(0)).toEqual([1, 0, 0, 2]); }); test.todo("pipe"); test("quantile", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).quantile(0.5); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .quantile(0.5); expect(actual.row(0)).toEqual([2, 7, null]); }); test("rename", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).rename({ - "foo": "foo_new", - "bar": "bar_new", - "ham": "ham_new" - }); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .rename({ + foo: "foo_new", + bar: "bar_new", + ham: "ham_new", + }); expect(actual.columns).toEqual(["foo_new", "bar_new", "ham_new"]); }); test("replaceAtIdx", () => { const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], }); const s = pl.Series("new_foo", [0.12, 2.0, 9.99]); actual.replaceAtIdx(0, s); @@ -607,71 +676,88 @@ describe("dataframe", () => { expect(actual.findIdxByName("new_foo")).toEqual(0); }); test("row", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).row(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .row(1); expect(actual).toEqual([2, 7, "b"]); }); test("rows", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).rows(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .rows(); expect(actual).toEqual([ [1, 6, "a"], [2, 7, "b"], - [3, 8, "c"] + [3, 8, "c"], ]); }); test("sample:n", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).sample(2); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .sample(2); expect(actual.height).toStrictEqual(2); }); test("sample:default", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).sample(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .sample(); expect(actual.height).toStrictEqual(1); }); test("sample:frac", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).sample({frac: 0.5}); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .sample({ frac: 0.5 }); expect(actual.height).toStrictEqual(2); }); test("sample:frac", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).sample({frac: 0.75}); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .sample({ frac: 0.75 }); expect(actual.height).toStrictEqual(3); }); test("sample:invalid", () => { - const fn = () => pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).sample({} as any); + const fn = () => + pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .sample({} as any); expect(fn).toThrow(TypeError); }); test("select:strings", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).select("ham", "foo"); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .select("ham", "foo"); const foo = pl.Series("foo", [1, 2, 3, 1]); const ham = pl.Series("ham", ["a", "b", "c", null]); expect(actual.width).toStrictEqual(2); @@ -679,11 +765,13 @@ describe("dataframe", () => { expect(actual.getColumn("ham")).toSeriesEqual(ham); }); test("select:expr", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).select(pl.col("foo"), "ham"); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .select(pl.col("foo"), "ham"); const foo = pl.Series("foo", [1, 2, 3, 1]); const ham = pl.Series("ham", ["a", "b", "c", null]); expect(actual.width).toStrictEqual(2); @@ -691,140 +779,159 @@ describe("dataframe", () => { expect(actual.getColumn("ham")).toSeriesEqual(ham); }); test("shift:pos", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).shift(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .shift(1); const expected = pl.DataFrame({ - "foo": [null, 1, 2, 3], - "bar": [null, 6, 7, 8], + foo: [null, 1, 2, 3], + bar: [null, 6, 7, 8], }); expect(actual).toFrameEqual(expected); }); test("shift:neg", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).shift(-1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .shift(-1); const expected = pl.DataFrame({ - "foo": [2, 3, 1, null], - "bar": [7, 8, 1, null], + foo: [2, 3, 1, null], + bar: [7, 8, 1, null], }); expect(actual).toFrameEqual(expected); }); test("shiftAndFill:positional", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).shiftAndFill(-1, 99); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .shiftAndFill(-1, 99); const expected = pl.DataFrame({ - "foo": [2, 3, 1, 99], - "bar": [7, 8, 1, 99], + foo: [2, 3, 1, 99], + bar: [7, 8, 1, 99], }); expect(actual).toFrameEqual(expected); }); test("shiftAndFill:named", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).shiftAndFill({periods: -1, fillValue: 99}); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .shiftAndFill({ periods: -1, fillValue: 99 }); const expected = pl.DataFrame({ - "foo": [2, 3, 1, 99], - "bar": [7, 8, 1, 99], + foo: [2, 3, 1, 99], + bar: [7, 8, 1, 99], }); expect(actual).toFrameEqual(expected); }); test("shrinkToFit:inPlace", () => { const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], }); actual.shrinkToFit(true); const expected = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], }); expect(actual).toFrameEqual(expected); }); test("shrinkToFit", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).shrinkToFit(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .shrinkToFit(); const expected = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], }); expect(actual).toFrameEqual(expected); }); test("slice:positional", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).slice(0, 2); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .slice(0, 2); const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": [6, 7], + foo: [1, 2], + bar: [6, 7], }); expect(actual).toFrameEqual(expected); }); test("slice:named", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).slice({offset: 0, length: 2}); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .slice({ offset: 0, length: 2 }); const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": [6, 7], + foo: [1, 2], + bar: [6, 7], }); expect(actual).toFrameEqual(expected); }); test("sort:positional", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).sort("bar"); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .sort("bar"); const expected = pl.DataFrame({ - "foo": [1, 1, 2, 3], - "bar": [1, 6, 7, 8], + foo: [1, 1, 2, 3], + bar: [1, 6, 7, 8], }); expect(actual).toFrameEqual(expected); }); test("sort:named", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).sort({by: "bar", reverse: true}); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .sort({ by: "bar", reverse: true }); const expected = pl.DataFrame({ - "foo": [3, 2, 1, 1], - "bar": [8, 7, 6, 1], + foo: [3, 2, 1, 1], + bar: [8, 7, 6, 1], }); expect(actual).toFrameEqual(expected); }); test("sort:multi-args", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, -1], - "bar": [6, 7, 8, 2], - "baz": ["a", "b", "d", "A"], - }).sort({ - by: [ - pl.col("baz"), - pl.col("bar") - ] - }); + const actual = pl + .DataFrame({ + foo: [1, 2, 3, -1], + bar: [6, 7, 8, 2], + baz: ["a", "b", "d", "A"], + }) + .sort({ + by: [pl.col("baz"), pl.col("bar")], + }); const expected = pl.DataFrame({ - "foo": [-1, 1, 2, 3], - "bar": [2, 6, 7, 8], - "baz": ["A", "a", "b", "d"], + foo: [-1, 1, 2, 3], + bar: [2, 6, 7, 8], + baz: ["A", "a", "b", "d"], }); expect(actual).toFrameEqual(expected); }); test("std", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).std(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .std(); const expected = pl.DataFrame([ pl.Series("foo", [1]), pl.Series("bar", [1]), @@ -833,79 +940,87 @@ describe("dataframe", () => { expect(actual).toFrameEqual(expected); }); test("sum:axis:0", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).sum(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .sum(); expect(actual.row(0)).toEqual([6, 21, null]); }); test("sum:axis:1", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).sum(1).rename("sum"); - const expected = pl.Series("sum", [7, 4, 17]); + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .sum(1) + .rename("sum"); + const expected = pl.Series("sum", [7, 4, 17]); expect(actual).toSeriesEqual(expected); }); test("tail", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).tail(1).row(0); - const expected = [9, 8]; + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .tail(1) + .row(0); + const expected = [9, 8]; expect(actual).toEqual(expected); }); test.skip("transpose", () => { const expected = pl.DataFrame({ - "column_0": [1, 1], - "column_1": [2, 2], - "column_2": [3, 3] + column_0: [1, 1], + column_1: [2, 2], + column_2: [3, 3], }); const df = pl.DataFrame({ a: [1, 2, 3], - b: [1, 2, 3] + b: [1, 2, 3], }); const actual = df.transpose(); expect(actual).toFrameEqual(expected); }); test.skip("transpose:includeHeader", () => { const expected = pl.DataFrame({ - "column": ["a", "b"], - "column_0": [1, 1], - "column_1": [2, 2], - "column_2": [3, 3] + column: ["a", "b"], + column_0: [1, 1], + column_1: [2, 2], + column_2: [3, 3], }); const df = pl.DataFrame({ a: [1, 2, 3], - b: [1, 2, 3] + b: [1, 2, 3], }); - const actual = df.transpose({includeHeader:true}); + const actual = df.transpose({ includeHeader: true }); expect(actual).toFrameEqual(expected); }); test.skip("transpose:columnNames", () => { const expected = pl.DataFrame({ - "a": [1, 1], - "b": [2, 2], - "c": [3, 3] + a: [1, 1], + b: [2, 2], + c: [3, 3], }); const df = pl.DataFrame({ a: [1, 2, 3], - b: [1, 2, 3] + b: [1, 2, 3], }); - const actual = df.transpose({includeHeader:false, columnNames: "abc"}); + const actual = df.transpose({ includeHeader: false, columnNames: "abc" }); expect(actual).toFrameEqual(expected); }); test.skip("transpose:columnNames:generator", () => { const expected = pl.DataFrame({ - "col_0": [1, 1], - "col_1": [2, 2], - "col_2": [3, 3] + col_0: [1, 1], + col_1: [2, 2], + col_2: [3, 3], }); - function *namesGenerator() { + function* namesGenerator() { const baseName = "col_"; let count = 0; - while(true) { + while (true) { let name = `${baseName}${count}`; yield name; count++; @@ -913,17 +1028,22 @@ describe("dataframe", () => { } const df = pl.DataFrame({ a: [1, 2, 3], - b: [1, 2, 3] + b: [1, 2, 3], + }); + const actual = df.transpose({ + includeHeader: false, + columnNames: namesGenerator(), }); - const actual = df.transpose({includeHeader:false, columnNames: namesGenerator()}); expect(actual).toFrameEqual(expected); }); test("var", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).var(); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .var(); const expected = pl.DataFrame([ pl.Series("foo", [1]), pl.Series("bar", [1]), @@ -933,14 +1053,14 @@ describe("dataframe", () => { }); test("vstack", () => { const df1 = pl.DataFrame({ - "foo": [1, 2], - "bar": [6, 7], - "ham": ["a", "b"] + foo: [1, 2], + bar: [6, 7], + ham: ["a", "b"], }); const df2 = pl.DataFrame({ - "foo": [3, 4], - "bar": [8, 9], - "ham": ["c", "d"] + foo: [3, 4], + bar: [8, 9], + ham: ["c", "d"], }); const actual = df1.vstack(df2); @@ -955,7 +1075,7 @@ describe("dataframe", () => { const actual = df .clone() .withColumn(pl.Series("col_a", ["a", "a", "a"], pl.Utf8)); - const expected = pl.DataFrame([ + const expected = pl.DataFrame([ pl.Series("foo", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), pl.Series("col_a", ["a", "a", "a"], pl.Utf8), @@ -963,11 +1083,9 @@ describe("dataframe", () => { expect(actual).toFrameEqual(expected); }); test("withColumn:expr", () => { - const actual = df - .clone() - .withColumn(pl.lit("a").alias("col_a")); + const actual = df.clone().withColumn(pl.lit("a").alias("col_a")); - const expected = pl.DataFrame([ + const expected = pl.DataFrame([ pl.Series("foo", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), pl.Series("col_a", ["a", "a", "a"], pl.Utf8), @@ -979,9 +1097,9 @@ describe("dataframe", () => { .clone() .withColumns( pl.Series("col_a", ["a", "a", "a"], pl.Utf8), - pl.Series("col_b", ["b", "b", "b"], pl.Utf8) + pl.Series("col_b", ["b", "b", "b"], pl.Utf8), ); - const expected = pl.DataFrame([ + const expected = pl.DataFrame([ pl.Series("foo", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), pl.Series("col_a", ["a", "a", "a"], pl.Utf8), @@ -992,11 +1110,8 @@ describe("dataframe", () => { test("withColumns:expr", () => { const actual = df .clone() - .withColumns( - pl.lit("a").alias("col_a"), - pl.lit("b").alias("col_b") - ); - const expected = pl.DataFrame([ + .withColumns(pl.lit("a").alias("col_a"), pl.lit("b").alias("col_b")); + const expected = pl.DataFrame([ pl.Series("foo", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), pl.Series("col_a", ["a", "a", "a"], pl.Utf8), @@ -1005,11 +1120,9 @@ describe("dataframe", () => { expect(actual).toFrameEqual(expected); }); test("withColumnRenamed:positional", () => { - const actual = df - .clone() - .withColumnRenamed("foo", "apple"); + const actual = df.clone().withColumnRenamed("foo", "apple"); - const expected = pl.DataFrame([ + const expected = pl.DataFrame([ pl.Series("apple", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), ]); @@ -1018,20 +1131,18 @@ describe("dataframe", () => { test("withColumnRenamed:named", () => { const actual = df .clone() - .withColumnRenamed({existing: "foo", replacement: "apple"}); + .withColumnRenamed({ existing: "foo", replacement: "apple" }); - const expected = pl.DataFrame([ + const expected = pl.DataFrame([ pl.Series("apple", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), ]); expect(actual).toFrameEqual(expected); }); test("withRowCount", () => { - const actual = df - .clone() - .withRowCount(); + const actual = df.clone().withRowCount(); - const expected = pl.DataFrame([ + const expected = pl.DataFrame([ pl.Series("row_nr", [0, 1, 2], pl.UInt32), pl.Series("foo", [1, 2, 9], pl.Int16), pl.Series("bar", [6, 2, 8], pl.Int16), @@ -1040,20 +1151,31 @@ describe("dataframe", () => { }); test("pivot", () => { const df = pl.DataFrame({ - "a": pl.Series([1, 2, 3]).cast(pl.Int32), - "b": pl.Series([[1, 1], [2, 2], [3, 3]]).cast(pl.List(pl.Int32)) - }); - - const expected = pl.DataFrame( - { - "a": pl.Series([1, 2, 3]).cast(pl.Int32), + a: pl.Series([1, 2, 3]).cast(pl.Int32), + b: pl + .Series([ + [1, 1], + [2, 2], + [3, 3], + ]) + .cast(pl.List(pl.Int32)), + }); + + const expected = pl + .DataFrame({ + a: pl.Series([1, 2, 3]).cast(pl.Int32), "1": pl.Series([[1, 1], null, null]).cast(pl.List(pl.Int32)), "2": pl.Series([null, [2, 2], null]).cast(pl.List(pl.Int32)), "3": pl.Series([null, null, [3, 3]]).cast(pl.List(pl.Int32)), - } - ).select("a", "1", "2", "3"); + }) + .select("a", "1", "2", "3"); - const actual = df.pivot("b", {index:"a", columns:"a", aggregateFunc:"first", sortColumns:true}); + const actual = df.pivot("b", { + index: "a", + columns: "a", + aggregateFunc: "first", + sortColumns: true, + }); expect(actual).toFrameEqual(expected, true); }); @@ -1061,141 +1183,137 @@ describe("dataframe", () => { describe("join", () => { test("on", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"] + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], }); - const actual = df.join(otherDF, {on: "ham"}); + const actual = df.join(otherDF, { on: "ham" }); const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": [6.0, 7.0], - "ham": ["a", "b"], - "apple": ["x", "y"], + foo: [1, 2], + bar: [6.0, 7.0], + ham: ["a", "b"], + apple: ["x", "y"], }); expect(actual).toFrameEqual(expected); }); test("on:multiple-columns", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo": [1, 10, 11], - + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo: [1, 10, 11], }); - const actual = df.join(otherDF, {on: ["ham", "foo"]}); + const actual = df.join(otherDF, { on: ["ham", "foo"] }); const expected = pl.DataFrame({ - "foo": [1], - "bar": [6.0], - "ham": ["a"], - "apple": ["x"], + foo: [1], + bar: [6.0], + ham: ["a"], + apple: ["x"], }); expect(actual).toFrameEqual(expected); }); test("on:left&right", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], }); const actual = df.join(otherDF, { leftOn: ["foo_left", "ham"], - rightOn: ["foo_right", "ham"] + rightOn: ["foo_right", "ham"], }); const expected = pl.DataFrame({ - "foo_left": [1], - "bar": [6.0], - "ham": ["a"], - "apple": ["x"], + foo_left: [1], + bar: [6.0], + ham: ["a"], + apple: ["x"], }); expect(actual).toFrameEqual(expected); }); test("on:left&right", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], }); const actual = df.join(otherDF, { leftOn: ["foo_left", "ham"], - rightOn: ["foo_right", "ham"] + rightOn: ["foo_right", "ham"], }); const expected = pl.DataFrame({ - "foo_left": [1], - "bar": [6.0], - "ham": ["a"], - "apple": ["x"], + foo_left: [1], + bar: [6.0], + ham: ["a"], + apple: ["x"], }); expect(actual).toFrameEqual(expected); }); test("on throws error if only 'leftOn' is specified", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - - }); - const f = () => df.join(otherDF, { - leftOn: ["foo_left", "ham"], - } as any); + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }); + const f = () => + df.join(otherDF, { + leftOn: ["foo_left", "ham"], + } as any); expect(f).toThrow(TypeError); }); test("on throws error if only 'rightOn' is specified", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - - }); - const f = () => df.join(otherDF, { - rightOn: ["foo_right", "ham"], - } as any); + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }); + const f = () => + df.join(otherDF, { + rightOn: ["foo_right", "ham"], + } as any); expect(f).toThrow(TypeError); }); test("on takes precedence over left&right", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], }); const actual = df.join(otherDF, { on: "ham", @@ -1203,107 +1321,107 @@ describe("join", () => { rightOn: ["foo_right", "ham"], } as any); const expected = pl.DataFrame({ - "foo_left": [1, 2], - "bar": [6.0, 7.0], - "ham": ["a", "b"], - "apple": ["x", "y"], - "foo_right": [1, 10], + foo_left: [1, 2], + bar: [6.0, 7.0], + ham: ["a", "b"], + apple: ["x", "y"], + foo_right: [1, 10], }); expect(actual).toFrameEqual(expected); }); test("how:left", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo": [1, 10, 11], - + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo: [1, 10, 11], }); const actual = df.join(otherDF, { on: "ham", - how: "left" + how: "left", }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], - "apple": ["x", "y", null], - "fooright": [1, 10, null], + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + apple: ["x", "y", null], + fooright: [1, 10, null], }); expect(actual).toFrameEqual(expected); }); test("how:outer", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y"], - "ham": ["a", "d"], - "foo": [1, 10], - + apple: ["x", "y"], + ham: ["a", "d"], + foo: [1, 10], }); const actual = df.join(otherDF, { on: "ham", - how: "outer" + how: "outer", }); const expected = pl.DataFrame({ - "foo": [1, 2, 3, null], - "bar": [6, 7, 8, null], - "ham": ["a", "b", "c", "d"], - "apple": ["x", null, null, "y"], - "fooright": [1, null, null, 10], + foo: [1, 2, 3, null], + bar: [6, 7, 8, null], + ham: ["a", "b", "c", "d"], + apple: ["x", null, null, "y"], + fooright: [1, null, null, 10], }); expect(actual).toFrameEqual(expected); }); test("suffix", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo": [1, 10, 11], - + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo: [1, 10, 11], }); const actual = df.join(otherDF, { on: "ham", how: "left", - suffix: "_other" + suffix: "_other", }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], - "apple": ["x", "y", null], - "foo_other": [1, 10, null], + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + apple: ["x", "y", null], + foo_other: [1, 10, null], }); expect(actual).toFrameEqual(expected); }); test("asof_cross_join", () => { - const left = pl.DataFrame({"a": [-10, 5, 10], "left_val": ["a", "b", "c"]}); - const right = pl.DataFrame({"a": [1, 2, 3, 6, 7], "right_val": [1, 2, 3, 6, 7]}); + const left = pl.DataFrame({ a: [-10, 5, 10], left_val: ["a", "b", "c"] }); + const right = pl.DataFrame({ + a: [1, 2, 3, 6, 7], + right_val: [1, 2, 3, 6, 7], + }); // only test dispatch of asof join - let out = left.joinAsof(right, {on:"a"}); - expect(out.shape).toEqual({height: 3, width: 3}); + let out = left.joinAsof(right, { on: "a" }); + expect(out.shape).toEqual({ height: 3, width: 3 }); - out = left.lazy().joinAsof(right.lazy(), {on:"a"}).collectSync(); - expect(out.shape).toEqual({height: 3, width: 3}); + out = left.lazy().joinAsof(right.lazy(), { on: "a" }).collectSync(); + expect(out.shape).toEqual({ height: 3, width: 3 }); // only test dispatch of cross join - out = left.join(right, {how:"cross"}); - expect(out.shape).toEqual({height: 15, width: 4}); + out = left.join(right, { how: "cross" }); + expect(out.shape).toEqual({ height: 15, width: 4 }); - left.lazy().join(right.lazy(), {how:"cross"}).collectSync(); - expect(out.shape).toEqual({height: 15, width: 4}); + left.lazy().join(right.lazy(), { how: "cross" }).collectSync(); + expect(out.shape).toEqual({ height: 15, width: 4 }); }); }); describe("io", () => { @@ -1313,31 +1431,33 @@ describe("io", () => { ]); test("writeCSV:string", () => { const actual = df.clone().writeCSV().toString(); - const expected = "foo,bar\n1,6\n2,2\n9,8\n"; + const expected = "foo,bar\n1,6\n2,2\n9,8\n"; expect(actual).toEqual(expected); }); test("writeCSV:string:sep", () => { - const actual = df.clone().writeCSV({sep: "X"}).toString(); - const expected = "fooXbar\n1X6\n2X2\n9X8\n"; + const actual = df.clone().writeCSV({ sep: "X" }).toString(); + const expected = "fooXbar\n1X6\n2X2\n9X8\n"; expect(actual).toEqual(expected); }); test("writeCSV:string:header", () => { - const actual = df.clone().writeCSV({sep: "X", hasHeader: false}).toString(); - const expected = "1X6\n2X2\n9X8\n"; + const actual = df + .clone() + .writeCSV({ sep: "X", hasHeader: false }) + .toString(); + const expected = "1X6\n2X2\n9X8\n"; expect(actual).toEqual(expected); }); test("writeCSV:stream", (done) => { const df = pl.DataFrame([ pl.Series("foo", [1, 2, 3], pl.UInt32), - pl.Series("bar", ["a", "b", "c"]) + pl.Series("bar", ["a", "b", "c"]), ]); let body = ""; const writeStream = new Stream.Writable({ write(chunk, encoding, callback) { body += chunk; callback(null); - - } + }, }); df.writeCSV(writeStream); const newDF = pl.readCSV(body); @@ -1347,7 +1467,7 @@ describe("io", () => { test("writeCSV:path", (done) => { const df = pl.DataFrame([ pl.Series("foo", [1, 2, 3], pl.UInt32), - pl.Series("bar", ["a", "b", "c"]) + pl.Series("bar", ["a", "b", "c"]), ]); df.writeCSV("./test.csv"); const newDF = pl.readCSV("./test.csv"); @@ -1358,21 +1478,21 @@ describe("io", () => { test("JSON.stringify", () => { const df = pl.DataFrame({ foo: [1], - bar: ["a"] + bar: ["a"], }); const expected = JSON.stringify({ columns: [ { name: "foo", datatype: "Float64", - values: [1.0] + values: [1.0], }, { name: "bar", datatype: "Utf8", - values: ["a"] + values: ["a"], }, - ] + ], }); const actual = JSON.stringify(df); expect(actual).toEqual(expected); @@ -1380,18 +1500,16 @@ describe("io", () => { test("toRecords", () => { const df = pl.DataFrame({ foo: [1], - bar: ["a"] + bar: ["a"], }); - const expected = [ - {foo: 1.0, bar: "a"} - ]; + const expected = [{ foo: 1.0, bar: "a" }]; const actual = df.toRecords(); expect(actual).toEqual(expected); }); test("toObject", () => { const expected = { foo: [1], - bar: ["a"] + bar: ["a"], }; const df = pl.DataFrame(expected); @@ -1399,19 +1517,18 @@ describe("io", () => { expect(actual).toEqual(expected); }); test("writeJSON:lines", () => { - const rows = [ - {foo: 1.1}, - {foo: 3.1}, - {foo: 3.1} - ]; - const actual = pl.DataFrame(rows).writeJSON({format:"lines"}).toString(); - const expected = rows.map(r => JSON.stringify(r)).join("\n").concat("\n"); + const rows = [{ foo: 1.1 }, { foo: 3.1 }, { foo: 3.1 }]; + const actual = pl.DataFrame(rows).writeJSON({ format: "lines" }).toString(); + const expected = rows + .map((r) => JSON.stringify(r)) + .join("\n") + .concat("\n"); expect(actual).toEqual(expected); }); test("writeJSON:stream", (done) => { const df = pl.DataFrame([ pl.Series("foo", [1, 2, 3], pl.UInt32), - pl.Series("bar", ["a", "b", "c"]) + pl.Series("bar", ["a", "b", "c"]), ]); let body = ""; @@ -1419,10 +1536,9 @@ describe("io", () => { write(chunk, encoding, callback) { body += chunk; callback(null); - - } + }, }); - df.writeJSON(writeStream, {format:"lines"}); + df.writeJSON(writeStream, { format: "lines" }); const newDF = pl.readJSON(body).select("foo", "bar"); expect(newDF).toFrameEqual(df); done(); @@ -1430,9 +1546,9 @@ describe("io", () => { test("writeJSON:path", (done) => { const df = pl.DataFrame([ pl.Series("foo", [1, 2, 3], pl.UInt32), - pl.Series("bar", ["a", "b", "c"]) + pl.Series("bar", ["a", "b", "c"]), ]); - df.writeJSON("./test.json", {format:"lines"}); + df.writeJSON("./test.json", { format: "lines" }); const newDF = pl.readJSON("./test.json").select("foo", "bar"); expect(newDF).toFrameEqual(df); fs.rmSync("./test.json"); @@ -1440,13 +1556,12 @@ describe("io", () => { }); test("writeJSON:rows", () => { - const rows = [ - {foo: 1.1}, - {foo: 3.1}, - {foo: 3.1} - ]; + const rows = [{ foo: 1.1 }, { foo: 3.1 }, { foo: 3.1 }]; const expected = JSON.stringify(rows); - const actual = pl.readRecords(rows).writeJSON({format:"json"}).toString(); + const actual = pl + .readRecords(rows) + .writeJSON({ format: "json" }) + .toString(); expect(actual).toEqual(expected); }); test("toSeries", () => { @@ -1522,7 +1637,11 @@ describe("create", () => { }); test("from series-array", () => { const s1 = pl.Series("num", [1, 2, 3]); - const s2 = pl.Series("date", [null, Date.now(), Date.now()], pl.Datetime("ms")); + const s2 = pl.Series( + "date", + [null, Date.now(), Date.now()], + pl.Datetime("ms"), + ); const df = pl.DataFrame([s1, s2]); expect(df.getColumn("num")).toSeriesEqual(s1); expect(df.getColumn("date")).toSeriesEqual(s2); @@ -1530,7 +1649,7 @@ describe("create", () => { test("from arrays", () => { const columns = [ [1, 2, 3], - [1, 2, 2] + [1, 2, 2], ]; const df = pl.DataFrame(columns); @@ -1541,10 +1660,10 @@ describe("create", () => { test("from arrays: orient=col", () => { const columns = [ [1, 2, 3], - [1, 2, 2] + [1, 2, 2], ]; - const df = pl.DataFrame(columns, {orient: "col"}); + const df = pl.DataFrame(columns, { orient: "col" }); expect(df.getColumns()[0].toArray()).toEqual(columns[0]); expect(df.getColumns()[1].toArray()).toEqual(columns[1]); @@ -1552,7 +1671,7 @@ describe("create", () => { test("from arrays: orient=row", () => { const rows = [ [1, 2, 3], - [1, 2, 2] + [1, 2, 2], ]; const df = pl.readRecords(rows); @@ -1563,33 +1682,35 @@ describe("create", () => { test("from arrays with column names: orient=col", () => { const columns = [ [1, 2, 3], - [1, 2, 2] + [1, 2, 2], ]; const expectedColumnNames = ["a", "b"]; - const df = pl.DataFrame(columns, {columns: expectedColumnNames, orient: "col"}); + const df = pl.DataFrame(columns, { + columns: expectedColumnNames, + orient: "col", + }); expect(df.getColumns()[0].toArray()).toEqual(columns[0]); expect(df.getColumns()[1].toArray()).toEqual(columns[1]); expect(df.columns).toEqual(expectedColumnNames); - }); test("from arrays: invalid ", () => { const columns = [ [1, 2, 3], - [1, 2, 2] + [1, 2, 2], ]; - const fn = () => pl.DataFrame(columns, {columns: ["a", "b", "c", "d"]}); + const fn = () => pl.DataFrame(columns, { columns: ["a", "b", "c", "d"] }); expect(fn).toThrow(); }); test("from arrays with columns, orient=row", () => { const rows = [ [1, 2, 3], - [1, 2, 2] + [1, 2, 2], ]; const expectedColumns = ["a", "b", "c"]; - const df = pl.DataFrame(rows, {columns: expectedColumns, orient: "row"}); + const df = pl.DataFrame(rows, { columns: expectedColumns, orient: "row" }); expect(df.row(0).sort()).toEqual(rows[0].sort()); expect(df.row(1).sort()).toEqual(rows[1].sort()); @@ -1597,58 +1718,60 @@ describe("create", () => { }); test("from row objects, inferred schema", () => { const rows = [ - {"num": 1, "date": new Date(Date.now()), "string": "foo1"}, - {"num": 1, "date": new Date(Date.now()), "string": 1} + { num: 1, date: new Date(Date.now()), string: "foo1" }, + { num: 1, date: new Date(Date.now()), string: 1 }, ]; const expected = [ rows[0], - {num: 1, date: rows[1].date, string: rows[1].string.toString()} + { num: 1, date: rows[1].date, string: rows[1].string.toString() }, ]; - const df = pl.readRecords(rows, {inferSchemaLength: 1}); + const df = pl.readRecords(rows, { inferSchemaLength: 1 }); expect(df.toRecords()).toEqual(expected); }); test("from row objects, with schema", () => { const rows = [ - {"num": 1, "date": "foo", "string": "foo1"}, - {"num": 1, "date": "foo"} + { num: 1, date: "foo", string: "foo1" }, + { num: 1, date: "foo" }, ]; const expected = [ - {num: 1, date: rows[0].date.toString(), string: "foo1"}, - {num: 1, date: rows[1].date.toString(), string: null} + { num: 1, date: rows[0].date.toString(), string: "foo1" }, + { num: 1, date: rows[1].date.toString(), string: null }, ]; const schema = { num: pl.Int32, date: pl.Utf8, - string: pl.Utf8 + string: pl.Utf8, }; - const df = pl.readRecords(rows, {schema}); + const df = pl.readRecords(rows, { schema }); expect(df.toRecords()).toEqual(expected); expect(df.schema).toEqual(schema); }); test("from nulls", () => { - const df = pl.DataFrame({"nulls": [null, null, null]}); - const expected = pl.DataFrame([pl.Series("nulls", [null, null, null], pl.Float64)]); + const df = pl.DataFrame({ nulls: [null, null, null] }); + const expected = pl.DataFrame([ + pl.Series("nulls", [null, null, null], pl.Float64), + ]); expect(df).toFrameStrictEqual(expected); }); test("from list types", () => { const int8List = [ Int8Array.from([1, 2, 3]), Int8Array.from([2]), - Int8Array.from([1, 1, 1]) + Int8Array.from([1, 1, 1]), ]; const expected: any = { - "num_list": [[1, 2], [], [3, null]], - "bool_list": [[true, null], [], [false]], - "str_list": [["a", null], ["b", "c"], []], - "bigint_list": [[1n], [2n, 3n], []], - "int8_list": int8List + num_list: [[1, 2], [], [3, null]], + bool_list: [[true, null], [], [false]], + str_list: [["a", null], ["b", "c"], []], + bigint_list: [[1n], [2n, 3n], []], + int8_list: int8List, }; - expected.int8_list = int8List.map(i => [...i]); + expected.int8_list = int8List.map((i) => [...i]); const df = pl.DataFrame(expected); expect(df.toObject()).toEqual(expected); @@ -1656,222 +1779,262 @@ describe("create", () => { }); describe("arithmetic", () => { test("add", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).add(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .add(1); const expected = pl.DataFrame({ - "foo": [2, 3, 4], - "bar": [5, 6, 7] + foo: [2, 3, 4], + bar: [5, 6, 7], }); expect(actual).toFrameEqual(expected); }); test("sub", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).sub(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .sub(1); const expected = pl.DataFrame({ - "foo": [0, 1, 2], - "bar": [3, 4, 5] + foo: [0, 1, 2], + bar: [3, 4, 5], }); expect(actual).toFrameEqual(expected); }); test("div", () => { - const actual = pl.DataFrame({ - "foo": [2, 4, 6], - "bar": [2, 2, 2] - }).div(2); + const actual = pl + .DataFrame({ + foo: [2, 4, 6], + bar: [2, 2, 2], + }) + .div(2); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [1, 1, 1] + foo: [1, 2, 3], + bar: [1, 1, 1], }); expect(actual).toFrameEqual(expected); }); test("mul", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).mul(2); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .mul(2); const expected = pl.DataFrame({ - "foo": [2, 4, 6], - "bar": [6, 4, 2] + foo: [2, 4, 6], + bar: [6, 4, 2], }); expect(actual).toFrameEqual(expected); }); test("rem", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).rem(2); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .rem(2); const expected = pl.DataFrame({ - "foo": [1, 0, 1], - "bar": [1, 0, 1] + foo: [1, 0, 1], + bar: [1, 0, 1], }); expect(actual).toFrameEqual(expected); }); test("plus", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).plus(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .plus(1); const expected = pl.DataFrame({ - "foo": [2, 3, 4], - "bar": [5, 6, 7] + foo: [2, 3, 4], + bar: [5, 6, 7], }); expect(actual).toFrameEqual(expected); }); test("minus", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).minus(1); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .minus(1); const expected = pl.DataFrame({ - "foo": [0, 1, 2], - "bar": [3, 4, 5] + foo: [0, 1, 2], + bar: [3, 4, 5], }); expect(actual).toFrameEqual(expected); }); test("divideBy", () => { - const actual = pl.DataFrame({ - "foo": [2, 4, 6], - "bar": [2, 2, 2] - }).divideBy(2); + const actual = pl + .DataFrame({ + foo: [2, 4, 6], + bar: [2, 2, 2], + }) + .divideBy(2); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [1, 1, 1] + foo: [1, 2, 3], + bar: [1, 1, 1], }); expect(actual).toFrameEqual(expected); }); test("multiplyBy", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).multiplyBy(2); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .multiplyBy(2); const expected = pl.DataFrame({ - "foo": [2, 4, 6], - "bar": [6, 4, 2] + foo: [2, 4, 6], + bar: [6, 4, 2], }); expect(actual).toFrameEqual(expected); }); test("modulo", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).modulo(2); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .modulo(2); const expected = pl.DataFrame({ - "foo": [1, 0, 1], - "bar": [1, 0, 1] + foo: [1, 0, 1], + bar: [1, 0, 1], }); expect(actual).toFrameEqual(expected); }); test("add:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).add(pl.Series([3, 2, 1])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .add(pl.Series([3, 2, 1])); const expected = pl.DataFrame({ - "foo": [4, 4, 4], - "bar": [7, 7, 7] + foo: [4, 4, 4], + bar: [7, 7, 7], }); expect(actual).toFrameEqual(expected); }); test("sub:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).sub(pl.Series([1, 2, 3])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .sub(pl.Series([1, 2, 3])); const expected = pl.DataFrame({ - "foo": [0, 0, 0], - "bar": [3, 3, 3] + foo: [0, 0, 0], + bar: [3, 3, 3], }); expect(actual).toFrameEqual(expected); }); test("div:series", () => { - const actual = pl.DataFrame({ - "foo": [2, 4, 6], - "bar": [2, 2, 2] - }).div(pl.Series([2, 2, 1])); + const actual = pl + .DataFrame({ + foo: [2, 4, 6], + bar: [2, 2, 2], + }) + .div(pl.Series([2, 2, 1])); const expected = pl.DataFrame({ - "foo": [1, 2, 6], - "bar": [1, 1, 2] + foo: [1, 2, 6], + bar: [1, 1, 2], }); expect(actual).toFrameEqual(expected); }); test("mul:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).mul(pl.Series([2, 3, 1])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .mul(pl.Series([2, 3, 1])); const expected = pl.DataFrame({ - "foo": [2, 6, 3], - "bar": [6, 6, 1] + foo: [2, 6, 3], + bar: [6, 6, 1], }); expect(actual).toFrameEqual(expected); }); test("rem:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).rem(pl.Series([1, 1, 3])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .rem(pl.Series([1, 1, 3])); const expected = pl.DataFrame({ - "foo": [0, 0, 0], - "bar": [0, 0, 1] + foo: [0, 0, 0], + bar: [0, 0, 1], }); expect(actual).toFrameEqual(expected); }); test("plus:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).plus(pl.Series([3, 2, 1])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .plus(pl.Series([3, 2, 1])); const expected = pl.DataFrame({ - "foo": [4, 4, 4], - "bar": [7, 7, 7] + foo: [4, 4, 4], + bar: [7, 7, 7], }); expect(actual).toFrameEqual(expected); }); test("minus:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6] - }).minus(pl.Series([1, 2, 3])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .minus(pl.Series([1, 2, 3])); const expected = pl.DataFrame({ - "foo": [0, 0, 0], - "bar": [3, 3, 3] + foo: [0, 0, 0], + bar: [3, 3, 3], }); expect(actual).toFrameEqual(expected); }); test("divideBy:series", () => { - const actual = pl.DataFrame({ - "foo": [2, 4, 6], - "bar": [2, 2, 2] - }).divideBy(pl.Series([2, 2, 1])); + const actual = pl + .DataFrame({ + foo: [2, 4, 6], + bar: [2, 2, 2], + }) + .divideBy(pl.Series([2, 2, 1])); const expected = pl.DataFrame({ - "foo": [1, 2, 6], - "bar": [1, 1, 2] + foo: [1, 2, 6], + bar: [1, 1, 2], }); expect(actual).toFrameEqual(expected); }); test("multiplyBy:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).multiplyBy(pl.Series([2, 3, 1])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .multiplyBy(pl.Series([2, 3, 1])); const expected = pl.DataFrame({ - "foo": [2, 6, 3], - "bar": [6, 6, 1] + foo: [2, 6, 3], + bar: [6, 6, 1], }); expect(actual).toFrameEqual(expected); }); test("modulo:series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [3, 2, 1] - }).modulo(pl.Series([1, 1, 3])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [3, 2, 1], + }) + .modulo(pl.Series([1, 1, 3])); const expected = pl.DataFrame({ - "foo": [0, 0, 0], - "bar": [0, 0, 1] + foo: [0, 0, 0], + bar: [0, 0, 1], }); expect(actual).toFrameEqual(expected); }); @@ -1881,36 +2044,36 @@ describe("meta", () => { test("array destructuring", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); const [col0] = df; expect(col0).toSeriesEqual(df.getColumn("os")); - const [,version] = df; + const [, version] = df; expect(version).toSeriesEqual(df.getColumn("version")); - const [[row0Index0], [,row1Index1]] = df; + const [[row0Index0], [, row1Index1]] = df; expect(row0Index0).toStrictEqual("apple"); expect(row1Index1).toStrictEqual(18.04); }); test("object destructuring", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); - const {os, version} = df; + const { os, version } = df; expect(os).toSeriesEqual(df.getColumn("os")); expect(version).toSeriesEqual(df.getColumn("version")); const df2 = pl.DataFrame({ fruits: ["apple", "orange"], - cars: ["ford", "honda"] + cars: ["ford", "honda"], }); - const df3 = pl.DataFrame({...df, ...df2}); + const df3 = pl.DataFrame({ ...df, ...df2 }); const expected = df.hstack(df2); expect(df3).toFrameEqual(expected); }); test("object bracket notation", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); expect(df["os"]).toSeriesEqual(df.getColumn("os")); @@ -1922,7 +2085,7 @@ describe("meta", () => { test("object.keys shows column names", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); const keys = Object.keys(df); expect(keys).toEqual(df.columns); @@ -1930,7 +2093,7 @@ describe("meta", () => { test("object.values shows column values", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); const values = Object.values(df); expect(values[0]).toSeriesEqual(df["os"]); @@ -1939,7 +2102,7 @@ describe("meta", () => { test("df rows", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); const actual = df[0][0]; expect(actual).toStrictEqual(df.getColumn("os").get(0)); @@ -1948,13 +2111,13 @@ describe("meta", () => { test("proxy:has", () => { const df = pl.DataFrame({ os: ["apple", "linux"], - version: [10.12, 18.04] + version: [10.12, 18.04], }); expect("os" in df).toBe(true); }); test("inspect & toString", () => { const df = pl.DataFrame({ - a: [1] + a: [1], }); const expected = `shape: (1, 1) ┌─────┐ @@ -1971,23 +2134,22 @@ describe("meta", () => { }); }); - describe("additional", () => { test("partitionBy", () => { const df = pl.DataFrame({ label: ["a", "a", "b", "b"], - value: [1, 2, 3, 4] + value: [1, 2, 3, 4], }); - const dfs = df.partitionBy(["label"], true).map(df => df.toObject()); + const dfs = df.partitionBy(["label"], true).map((df) => df.toObject()); const expected = [ { label: ["a", "a"], - value: [1, 2] + value: [1, 2], }, { label: ["b", "b"], - value: [3, 4] - } + value: [3, 4], + }, ]; expect(dfs).toEqual(expected); @@ -1995,18 +2157,18 @@ describe("additional", () => { test("partitionBy with callback", () => { const df = pl.DataFrame({ label: ["a", "a", "b", "b"], - value: [1, 2, 3, 4] + value: [1, 2, 3, 4], }); - const dfs = df.partitionBy(["label"], true, df => df.toObject()); + const dfs = df.partitionBy(["label"], true, (df) => df.toObject()); const expected = [ { label: ["a", "a"], - value: [1, 2] + value: [1, 2], }, { label: ["b", "b"], - value: [3, 4] - } + value: [3, 4], + }, ]; expect(dfs).toEqual(expected); diff --git a/__tests__/datelike.test.ts b/__tests__/datelike.test.ts index 9e9faa577..7f8a31956 100644 --- a/__tests__/datelike.test.ts +++ b/__tests__/datelike.test.ts @@ -2,9 +2,9 @@ import pl from "@polars"; describe("datelike", () => { test("asof join", () => { const fmt = "%F %T%.3f"; - const quotes = pl.DataFrame( - { - dates: pl.Series([ + const quotes = pl.DataFrame({ + dates: pl + .Series([ "2016-05-25 13:30:00.023", "2016-05-25 13:30:00.023", "2016-05-25 13:30:00.030", @@ -12,70 +12,54 @@ describe("datelike", () => { "2016-05-25 13:30:00.048", "2016-05-25 13:30:00.049", "2016-05-25 13:30:00.072", - "2016-05-25 13:30:00.075" - ]).str.strptime(pl.Datetime("ms"), fmt), - ticker: [ - "GOOG", - "MSFT", - "MSFT", - "MSFT", - "GOOG", - "AAPL", - "GOOG", - "MSFT", - ], - bid: [ - 720.5, - 51.95, - 51.97, - 51.99, - 720.50, - 97.99, - 720.50, - 52.01 - ], - }); + "2016-05-25 13:30:00.075", + ]) + .str.strptime(pl.Datetime("ms"), fmt), + ticker: ["GOOG", "MSFT", "MSFT", "MSFT", "GOOG", "AAPL", "GOOG", "MSFT"], + bid: [720.5, 51.95, 51.97, 51.99, 720.5, 97.99, 720.5, 52.01], + }); const trades = pl.DataFrame({ - dates: pl.Series([ - "2016-05-25 13:30:00.023", - "2016-05-25 13:30:00.038", - "2016-05-25 13:30:00.048", - "2016-05-25 13:30:00.048", - "2016-05-25 13:30:00.048" - ]).str.strptime(pl.Datetime("ms"), fmt), - ticker: [ - "MSFT", - "MSFT", - "GOOG", - "GOOG", - "AAPL", - ], - bid: [ - 51.95, - 51.95, - 720.77, - 720.92, - 98.0 - ], + dates: pl + .Series([ + "2016-05-25 13:30:00.023", + "2016-05-25 13:30:00.038", + "2016-05-25 13:30:00.048", + "2016-05-25 13:30:00.048", + "2016-05-25 13:30:00.048", + ]) + .str.strptime(pl.Datetime("ms"), fmt), + ticker: ["MSFT", "MSFT", "GOOG", "GOOG", "AAPL"], + bid: [51.95, 51.95, 720.77, 720.92, 98.0], }); - let out: any = trades.joinAsof(quotes, {on: "dates"}); - expect(out.columns).toEqual(["dates", "ticker", "bid", "ticker_right", "bid_right"]); - expect(out.getColumn("dates").cast(pl.Float64) - .div(1000) - .toArray()).toEqual([ - 1464183000023, - 1464183000038, - 1464183000048, - 1464183000048, - 1464183000048, + let out: any = trades.joinAsof(quotes, { on: "dates" }); + expect(out.columns).toEqual([ + "dates", + "ticker", + "bid", + "ticker_right", + "bid_right", ]); - out = trades.joinAsof(quotes, {on:"dates", strategy:"forward"}).getColumn("bid_right") + expect(out.getColumn("dates").cast(pl.Float64).div(1000).toArray()).toEqual( + [ + 1464183000023, 1464183000038, 1464183000048, 1464183000048, + 1464183000048, + ], + ); + out = trades + .joinAsof(quotes, { on: "dates", strategy: "forward" }) + .getColumn("bid_right") .toArray(); expect(out).toEqual([720.5, 51.99, 720.5, 720.5, 720.5]); - out = trades.joinAsof(quotes, {on:"dates", by:"ticker"}); - expect(out.getColumn("bid_right").toArray()).toEqual([51.95, 51.97, 720.5, 720.5, null]); - out = quotes.joinAsof(trades, {on:"dates", by:"ticker"}); + out = trades.joinAsof(quotes, { on: "dates", by: "ticker" }); + expect(out.getColumn("bid_right").toArray()).toEqual([ + 51.95, + 51.97, + 720.5, + 720.5, + null, + ]); + out = quotes.joinAsof(trades, { on: "dates", by: "ticker" }); expect(out.getColumn("bid_right").toArray()).toEqual([ null, 51.95, @@ -86,37 +70,34 @@ describe("datelike", () => { 720.92, 51.95, ]); - out = quotes.joinAsof(trades, {on:"dates", strategy:"backward", tolerance:"5ms"})[ - "bid_right" - ].toArray(); + out = quotes + .joinAsof(trades, { on: "dates", strategy: "backward", tolerance: "5ms" }) + ["bid_right"].toArray(); expect(out).toEqual([51.95, 51.95, null, 51.95, 98.0, 98.0, null, null]); - out = quotes.joinAsof(trades, {on:"dates", strategy:"forward", tolerance:"5ms"})[ - "bid_right" - ].toArray(); + out = quotes + .joinAsof(trades, { on: "dates", strategy: "forward", tolerance: "5ms" }) + ["bid_right"].toArray(); expect(out).toEqual([51.95, 51.95, null, null, 720.77, null, null, null]); }); test("asofjoin tolerance grouper", () => { + const df1 = pl.DataFrame({ + date: [new Date(2020, 1, 5), new Date(2020, 1, 10)], + by: [1, 1], + }); + const df2 = pl.DataFrame({ + date: [new Date(2020, 1, 5), new Date(2020, 1, 6)], + by: [1, 1], + values: [100, 200], + }); - const df1 = pl.DataFrame({"date": [new Date(2020, 1, 5), new Date(2020, 1, 10)], "by": [1, 1]}); - const df2 = pl.DataFrame( - { - "date": [new Date(2020, 1, 5), new Date(2020, 1, 6)], - "by": [1, 1], - "values": [100, 200], - } - ); - - const out = df1.joinAsof(df2, {by: "by", on:"date", tolerance:"3d"}); + const out = df1.joinAsof(df2, { by: "by", on: "date", tolerance: "3d" }); - const expected = pl.DataFrame( - { - "date": [new Date(2020, 1, 5), new Date(2020, 1, 10)], - "by": [1, 1], - "values": [100, null], - } - ); + const expected = pl.DataFrame({ + date: [new Date(2020, 1, 5), new Date(2020, 1, 10)], + by: [1, 1], + values: [100, null], + }); expect(out).toFrameEqual(expected); - }); }); diff --git a/__tests__/expr.test.ts b/__tests__/expr.test.ts index fa407a55c..d7bf84139 100644 --- a/__tests__/expr.test.ts +++ b/__tests__/expr.test.ts @@ -1351,7 +1351,8 @@ describe("expr.lst", () => { expect( df .select(pl.concatList(["a", "b"]).alias("a")) - .getColumn("a").seriesEqual(expected), + .getColumn("a") + .seriesEqual(expected), ).toBeTruthy(); expect( df diff --git a/__tests__/functions.test.ts b/__tests__/functions.test.ts index 5c81fd2f4..ba868a631 100644 --- a/__tests__/functions.test.ts +++ b/__tests__/functions.test.ts @@ -3,17 +3,17 @@ import pl from "@polars"; describe("concat", () => { it("can concat multiple dataframes vertically", () => { const df1 = pl.DataFrame({ - "a": [1, 2, 3], - "b": ["a", "b", "c"] + a: [1, 2, 3], + b: ["a", "b", "c"], }); const df2 = pl.DataFrame({ - "a": [4, 5, 6], - "b": ["d", "e", "f"] + a: [4, 5, 6], + b: ["d", "e", "f"], }); const actual = pl.concat([df1, df2]); const expected = pl.DataFrame({ - "a": [1, 2, 3, 4, 5, 6], - "b": ["a", "b", "c", "d", "e", "f"] + a: [1, 2, 3, 4, 5, 6], + b: ["a", "b", "c", "d", "e", "f"], }); expect(actual).toFrameEqual(expected); }); @@ -36,18 +36,20 @@ describe("concat", () => { expect(fn).toThrowError(); }); test("horizontal concat", () => { - const a = pl.DataFrame({"a": ["a", "b"], "b": [1, 2]}); - const b = pl.DataFrame({"c": [5, 7, 8, 9], "d": [1, 2, 1, 2], "e": [1, 2, 1, 2]}); - const actual = pl.concat([a, b], {how:"horizontal"}); - const expected = pl.DataFrame( - { - "a": ["a", "b", null, null], - "b": [1, 2, null, null], - "c": [5, 7, 8, 9], - "d": [1, 2, 1, 2], - "e": [1, 2, 1, 2], - } - ); + const a = pl.DataFrame({ a: ["a", "b"], b: [1, 2] }); + const b = pl.DataFrame({ + c: [5, 7, 8, 9], + d: [1, 2, 1, 2], + e: [1, 2, 1, 2], + }); + const actual = pl.concat([a, b], { how: "horizontal" }); + const expected = pl.DataFrame({ + a: ["a", "b", null, null], + b: [1, 2, null, null], + c: [5, 7, 8, 9], + d: [1, 2, 1, 2], + e: [1, 2, 1, 2], + }); expect(actual).toFrameEqual(expected); }); }); @@ -55,7 +57,10 @@ describe("repeat", () => { it("repeats a value n number of times into a series", () => { const value = "foobar"; const actual = pl.repeat(value, 4, "foo"); - const expected = pl.Series("foo", Array.from({length: 4}, () => value)); + const expected = pl.Series( + "foo", + Array.from({ length: 4 }, () => value), + ); expect(actual).toSeriesEqual(expected); }); diff --git a/__tests__/groupby.test.ts b/__tests__/groupby.test.ts index d6299e798..8e1627547 100644 --- a/__tests__/groupby.test.ts +++ b/__tests__/groupby.test.ts @@ -1,185 +1,155 @@ import pl from "@polars"; - describe("groupby", () => { let df: pl.DataFrame; beforeEach(() => { df = pl.DataFrame({ - "name": ["a", "b", "a", "c", "b"], - "foo": [1, 3, 3, 5, 7], - "bar": [2, 4, 4, 6, 8] + name: ["a", "b", "a", "c", "b"], + foo: [1, 3, 3, 5, 7], + bar: [2, 4, 4, 6, 8], }); }); test("aggList", () => { const s = pl.Series("a", [1], pl.Int16); - const actual = df - .groupBy("name") - .aggList() - .sort("name"); + const actual = df.groupBy("name").aggList().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [[1, 3], [3, 7], [5]], - "bar": [[2, 4], [4, 8], [6]] + name: ["a", "b", "c"], + foo: [[1, 3], [3, 7], [5]], + bar: [[2, 4], [4, 8], [6]], }); expect(actual).toFrameEqual(expected); }); test("agg:column", () => { - const actual = df.groupBy("name").agg({ - "foo": "min" - }) + const actual = df + .groupBy("name") + .agg({ + foo: "min", + }) .sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [1, 3, 5] + name: ["a", "b", "c"], + foo: [1, 3, 5], }); expect(actual).toFrameEqual(expected); }); test("agg:columns", () => { - const actual = df.groupBy("name").agg({ - "foo": "min", - "bar": "sum" - }) + const actual = df + .groupBy("name") + .agg({ + foo: "min", + bar: "sum", + }) .sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [1, 3, 5], - "bar": [6, 12, 6] + name: ["a", "b", "c"], + foo: [1, 3, 5], + bar: [6, 12, 6], }); expect(actual).toFrameEqual(expected); }); test("count", () => { - const actual = df.groupBy("name").count() - .sort("name"); + const actual = df.groupBy("name").count().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo_count": [2, 2, 1], - "bar_count": [2, 2, 1] + name: ["a", "b", "c"], + foo_count: [2, 2, 1], + bar_count: [2, 2, 1], }); expect(actual).toFrameEqual(expected); }); test("first", () => { - const actual = df.groupBy("name").first() - .sort("name"); + const actual = df.groupBy("name").first().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [1, 3, 5], - "bar": [2, 4, 6] + name: ["a", "b", "c"], + foo: [1, 3, 5], + bar: [2, 4, 6], }); expect(actual).toFrameEqual(expected); }); test("head", () => { - const actual = df - .groupBy("name") - .head(1) - .sort("name"); + const actual = df.groupBy("name").head(1).sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [[1], [3], [5]], - "bar": [[2], [4], [6]] + name: ["a", "b", "c"], + foo: [[1], [3], [5]], + bar: [[2], [4], [6]], }); expect(actual).toFrameEqual(expected); }); test("last", () => { - const actual = df - .groupBy("name") - .last() - .sort("name"); + const actual = df.groupBy("name").last().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [3, 7, 5], - "bar": [4, 8, 6] + name: ["a", "b", "c"], + foo: [3, 7, 5], + bar: [4, 8, 6], }); expect(actual).toFrameEqual(expected); }); test("tail", () => { - const actual = df - .groupBy("name") - .tail(1) - .sort("name"); + const actual = df.groupBy("name").tail(1).sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [[3], [7], [5]], - "bar": [[4], [8], [6]] + name: ["a", "b", "c"], + foo: [[3], [7], [5]], + bar: [[4], [8], [6]], }); expect(actual).toFrameEqual(expected); }); test("max", () => { - const actual = df - .groupBy("name") - .max() - .sort("name"); + const actual = df.groupBy("name").max().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [3, 7, 5], - "bar": [4, 8, 6] + name: ["a", "b", "c"], + foo: [3, 7, 5], + bar: [4, 8, 6], }); expect(actual).toFrameEqual(expected); }); test("mean", () => { - const actual = df - .groupBy("name") - .mean() - .sort("name"); + const actual = df.groupBy("name").mean().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [2, 5, 5], - "bar": [3, 6, 6] + name: ["a", "b", "c"], + foo: [2, 5, 5], + bar: [3, 6, 6], }); expect(actual).toFrameEqual(expected); }); test("median", () => { - const actual = df - .groupBy("name") - .median() - .sort("name"); + const actual = df.groupBy("name").median().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [2, 5, 5], - "bar": [3, 6, 6] + name: ["a", "b", "c"], + foo: [2, 5, 5], + bar: [3, 6, 6], }); expect(actual).toFrameEqual(expected); }); test("min", () => { - const actual = df - .groupBy("name") - .min() - .sort("name"); + const actual = df.groupBy("name").min().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [1, 3, 5], - "bar": [2, 4, 6] + name: ["a", "b", "c"], + foo: [1, 3, 5], + bar: [2, 4, 6], }); expect(actual).toFrameEqual(expected); }); test("nUnique", () => { - const actual = df - .groupBy("name") - .nUnique() - .sort("name"); + const actual = df.groupBy("name").nUnique().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [2, 2, 1], - "bar": [2, 2, 1] + name: ["a", "b", "c"], + foo: [2, 2, 1], + bar: [2, 2, 1], }); expect(actual).toFrameEqual(expected); }); test("sum", () => { - const actual = df - .groupBy("name") - .sum() - .sort("name"); + const actual = df.groupBy("name").sum().sort("name"); const expected = pl.DataFrame({ - "name": ["a", "b", "c"], - "foo": [4, 10, 5], - "bar": [6, 12, 6] + name: ["a", "b", "c"], + foo: [4, 10, 5], + bar: [6, 12, 6], }); expect(actual).toFrameEqual(expected); }); test.todo("groups"); - }); describe("groupby ops", () => { test("rolling", () => { @@ -193,41 +163,37 @@ describe("groupby ops", () => { ]; const df = pl - .DataFrame({"dt": dates, "a": [3, 7, 5, 9, 2, 1]}) + .DataFrame({ dt: dates, a: [3, 7, 5, 9, 2, 1] }) .withColumn(pl.col("dt").str.strptime(pl.Datetime("ms"))); const a = pl.col("a"); - const out = df.groupByRolling({indexColumn:"dt", period:"2d"}).agg( - a.sum().as("sum_a"), - a.min().as("min_a"), - a.max().as("max_a"), - ); + const out = df + .groupByRolling({ indexColumn: "dt", period: "2d" }) + .agg(a.sum().as("sum_a"), a.min().as("min_a"), a.max().as("max_a")); expect(out["sum_a"].toArray()).toEqual([3, 10, 15, 24, 11, 1]); expect(out["max_a"].toArray()).toEqual([3, 7, 7, 9, 9, 1]); expect(out["min_a"].toArray()).toEqual([3, 3, 3, 3, 2, 1]); }); test("dynamic - 1", () => { const df = pl.DataFrame({ - "event_date": [ + event_date: [ new Date("2021-04-11"), new Date("2021-04-29"), new Date("2021-05-29"), ], - "adm1_code": [1, 2, 1], - "five_type": ["a", "b", "a"], - "actor": ["a", "a", "a"], - "admin": ["a", "a", "a"], - "fatalities": [10, 20, 30], + adm1_code: [1, 2, 1], + five_type: ["a", "b", "a"], + actor: ["a", "a", "a"], + admin: ["a", "a", "a"], + fatalities: [10, 20, 30], }); - const out = df.groupByDynamic({ - by: ["admin", "five_type", "actor"], - indexColumn: "event_date", - every: "1mo", - }).agg( - pl.col("adm1_code").unique(), - pl.col("fatalities").gt(0) - .sum() - ); + const out = df + .groupByDynamic({ + by: ["admin", "five_type", "actor"], + indexColumn: "event_date", + every: "1mo", + }) + .agg(pl.col("adm1_code").unique(), pl.col("fatalities").gt(0).sum()); const expected = [ new Date("2021-04-01"), new Date("2021-05-01"), @@ -235,33 +201,27 @@ describe("groupby ops", () => { ]; const actual = out.getColumn("event_date"); expect(actual.toArray()).toEqual(expected); - }); test("dynamic - 2", () => { - const df = pl.DataFrame( - { - "event_date": [ - new Date("2021-04-11"), - new Date("2021-04-29"), - new Date("2021-05-29"), - ], - "adm1_code": [1, 2, 1], - "five_type": ["a", "b", "a"], - "actor": ["a", "a", "a"], - "admin": ["a", "a", "a"], - "fatalities": [10, 20, 30], - } - ); - const out = df.groupByDynamic({ - indexColumn: "event_date", - every: "1mo", - by: ["admin", "five_type", "actor"] - }).agg( - pl.col("adm1_code").unique(), - pl.col("fatalities") - .gt(0) - .sum() - ); + const df = pl.DataFrame({ + event_date: [ + new Date("2021-04-11"), + new Date("2021-04-29"), + new Date("2021-05-29"), + ], + adm1_code: [1, 2, 1], + five_type: ["a", "b", "a"], + actor: ["a", "a", "a"], + admin: ["a", "a", "a"], + fatalities: [10, 20, 30], + }); + const out = df + .groupByDynamic({ + indexColumn: "event_date", + every: "1mo", + by: ["admin", "five_type", "actor"], + }) + .agg(pl.col("adm1_code").unique(), pl.col("fatalities").gt(0).sum()); const expected = [ new Date("2021-04-01"), new Date("2021-05-01"), @@ -277,19 +237,24 @@ describe("groupby ops", () => { new Date("2020-02-01"), new Date("2020-03-01"), ]; - const df = pl.DataFrame({dt: dates, idx: Array.from({length: dates.length}, (_v, k) => k)}); - const actual = df.groupByDynamic({ - indexColumn: "dt", - every: "1mo", - closed: "right" - }).agg(pl.col("idx")); + const df = pl.DataFrame({ + dt: dates, + idx: Array.from({ length: dates.length }, (_v, k) => k), + }); + const actual = df + .groupByDynamic({ + indexColumn: "dt", + every: "1mo", + closed: "right", + }) + .agg(pl.col("idx")); const expected = pl.DataFrame({ - "dt": [ + dt: [ new Date("2019-12-01"), new Date("2020-01-01"), new Date("2020-02-01"), ], - "idx": [[0], [1, 2], [3]], + idx: [[0], [1, 2], [3]], }); expect(actual).toFrameEqual(expected); }); diff --git a/__tests__/io.test.ts b/__tests__/io.test.ts index ca7c0512f..daea0c1dc 100644 --- a/__tests__/io.test.ts +++ b/__tests__/io.test.ts @@ -1,6 +1,6 @@ import pl from "@polars"; import path from "path"; -import {Stream} from "stream"; +import { Stream } from "stream"; import fs from "fs"; // eslint-disable-next-line no-undef const csvpath = path.resolve(__dirname, "./examples/datasets/foods1.csv"); @@ -15,35 +15,36 @@ const jsonpath = path.resolve(__dirname, "./examples/foods.json"); describe("read:csv", () => { it("can read from a csv file", () => { const df = pl.readCSV(csvpath); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can read from a relative file", () => { const df = pl.readCSV(csvpath); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can read from a csv file with options", () => { - const df = pl.readCSV(csvpath, {hasHeader: false, skipRows: 1, nRows: 4}); - expect(df.shape).toEqual({height: 4, width: 4}); + const df = pl.readCSV(csvpath, { hasHeader: false, skipRows: 1, nRows: 4 }); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); it("can read from a csv string", () => { const csvString = "foo,bar,baz\n1,2,3\n4,5,6\n"; const df = pl.readCSV(csvString); - expect(df.toCSV().toString() - .slice(0, 22)).toEqual(csvString.slice(0, 22)); + expect(df.toCSV().toString().slice(0, 22)).toEqual(csvString.slice(0, 22)); }); it("can read from a csv buffer", () => { const csvBuffer = Buffer.from("foo,bar,baz\n1,2,3\n4,5,6\n", "utf-8"); const df = pl.readCSV(csvBuffer); - expect(df.toCSV().toString("utf-8") - .slice(0, 22)).toEqual(csvBuffer.toString("utf-8").slice(0, 22)); + expect(df.toCSV().toString("utf-8").slice(0, 22)).toEqual( + csvBuffer.toString("utf-8").slice(0, 22), + ); }); it("can read from a csv buffer with options", () => { const csvBuffer = Buffer.from("foo,bar,baz\n1,2,3\n4,5,6\n", "utf-8"); - const df = pl.readCSV(csvBuffer, {hasHeader: true, chunkSize: 10}); + const df = pl.readCSV(csvBuffer, { hasHeader: true, chunkSize: 10 }); // the newline characters are confusing jest - expect(df.toCSV().toString("utf-8") - .slice(0, 22)).toEqual(csvBuffer.toString("utf-8").slice(0, 22)); + expect(df.toCSV().toString("utf-8").slice(0, 22)).toEqual( + csvBuffer.toString("utf-8").slice(0, 22), + ); }); it("can parse datetimes", () => { const csv = `timestamp,open,high @@ -51,26 +52,29 @@ describe("read:csv", () => { 2021-01-01 00:15:00,0.00298800,0.00300400 2021-01-01 00:30:00,0.00298300,0.00300100 2021-01-01 00:45:00,0.00299400,0.00304000`; - const df = pl.readCSV(csv, {parseDates: true}); - expect(df.dtypes.map(dt => dt.toJSON())).toEqual([pl.Datetime("us").toJSON(), pl.Float64.toJSON(), pl.Float64.toJSON()]); + const df = pl.readCSV(csv, { parseDates: true }); + expect(df.dtypes.map((dt) => dt.toJSON())).toEqual([ + pl.Datetime("us").toJSON(), + pl.Float64.toJSON(), + pl.Float64.toJSON(), + ]); }); it.each` - csv | nullValues - ${"a,b,c\nna,b,c\na,na,c"} | ${"na"} - ${"a,b,c\nna,b,c\na,n/a,c"} | ${["na", "n/a"]} - ${"a,b,c\nna,b,c\na,n/a,c"} | ${{"a": "na", "b": "n/a"}} - `("can handle null values", ({csv, nullValues}) => { - const df = pl.readCSV(csv, {nullValues}); + csv | nullValues + ${"a,b,c\nna,b,c\na,na,c"} | ${"na"} + ${"a,b,c\nna,b,c\na,n/a,c"} | ${["na", "n/a"]} + ${"a,b,c\nna,b,c\na,n/a,c"} | ${{ a: "na", b: "n/a" }} + `("can handle null values", ({ csv, nullValues }) => { + const df = pl.readCSV(csv, { nullValues }); expect(df.getColumn("a")[0]).toBeNull(); expect(df.getColumn("b")[1]).toBeNull(); }); test("csv with rowcount", () => { - const df = pl.readCSV(csvpath, {rowCount: {name: "rc", offset: 11}}); + const df = pl.readCSV(csvpath, { rowCount: { name: "rc", offset: 11 } }); const expectedMaxRowCount = df.height + 10; const maxRowCount = df.getColumn("rc").max(); expect(expectedMaxRowCount).toStrictEqual(maxRowCount); - }); it.todo("can read from a stream"); }); @@ -78,73 +82,72 @@ describe("read:csv", () => { describe("read:json", () => { it("can read from a json file", () => { const df = pl.readJSON(jsonpath); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can specify read options", () => { - const df = pl.readJSON(jsonpath, {batchSize: 10, inferSchemaLength: 100}); - expect(df.shape).toEqual({height: 27, width: 4}); + const df = pl.readJSON(jsonpath, { batchSize: 10, inferSchemaLength: 100 }); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can read from a json buffer", () => { const json = [ - JSON.stringify({bar: "1", foo: 1}), - JSON.stringify({bar: "1", foo: 2}), - "" + JSON.stringify({ bar: "1", foo: 1 }), + JSON.stringify({ bar: "1", foo: 2 }), + "", ].join("\n"); const df = pl.readJSON(Buffer.from(json)); - expect(df.writeJSON({format:"lines"}).toString() - .slice(0, 30)).toEqual(json.slice(0, 30)); - + expect(df.writeJSON({ format: "lines" }).toString().slice(0, 30)).toEqual( + json.slice(0, 30), + ); }); }); describe("scan", () => { it("can lazy load (scan) from a csv file", () => { const df = pl.scanCSV(csvpath).collectSync(); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can lazy load (scan) from a json file", () => { const df = pl.scanJson(jsonpath).collectSync(); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can lazy load (scan) from a csv file with options", () => { const df = pl .scanCSV(csvpath, { hasHeader: false, skipRows: 1, - nRows: 4 + nRows: 4, }) .collectSync(); - expect(df.shape).toEqual({height: 4, width: 4}); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); it("can lazy load (scan) from a ipc file", () => { const df = pl.scanCSV(csvpath).collectSync(); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); it("can lazy load (scan) from a csv file with options", () => { const df = pl .scanCSV(csvpath, { hasHeader: false, skipRows: 1, - nRows: 4 + nRows: 4, }) .collectSync(); - expect(df.shape).toEqual({height: 4, width: 4}); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); it("can lazy load (scan) from a parquet file with options", () => { - pl - .readCSV(csvpath, { - hasHeader: false, - skipRows: 1, - nRows: 4 - }).writeParquet(parquetpath); + pl.readCSV(csvpath, { + hasHeader: false, + skipRows: 1, + nRows: 4, + }).writeParquet(parquetpath); const df = pl.scanParquet(parquetpath).collectSync(); - expect(df.shape).toEqual({height: 4, width: 4}); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); }); @@ -158,34 +161,34 @@ describe("parquet", () => { test("read", () => { const df = pl.readParquet(parquetpath); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("read:buffer", () => { const buff = fs.readFileSync(parquetpath); const df = pl.readParquet(buff); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("read:compressed", () => { const csvDF = pl.readCSV(csvpath); - csvDF.writeParquet(parquetpath, {compression: "lz4"}); + csvDF.writeParquet(parquetpath, { compression: "lz4" }); const df = pl.readParquet(parquetpath); expect(df).toFrameEqual(csvDF); }); test("read:options", () => { - const df = pl.readParquet(parquetpath, {numRows: 4}); - expect(df.shape).toEqual({height: 4, width: 4}); + const df = pl.readParquet(parquetpath, { numRows: 4 }); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); test("scan", () => { const df = pl.scanParquet(parquetpath).collectSync(); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("scan:options", () => { - const df = pl.scanParquet(parquetpath, {numRows: 4}).collectSync(); - expect(df.shape).toEqual({height: 4, width: 4}); + const df = pl.scanParquet(parquetpath, { numRows: 4 }).collectSync(); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); }); @@ -199,34 +202,33 @@ describe("ipc", () => { test("read", () => { const df = pl.readIPC(ipcpath); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("read/write:buffer", () => { - - const buff = pl.readCSV(csvpath).writeIPC(); + const buff = pl.readCSV(csvpath).writeIPC(); const df = pl.readIPC(buff); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("read:compressed", () => { const csvDF = pl.readCSV(csvpath); - csvDF.writeIPC(ipcpath, {compression: "lz4"}); + csvDF.writeIPC(ipcpath, { compression: "lz4" }); const ipcDF = pl.readIPC(ipcpath); expect(ipcDF).toFrameEqual(csvDF); }); test.skip("read:options", () => { - const df = pl.readIPC(ipcpath, {nRows: 4}); - expect(df.shape).toEqual({height: 4, width: 4}); + const df = pl.readIPC(ipcpath, { nRows: 4 }); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); test("scan", () => { const df = pl.scanIPC(ipcpath).collectSync(); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test.skip("scan:options", () => { - const df = pl.scanIPC(ipcpath, {nRows: 4}).collectSync(); - expect(df.shape).toEqual({height: 4, width: 4}); + const df = pl.scanIPC(ipcpath, { nRows: 4 }).collectSync(); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); test("writeIPC", () => { @@ -235,10 +237,8 @@ describe("ipc", () => { const ipcDF = pl.readIPC(ipcpath); expect(ipcDF).toFrameEqual(csvDF); }); - }); - describe("avro", () => { beforeEach(() => { pl.readCSV(csvpath).writeAvro(avropath); @@ -250,7 +250,7 @@ describe("avro", () => { test("round trip", () => { const expected = pl.DataFrame({ foo: [1, 2, 3], - bar: ["a", "b", "c"] + bar: ["a", "b", "c"], }); const buf = expected.writeAvro(); const actual = pl.readAvro(buf); @@ -258,83 +258,85 @@ describe("avro", () => { }); test("read", () => { const df = pl.readAvro(avropath); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("read:buffer", () => { const buff = fs.readFileSync(avropath); const df = pl.readAvro(buff); - expect(df.shape).toEqual({height: 27, width: 4}); + expect(df.shape).toEqual({ height: 27, width: 4 }); }); test("read:compressed", () => { const csvDF = pl.readCSV(csvpath); - csvDF.writeAvro(avropath, {compression: "snappy"}); + csvDF.writeAvro(avropath, { compression: "snappy" }); const df = pl.readAvro(avropath); expect(df).toFrameEqual(csvDF); }); test.skip("read:options", () => { - const df = pl.readAvro(avropath, {nRows: 4}); - expect(df.shape).toEqual({height: 4, width: 4}); + const df = pl.readAvro(avropath, { nRows: 4 }); + expect(df.shape).toEqual({ height: 4, width: 4 }); }); }); describe("stream", () => { test("readCSV", async () => { - const readStream = new Stream.Readable({read(){}}); - readStream.push(`a,b\n`); - readStream.push(`1,2\n`); - readStream.push(`2,2\n`); - readStream.push(`3,2\n`); - readStream.push(`4,2\n`); + const readStream = new Stream.Readable({ read() {} }); + readStream.push("a,b\n"); + readStream.push("1,2\n"); + readStream.push("2,2\n"); + readStream.push("3,2\n"); + readStream.push("4,2\n"); readStream.push(null); const expected = pl.DataFrame({ a: pl.Series("a", [1, 2, 3, 4], pl.Int64), - b: pl.Series("b", [2, 2, 2, 2], pl.Int64) + b: pl.Series("b", [2, 2, 2, 2], pl.Int64), }); - const df = await pl.readCSVStream(readStream, {chunkSize: 2}); + const df = await pl.readCSVStream(readStream, { chunkSize: 2 }); expect(df).toFrameEqual(expected); }); test("readCSV:schema mismatch", async () => { - const readStream = new Stream.Readable({read(){}}); - readStream.push(`a,b,c\n`); - readStream.push(`1,2\n`); - readStream.push(`2,2\n`); - readStream.push(`3,2\n`); - readStream.push(`11,1,2,3,4,5,1\n`); - readStream.push(`null`); + const readStream = new Stream.Readable({ read() {} }); + readStream.push("a,b,c\n"); + readStream.push("1,2\n"); + readStream.push("2,2\n"); + readStream.push("3,2\n"); + readStream.push("11,1,2,3,4,5,1\n"); + readStream.push("null"); readStream.push(null); - const promise = pl.readCSVStream(readStream, {inferSchemaLength: 2, ignoreErrors: false}); + const promise = pl.readCSVStream(readStream, { + inferSchemaLength: 2, + ignoreErrors: false, + }); await expect(promise).rejects.toBeDefined(); }); test("readJSON", async () => { - const readStream = new Stream.Readable({read(){}}); - readStream.push(`${JSON.stringify({a: 1, b: 2})} \n`); - readStream.push(`${JSON.stringify({a: 2, b: 2})} \n`); - readStream.push(`${JSON.stringify({a: 3, b: 2})} \n`); - readStream.push(`${JSON.stringify({a: 4, b: 2})} \n`); + const readStream = new Stream.Readable({ read() {} }); + readStream.push(`${JSON.stringify({ a: 1, b: 2 })} \n`); + readStream.push(`${JSON.stringify({ a: 2, b: 2 })} \n`); + readStream.push(`${JSON.stringify({ a: 3, b: 2 })} \n`); + readStream.push(`${JSON.stringify({ a: 4, b: 2 })} \n`); readStream.push(null); const expected = pl.DataFrame({ a: pl.Series("a", [1, 2, 3, 4], pl.Int64), - b: pl.Series("b", [2, 2, 2, 2], pl.Int64) + b: pl.Series("b", [2, 2, 2, 2], pl.Int64), }); const df = await pl.readJSONStream(readStream); expect(df).toFrameEqual(expected); }); test.skip("readJSON:error", async () => { - const readStream = new Stream.Readable({read(){}}); - readStream.push(`${JSON.stringify({a: 1, b: 2})} \n`); - readStream.push(`${JSON.stringify({a: 2, b: 2})} \n`); - readStream.push(`${JSON.stringify({a: 3, b: 2})} \n`); - readStream.push(`not parseable json `); + const readStream = new Stream.Readable({ read() {} }); + readStream.push(`${JSON.stringify({ a: 1, b: 2 })} \n`); + readStream.push(`${JSON.stringify({ a: 2, b: 2 })} \n`); + readStream.push(`${JSON.stringify({ a: 3, b: 2 })} \n`); + readStream.push("not parseable json "); readStream.push(null); await expect(pl.readJSONStream(readStream)).rejects.toBeDefined(); - }); }); diff --git a/__tests__/lazy_functions.test.ts b/__tests__/lazy_functions.test.ts index 0dc0f5d49..e86e9be07 100644 --- a/__tests__/lazy_functions.test.ts +++ b/__tests__/lazy_functions.test.ts @@ -1,142 +1,163 @@ -import pl, {col, cols, lit} from "@polars/index"; -import {df as _df} from "./setup"; +import pl, { col, cols, lit } from "@polars/index"; +import { df as _df } from "./setup"; describe("lazy functions", () => { test("col:string", () => { const expected = pl.Series("foo", [1, 2, 3]); const other = pl.Series("other", [1, 2, 3]); - const df = pl.DataFrame([ - expected, - other - ]); + const df = pl.DataFrame([expected, other]); const actual = df.select(col("foo")); expect(actual).toFrameEqual(expected.toFrame()); }); test("col:string[]", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - "other": ["a", "b", "c"] - }).select(col(["foo", "bar"])); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + other: ["a", "b", "c"], + }) + .select(col(["foo", "bar"])); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], + foo: [1, 2, 3], + bar: [4, 5, 6], }); expect(actual).toFrameEqual(expected); }); test("col:series", () => { const columnsToSelect = pl.Series(["foo", "bar"]); - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - "other": ["a", "b", "c"] - }).select(col(columnsToSelect)); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + other: ["a", "b", "c"], + }) + .select(col(columnsToSelect)); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], + foo: [1, 2, 3], + bar: [4, 5, 6], }); expect(actual).toFrameEqual(expected); }); test("cols", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - "other": ["a", "b", "c"] - }).select(cols("foo", "bar")); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + other: ["a", "b", "c"], + }) + .select(cols("foo", "bar")); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], + foo: [1, 2, 3], + bar: [4, 5, 6], }); expect(actual).toFrameEqual(expected); }); describe("lit", () => { test("string", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - }).select(col("foo"), lit("a").as("lit_a")); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .select(col("foo"), lit("a").as("lit_a")); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "lit_a": ["a", "a", "a"], + foo: [1, 2, 3], + lit_a: ["a", "a", "a"], }); expect(actual).toFrameEqual(expected); }); test("number", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - }).select(col("foo"), lit(-99).as("number")); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .select(col("foo"), lit(-99).as("number")); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "number": [-99, -99, -99], + foo: [1, 2, 3], + number: [-99, -99, -99], }); expect(actual).toFrameEqual(expected); }); test("bigint", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - }).select(col("foo"), lit(999283899189222n).as("bigint")); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .select(col("foo"), lit(999283899189222n).as("bigint")); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bigint": [999283899189222n, 999283899189222n, 999283899189222n], + foo: [1, 2, 3], + bigint: [999283899189222n, 999283899189222n, 999283899189222n], }); expect(actual).toFrameEqual(expected); }); test("series", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - }).select(col("foo"), lit(pl.Series(["one", "two", "three"])).as("series:string")); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .select( + col("foo"), + lit(pl.Series(["one", "two", "three"])).as("series:string"), + ); const expected = pl.DataFrame({ - "foo": [1, 2, 3], + foo: [1, 2, 3], "series:string": ["one", "two", "three"], }); expect(actual).toFrameEqual(expected); }); test("null", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [4, 5, 6], - }).select(col("foo"), lit(null).as("nulls")); + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [4, 5, 6], + }) + .select(col("foo"), lit(null).as("nulls")); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "nulls": [null, null, null], + foo: [1, 2, 3], + nulls: [null, null, null], }); expect(actual).toFrameEqual(expected); }); }); test("arange:positional", () => { const df = pl.DataFrame({ - "foo": [1, 1, 1], + foo: [1, 1, 1], }); - const expected = pl.DataFrame({"foo": [1, 1]}); + const expected = pl.DataFrame({ foo: [1, 1] }); const actual = df.filter(pl.col("foo").gtEq(pl.arange(0, 3))); expect(actual).toFrameEqual(expected); }); test("arange:named", () => { const df = pl.DataFrame({ - "foo": [1, 1, 1], + foo: [1, 1, 1], }); - const expected = pl.DataFrame({"foo": [1, 1]}); - const actual = df.filter(pl.col("foo").gtEq(pl.arange({low: 0, high: 3}))); + const expected = pl.DataFrame({ foo: [1, 1] }); + const actual = df.filter( + pl.col("foo").gtEq(pl.arange({ low: 0, high: 3 })), + ); expect(actual).toFrameEqual(expected); }); test("arange:eager", () => { const df = pl.DataFrame({ - "foo": [1, 1, 1], + foo: [1, 1, 1], }); - const expected = pl.DataFrame({"foo": [1, 1]}); - const actual = df.filter(pl.col("foo").gtEq(pl.arange({low: 0, high: 3, eager: true}))); + const expected = pl.DataFrame({ foo: [1, 1] }); + const actual = df.filter( + pl.col("foo").gtEq(pl.arange({ low: 0, high: 3, eager: true })), + ); expect(actual).toFrameEqual(expected); }); test.skip("argSortBy", () => { - const actual = _df().select(pl.argSortBy(["int_nulls", "floats"], [false, true])) + const actual = _df() + .select(pl.argSortBy(["int_nulls", "floats"], [false, true])) .getColumn("int_nulls"); }); test("avg", () => { - const df = pl.DataFrame({"foo": [4, 5, 6, 4, 5, 6]}); + const df = pl.DataFrame({ foo: [4, 5, 6, 4, 5, 6] }); const expected = pl.select(lit(5).as("foo")); const actual = df.select(pl.avg("foo")); @@ -156,7 +177,9 @@ describe("lazy functions", () => { const s1 = pl.Series("b", ["d", "e", "f"]); const expected = pl.Series("concat", ["a,d", "b,e", "c,f"]); const df = pl.DataFrame([s0, s1]); - const actual = df.select(pl.concatString(["a", "b"]).as("concat")).getColumn("concat"); + const actual = df + .select(pl.concatString(["a", "b"]).as("concat")) + .getColumn("concat"); expect(actual).toSeriesEqual(expected); }); test("concatString:sep", () => { @@ -164,7 +187,9 @@ describe("lazy functions", () => { const s1 = pl.Series("b", ["d", "e", "f"]); const expected = pl.Series("concat", ["a=d", "b=e", "c=f"]); const df = pl.DataFrame([s0, s1]); - const actual = df.select(pl.concatString(["a", "b"], "=").as("concat")).getColumn("concat"); + const actual = df + .select(pl.concatString(["a", "b"], "=").as("concat")) + .getColumn("concat"); expect(actual).toSeriesEqual(expected); }); test("concatString:named", () => { @@ -172,7 +197,9 @@ describe("lazy functions", () => { const s1 = pl.Series("b", ["d", "e", "f"]); const expected = pl.Series("concat", ["a=d", "b=e", "c=f"]); const df = pl.DataFrame([s0, s1]); - const actual = df.select(pl.concatString({exprs: ["a", "b"], sep: "="}).as("concat")).getColumn("concat"); + const actual = df + .select(pl.concatString({ exprs: ["a", "b"], sep: "=" }).as("concat")) + .getColumn("concat"); expect(actual).toSeriesEqual(expected); }); test("count:series", () => { @@ -184,11 +211,7 @@ describe("lazy functions", () => { test("count:column", () => { const s0 = pl.Series("a", [1, 2, 3, 4, 5]).cast(pl.Int32); const s1 = pl.Series("b", [11, 22, 33, 44, 55]).cast(pl.Int32); - const expected = pl.select( - lit(5) - .cast(pl.Int32) - .as("a") - ); + const expected = pl.select(lit(5).cast(pl.Int32).as("a")); const actual = pl.DataFrame([s0, s1]).select(pl.count("a")); expect(actual).toFrameEqual(expected); }); @@ -227,9 +250,7 @@ describe("lazy functions", () => { pl.Series("B", [5, 4, 3, 2, 1]), pl.Series("C", ["a", "b", "c", "d", "e"]), ]); - const expected = pl.DataFrame([ - pl.Series("C", ["a", "b", "c", "d", "e"]), - ]); + const expected = pl.DataFrame([pl.Series("C", ["a", "b", "c", "d", "e"])]); const actual = df.select(pl.exclude("A", "B")); expect(actual).toFrameEqual(expected); }); @@ -239,8 +260,7 @@ describe("lazy functions", () => { expect(actual).toStrictEqual(1); }); test("first:df", () => { - const actual = _df().select(pl.first("bools")) - .row(0)[0]; + const actual = _df().select(pl.first("bools")).row(0)[0]; expect(actual).toStrictEqual(false); }); test("first:invalid", () => { @@ -249,26 +269,33 @@ describe("lazy functions", () => { expect(fn).toThrow(); }); test("format:tag", () => { - const df = pl.DataFrame({ - "a": ["a", "b", "c"], - "b": [1, 2, 3], + const df = pl.DataFrame({ + a: ["a", "b", "c"], + b: [1, 2, 3], }); - const expected = pl.DataFrame({"eq": ["a=a;b=1.0", "a=b;b=2.0", "a=c;b=3.0"]}); - const actual = df.select(pl.format`${lit("a")}=${col("a")};b=${col("b")}`.as("eq")); + const expected = pl.DataFrame({ + eq: ["a=a;b=1.0", "a=b;b=2.0", "a=c;b=3.0"], + }); + const actual = df.select( + pl.format`${lit("a")}=${col("a")};b=${col("b")}`.as("eq"), + ); expect(actual).toFrameEqual(expected); }); test("format:pattern", () => { - const df = pl.DataFrame({ - "a": ["a", "b", "c"], - "b": [1, 2, 3], + const df = pl.DataFrame({ + a: ["a", "b", "c"], + b: [1, 2, 3], }); const fmt = pl.format("{}={};b={}", lit("a"), col("a"), col("b")).as("eq"); - const expected = pl.DataFrame({"eq": ["a=a;b=1.0", "a=b;b=2.0", "a=c;b=3.0"]}); + const expected = pl.DataFrame({ + eq: ["a=a;b=1.0", "a=b;b=2.0", "a=c;b=3.0"], + }); const actual = df.select(fmt); expect(actual).toFrameEqual(expected); }); test("format:invalid", () => { - const fn = () => pl.format("{}{}={};b={}", lit("a"), col("a"), col("b")).as("eq"); + const fn = () => + pl.format("{}{}={};b={}", lit("a"), col("a"), col("b")).as("eq"); expect(fn).toThrow(); }); test("head:series", () => { @@ -278,12 +305,12 @@ describe("lazy functions", () => { }); test("head:df", () => { const df = pl.DataFrame({ - "a": [1, 2, 5], - "b": ["foo", "bar", "baz"] + a: [1, 2, 5], + b: ["foo", "bar", "baz"], }); const expected = pl.DataFrame({ - "a": [1], - "b": ["foo"] + a: [1], + b: ["foo"], }); const actual = df.select(pl.head("*", 1)); @@ -291,12 +318,12 @@ describe("lazy functions", () => { }); test("head:expr", () => { const df = pl.DataFrame({ - "a": [1, 2, 5], - "b": ["foo", "bar", "baz"] + a: [1, 2, 5], + b: ["foo", "bar", "baz"], }); const expected = pl.DataFrame({ - "a": [1], - "b": ["foo"] + a: [1], + b: ["foo"], }); const actual = df.select(pl.head(col("*"), 1)); @@ -308,16 +335,16 @@ describe("lazy functions", () => { }); test("last:string", () => { const df = pl.DataFrame({ - "a": [1, 2, 5], - "b": ["foo", "bar", "baz"] + a: [1, 2, 5], + b: ["foo", "bar", "baz"], }); const actual = df.select(pl.last("b")); expect(actual).toFrameEqual(pl.select(lit("baz").as("b"))); }); test("last:col", () => { const df = pl.DataFrame({ - "a": [1, 2, 5], - "b": ["foo", "bar", "baz"] + a: [1, 2, 5], + b: ["foo", "bar", "baz"], }); const actual = df.select(pl.last(col("b"))); expect(actual).toFrameEqual(pl.select(lit("baz").as("b"))); @@ -328,19 +355,17 @@ describe("lazy functions", () => { }); test("list", () => { const df = pl.DataFrame({ - "a": [1, 2, 3], - "b": ["a", "b", "c"], + a: [1, 2, 3], + b: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "a": [1, 2, 3], - "b": [["a"], ["b"], ["c"]] + a: [1, 2, 3], + b: [["a"], ["b"], ["c"]], }); - const actual = df.groupBy("a") - .agg( - pl.list("b") - .keepName() - ) - .sort({by:"a"}); + const actual = df + .groupBy("a") + .agg(pl.list("b").keepName()) + .sort({ by: "a" }); expect(actual).toFrameEqual(expected); }); test("mean:series", () => { @@ -348,12 +373,12 @@ describe("lazy functions", () => { expect(actual).toStrictEqual(4); }); test("mean:string", () => { - const df = pl.DataFrame({"a": [2, 2, 8]}); + const df = pl.DataFrame({ a: [2, 2, 8] }); const actual = df.select(pl.mean("a")).getColumn("a")[0]; expect(actual).toStrictEqual(4); }); test("mean:col", () => { - const df = pl.DataFrame({"a": [2, 2, 8]}); + const df = pl.DataFrame({ a: [2, 2, 8] }); const actual = df.select(pl.mean(col("a"))).getColumn("a")[0]; expect(actual).toStrictEqual(4); }); @@ -362,12 +387,12 @@ describe("lazy functions", () => { expect(actual).toStrictEqual(2); }); test("median:string", () => { - const df = pl.DataFrame({"a": [2, 2, 8]}); + const df = pl.DataFrame({ a: [2, 2, 8] }); const actual = df.select(pl.median("a")).getColumn("a")[0]; expect(actual).toStrictEqual(2); }); test("median:col", () => { - const df = pl.DataFrame({"a": [2, 2, 8]}); + const df = pl.DataFrame({ a: [2, 2, 8] }); const actual = df.select(pl.median(col("a"))).getColumn("a")[0]; expect(actual).toStrictEqual(2); }); @@ -376,12 +401,12 @@ describe("lazy functions", () => { expect(actual).toStrictEqual(2); }); test("nUnique:string", () => { - const df = pl.DataFrame({"a": [2, 2, 8]}); + const df = pl.DataFrame({ a: [2, 2, 8] }); const actual = df.select(pl.nUnique("a")).getColumn("a")[0]; expect(actual).toStrictEqual(2); }); test("nUnique:col", () => { - const df = pl.DataFrame({"a": [2, 2, 8]}); + const df = pl.DataFrame({ a: [2, 2, 8] }); const actual = df.select(pl.nUnique(col("a"))).getColumn("a")[0]; expect(actual).toStrictEqual(2); }); @@ -390,10 +415,7 @@ describe("lazy functions", () => { pl.Series("A", [1, 2, 3, 4]), pl.Series("B", [2, 4, 6, 8]), ]); - const actual = df.select( - pl.pearsonCorr("A", "B").round(1) - ) - .row(0)[0]; + const actual = df.select(pl.pearsonCorr("A", "B").round(1)).row(0)[0]; expect(actual).toStrictEqual(1); }); test("quantile:series", () => { @@ -402,12 +424,12 @@ describe("lazy functions", () => { expect(actual).toStrictEqual(2); }); test("quantile:string", () => { - const df = pl.DataFrame({"a": [1, 2, 3]}); + const df = pl.DataFrame({ a: [1, 2, 3] }); const actual = df.select(pl.quantile("a", 0.5)).getColumn("a")[0]; expect(actual).toStrictEqual(2); }); test("quantile:col", () => { - const df = pl.DataFrame({"a": [1, 2, 3]}); + const df = pl.DataFrame({ a: [1, 2, 3] }); const actual = df.select(pl.quantile(col("a"), 0.5)).getColumn("a")[0]; expect(actual).toStrictEqual(2); }); @@ -416,10 +438,7 @@ describe("lazy functions", () => { pl.Series("A", [1, 2, 3, 4]), pl.Series("B", [2, 4, 6, 8]), ]); - const actual = df.select( - pl.spearmanRankCorr("A", "B").round(1) - ) - .row(0)[0]; + const actual = df.select(pl.spearmanRankCorr("A", "B").round(1)).row(0)[0]; expect(actual).toStrictEqual(1); }); test("tail:series", () => { @@ -429,23 +448,31 @@ describe("lazy functions", () => { expect(actual).toSeriesEqual(expected); }); test("tail:string", () => { - const df = pl.DataFrame({"a": [1, 2, 3]}); + const df = pl.DataFrame({ a: [1, 2, 3] }); const expected = pl.Series("a", [2, 3]); const actual = df.select(pl.tail("a", 2)).getColumn("a"); expect(actual).toSeriesEqual(expected); }); test("tail:col", () => { - const df = pl.DataFrame({"a": [1, 2, 3]}); + const df = pl.DataFrame({ a: [1, 2, 3] }); const expected = pl.Series("a", [2, 3]); const actual = df.select(pl.tail(col("a"), 2)).getColumn("a"); expect(actual).toSeriesEqual(expected); }); test("element", () => { const df = pl.DataFrame({ - a: [[1, 2], [3, 4], [5, 6]], + a: [ + [1, 2], + [3, 4], + [5, 6], + ], }); const expected = pl.DataFrame({ - a: [[2, 4], [6, 8], [10, 12]], + a: [ + [2, 4], + [6, 8], + [10, 12], + ], }); const actual = df.select(pl.col("a").lst.eval(pl.element().mul(2))); expect(actual).toFrameEqual(expected); diff --git a/__tests__/lazyframe.test.ts b/__tests__/lazyframe.test.ts index 84430eabe..1163f86ad 100644 --- a/__tests__/lazyframe.test.ts +++ b/__tests__/lazyframe.test.ts @@ -2,710 +2,793 @@ import pl from "@polars"; describe("lazyframe", () => { test("columns", () => { - const df = pl.DataFrame({ - "foo": [1, 2], - "bar": ["a", "b"] - }).lazy(); + const df = pl + .DataFrame({ + foo: [1, 2], + bar: ["a", "b"], + }) + .lazy(); const actual = df.columns; expect(actual).toEqual(["foo", "bar"]); }); test("collectSync", () => { const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": ["a", "b"] + foo: [1, 2], + bar: ["a", "b"], }); const actual = expected.lazy().collectSync(); expect(actual).toFrameEqual(expected); }); test("collect", async () => { const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": ["a", "b"] + foo: [1, 2], + bar: ["a", "b"], }); const actual = await expected.lazy().collect(); expect(actual).toFrameEqual(expected); }); test("describeOptimizedPlan", () => { - const df = pl.DataFrame({ - "foo": [1, 2], - "bar": ["a", "b"] - }).lazy(); + const df = pl + .DataFrame({ + foo: [1, 2], + bar: ["a", "b"], + }) + .lazy(); const actual = df.describeOptimizedPlan().replace(/\s+/g, " "); expect(actual).toEqual( - ` DF ["foo", "bar"]; PROJECT */2 COLUMNS; SELECTION: "None" `); + ` DF ["foo", "bar"]; PROJECT */2 COLUMNS; SELECTION: "None" `, + ); }); test("drop", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], - "apple": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + apple: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const actual = df.lazy() - .drop("apple") - .collectSync(); + const actual = df.lazy().drop("apple").collectSync(); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("drop:array", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], - "apple": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + apple: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], }); - const actual = df.lazy() - .drop(["apple", "ham"]) - .collectSync(); + const actual = df.lazy().drop(["apple", "ham"]).collectSync(); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("drop:rest", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], - "apple": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], + apple: ["a", "b", "c"], }); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], }); - const actual = df.lazy() - .drop("apple", "ham") - .collectSync(); + const actual = df.lazy().drop("apple", "ham").collectSync(); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("unique", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 2, 3], - "bar": [1, 2, 2, 4], - "ham": ["a", "d", "d", "c"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 2, 3], + bar: [1, 2, 2, 4], + ham: ["a", "d", "d", "c"], + }) + .lazy() .unique() .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [1, 2, 4], - "ham": ["a", "d", "c"], + foo: [1, 2, 3], + bar: [1, 2, 4], + ham: ["a", "d", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("unique:subset", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 2, 2], - "bar": [1, 2, 2, 2], - "ham": ["a", "b", "c", "c"], - }).lazy() - .unique({subset: ["foo", "ham"]}) + const actual = pl + .DataFrame({ + foo: [1, 2, 2, 2], + bar: [1, 2, 2, 2], + ham: ["a", "b", "c", "c"], + }) + .lazy() + .unique({ subset: ["foo", "ham"] }) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 2], - "bar": [1, 2, 2], - "ham": ["a", "b", "c"], + foo: [1, 2, 2], + bar: [1, 2, 2], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); // run this test 100 times to make sure it is deterministic. test("unique:maintainOrder", () => { - Array.from({length: 100}).forEach(() => { - const actual = pl.DataFrame({ - "foo": [0, 1, 2, 2, 2], - "bar": [0, 1, 2, 2, 2], - "ham": ["0", "a", "b", "b", "b"], - }).lazy() - .unique({maintainOrder: true}) + Array.from({ length: 100 }).forEach(() => { + const actual = pl + .DataFrame({ + foo: [0, 1, 2, 2, 2], + bar: [0, 1, 2, 2, 2], + ham: ["0", "a", "b", "b", "b"], + }) + .lazy() + .unique({ maintainOrder: true }) .collectSync(); const expected = pl.DataFrame({ - "foo": [0, 1, 2], - "bar": [0, 1, 2], - "ham": ["0", "a", "b"], + foo: [0, 1, 2], + bar: [0, 1, 2], + ham: ["0", "a", "b"], }); expect(actual).toFrameEqual(expected); }); }); // run this test 100 times to make sure it is deterministic. test("unique:maintainOrder:single subset", () => { - Array.from({length: 100}).forEach(() => { - const actual = pl.DataFrame({ - "foo": [0, 1, 2, 2, 2], - "bar": [0, 1, 2, 2, 2], - "ham": ["0", "a", "b", "c", "d"], - }).lazy() - .unique({maintainOrder: true, subset: "foo"}) + Array.from({ length: 100 }).forEach(() => { + const actual = pl + .DataFrame({ + foo: [0, 1, 2, 2, 2], + bar: [0, 1, 2, 2, 2], + ham: ["0", "a", "b", "c", "d"], + }) + .lazy() + .unique({ maintainOrder: true, subset: "foo" }) .collectSync(); const expected = pl.DataFrame({ - "foo": [0, 1, 2], - "bar": [0, 1, 2], - "ham": ["0", "a", "b"], + foo: [0, 1, 2], + bar: [0, 1, 2], + ham: ["0", "a", "b"], }); expect(actual).toFrameEqual(expected); }); }); // run this test 100 times to make sure it is deterministic. test("unique:maintainOrder:multi subset", () => { - Array.from({length: 100}).forEach(() => { - const actual = pl.DataFrame({ - "foo": [0, 1, 2, 2, 2], - "bar": [0, 1, 2, 2, 2], - "ham": ["0", "a", "b", "c", "c"], - }).lazy() - .unique({maintainOrder: true, subset: ["foo", "ham"]}) + Array.from({ length: 100 }).forEach(() => { + const actual = pl + .DataFrame({ + foo: [0, 1, 2, 2, 2], + bar: [0, 1, 2, 2, 2], + ham: ["0", "a", "b", "c", "c"], + }) + .lazy() + .unique({ maintainOrder: true, subset: ["foo", "ham"] }) .collectSync(); const expected = pl.DataFrame({ - "foo": [0, 1, 2, 2], - "bar": [0, 1, 2, 2], - "ham": ["0", "a", "b", "c"], + foo: [0, 1, 2, 2], + bar: [0, 1, 2, 2], + ham: ["0", "a", "b", "c"], }); expect(actual).toFrameEqual(expected); }); }); test("dropNulls", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .lazy() .dropNulls() .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"], + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("dropNulls:array", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, null, 8.0], - "ham": ["a", "d", "b", "c"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, null, 8.0], + ham: ["a", "d", "b", "c"], + }) + .lazy() .dropNulls(["foo"]) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, null, 8.0], - "ham": ["a", "b", "c"], + foo: [1, 2, 3], + bar: [6.0, null, 8.0], + ham: ["a", "b", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("dropNulls:rest", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, null, 8.0], - "ham": ["a", "d", "b", "c"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, null, 8.0], + ham: ["a", "d", "b", "c"], + }) + .lazy() .dropNulls("foo", "bar") .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 3], - "bar": [6.0, 8.0], - "ham": ["a", "c"], + foo: [1, 3], + bar: [6.0, 8.0], + ham: ["a", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("explode", () => { - const actual = pl.DataFrame({ - "letters": ["c", "a"], - "list_1": [[1, 2], [1, 3]], - }).lazy() + const actual = pl + .DataFrame({ + letters: ["c", "a"], + list_1: [ + [1, 2], + [1, 3], + ], + }) + .lazy() .explode("list_1") .collectSync(); const expected = pl.DataFrame({ - "letters": ["c", "c", "a", "a"], - "list_1": [1, 2, 1, 3], + letters: ["c", "c", "a", "a"], + list_1: [1, 2, 1, 3], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("fetch", async () => { const df = pl.DataFrame({ - "foo": [1, 2], - "bar": ["a", "b"] + foo: [1, 2], + bar: ["a", "b"], }); const expected = pl.DataFrame({ - "foo": [1], - "bar": ["a"] + foo: [1], + bar: ["a"], }); - const actual = await df - .lazy() - .select("*") - .fetch(1); + const actual = await df.lazy().select("*").fetch(1); expect(actual).toFrameEqual(expected); }); test("fetchSync", () => { const df = pl.DataFrame({ - "foo": [1, 2], - "bar": ["a", "b"] + foo: [1, 2], + bar: ["a", "b"], }); const expected = pl.DataFrame({ - "foo": [1], - "bar": ["a"] + foo: [1], + bar: ["a"], }); - const actual = df - .lazy() - .select("*") - .fetchSync(1); + const actual = df.lazy().select("*").fetchSync(1); expect(actual).toFrameEqual(expected); }); test("fillNull:zero", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .lazy() .fillNull(0) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 0, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], + foo: [1, 0, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("fillNull:expr", () => { - const actual = pl.DataFrame({ - "foo": [1, null, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, null, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], + }) + .lazy() .fillNull(pl.lit(1)) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 1, 2, 3], - "bar": [6.0, .5, 7.0, 8.0], - "ham": ["a", "d", "b", "c"], + foo: [1, 1, 2, 3], + bar: [6.0, 0.5, 7.0, 8.0], + ham: ["a", "d", "b", "c"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("filter", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .filter(pl.col("foo").eq(2)) .collectSync(); const expected = pl.DataFrame({ - "foo": [2, 2], - "bar": [2, 3] + foo: [2, 2], + bar: [2, 3], }); expect(actual).toFrameEqual(expected); }); test("filter:lit", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .filter(pl.col("foo").eq(pl.lit(2))) .collectSync(); const expected = pl.DataFrame({ - "foo": [2, 2], - "bar": [2, 3] + foo: [2, 2], + bar: [2, 3], }); expect(actual).toFrameEqual(expected); }); - describe("groupby", () => { }); + describe("groupby", () => {}); test("head", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .lazy() .head(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [1], - "ham": ["a"] + foo: [1], + ham: ["a"], }); expect(actual).toFrameEqual(expected); }); describe("join", () => { test("on", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"] - }).lazy(); - const actual = df.lazy().join(otherDF, {on: "ham"}) - .collectSync(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + }) + .lazy(); + const actual = df.lazy().join(otherDF, { on: "ham" }).collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": [6.0, 7.0], - "ham": ["a", "b"], - "apple": ["x", "y"], + foo: [1, 2], + bar: [6.0, 7.0], + ham: ["a", "b"], + apple: ["x", "y"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("on:multiple-columns", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo: [1, 10, 11], + }) + .lazy(); - const actual = df.lazy().join(otherDF, {on: ["ham", "foo"]}) + const actual = df + .lazy() + .join(otherDF, { on: ["ham", "foo"] }) .collectSync(); const expected = pl.DataFrame({ - "foo": [1], - "bar": [6.0], - "ham": ["a"], - "apple": ["x"], + foo: [1], + bar: [6.0], + ham: ["a"], + apple: ["x"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("on:left&right", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }) + .lazy(); - const actual = df.lazy().join(otherDF, { - leftOn: ["foo_left", "ham"], - rightOn: ["foo_right", "ham"] - }) + const actual = df + .lazy() + .join(otherDF, { + leftOn: ["foo_left", "ham"], + rightOn: ["foo_right", "ham"], + }) .collectSync(); const expected = pl.DataFrame({ - "foo_left": [1], - "bar": [6.0], - "ham": ["a"], - "apple": ["x"], + foo_left: [1], + bar: [6.0], + ham: ["a"], + apple: ["x"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("on:left&right", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }) + .lazy(); - const actual = df.lazy().join(otherDF, { - leftOn: ["foo_left", "ham"], - rightOn: ["foo_right", "ham"] - }) + const actual = df + .lazy() + .join(otherDF, { + leftOn: ["foo_left", "ham"], + rightOn: ["foo_right", "ham"], + }) .collectSync(); const expected = pl.DataFrame({ - "foo_left": [1], - "bar": [6.0], - "ham": ["a"], - "apple": ["x"], + foo_left: [1], + bar: [6.0], + ham: ["a"], + apple: ["x"], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("on throws error if only 'leftOn' is specified", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }) + .lazy(); - const f = () => df.lazy().join(otherDF, { - leftOn: ["foo_left", "ham"], - } as any); + const f = () => + df.lazy().join(otherDF, { + leftOn: ["foo_left", "ham"], + } as any); expect(f).toThrow(TypeError); }); test("on throws error if only 'rightOn' is specified", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }) + .lazy(); - const f = () => df.lazy().join(otherDF, { - rightOn: ["foo_right", "ham"], - } as any) - .collectSync(); + const f = () => + df + .lazy() + .join(otherDF, { + rightOn: ["foo_right", "ham"], + } as any) + .collectSync(); expect(f).toThrow(TypeError); }); test("on takes precedence over left&right", () => { const df = pl.DataFrame({ - "foo_left": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo_left: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo_right": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo_right: [1, 10, 11], + }) + .lazy(); - const actual = df.lazy().join(otherDF, { - on: "ham", - leftOn: ["foo_left", "ham"], - rightOn: ["foo_right", "ham"], - } as any) + const actual = df + .lazy() + .join(otherDF, { + on: "ham", + leftOn: ["foo_left", "ham"], + rightOn: ["foo_right", "ham"], + } as any) .collectSync(); const expected = pl.DataFrame({ - "foo_left": [1, 2], - "bar": [6.0, 7.0], - "ham": ["a", "b"], - "apple": ["x", "y"], - "foo_right": [1, 10], + foo_left: [1, 2], + bar: [6.0, 7.0], + ham: ["a", "b"], + apple: ["x", "y"], + foo_right: [1, 10], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("how:left", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo: [1, 10, 11], + }) + .lazy(); - const actual = df.lazy().join(otherDF, { - on: "ham", - how: "left" - }) + const actual = df + .lazy() + .join(otherDF, { + on: "ham", + how: "left", + }) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], - "apple": ["x", "y", null], - "fooright": [1, 10, null], + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + apple: ["x", "y", null], + fooright: [1, 10, null], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("how:outer", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y"], - "ham": ["a", "d"], - "foo": [1, 10], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y"], + ham: ["a", "d"], + foo: [1, 10], + }) + .lazy(); - const actual = df.lazy().join(otherDF, { - on: "ham", - how: "outer" - }) + const actual = df + .lazy() + .join(otherDF, { + on: "ham", + how: "outer", + }) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3, null], - "bar": [6, 7, 8, null], - "ham": ["a", "b", "c", "d"], - "apple": ["x", null, null, "y"], - "fooright": [1, null, null, 10], + foo: [1, 2, 3, null], + bar: [6, 7, 8, null], + ham: ["a", "b", "c", "d"], + apple: ["x", null, null, "y"], + fooright: [1, null, null, 10], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); test("suffix", () => { const df = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6.0, 7.0, 8.0], - "ham": ["a", "b", "c"] + foo: [1, 2, 3], + bar: [6.0, 7.0, 8.0], + ham: ["a", "b", "c"], }); - const otherDF = pl.DataFrame({ - "apple": ["x", "y", "z"], - "ham": ["a", "b", "d"], - "foo": [1, 10, 11], - }).lazy(); + const otherDF = pl + .DataFrame({ + apple: ["x", "y", "z"], + ham: ["a", "b", "d"], + foo: [1, 10, 11], + }) + .lazy(); - const actual = df.lazy().join(otherDF, { - on: "ham", - how: "left", - suffix: "_other" - }) + const actual = df + .lazy() + .join(otherDF, { + on: "ham", + how: "left", + suffix: "_other", + }) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"], - "apple": ["x", "y", null], - "foo_other": [1, 10, null], + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + apple: ["x", "y", null], + foo_other: [1, 10, null], }); expect(actual).toFrameEqualIgnoringOrder(expected); }); }); test("last", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .lazy() .last() .collectSync(); const expected = pl.DataFrame({ - "foo": [3], - "ham": ["c"] + foo: [3], + ham: ["c"], }); expect(actual).toFrameEqual(expected); }); test("limit", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .lazy() .limit(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [1], - "ham": ["a"] + foo: [1], + ham: ["a"], }); expect(actual).toFrameEqual(expected); }); test("max", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 11] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 11], + }) + .lazy() .max() .collectSync(); const expected = pl.DataFrame({ - "foo": [4], - "bar": [11] + foo: [4], + bar: [11], }); expect(actual).toFrameEqual(expected); }); test("mean", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .mean() .collectSync(); const expected = pl.DataFrame({ - "foo": [2.75], - "bar": [1.75] + foo: [2.75], + bar: [1.75], }); expect(actual).toFrameEqual(expected); }); test("median", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .median() .collectSync(); const expected = pl.DataFrame({ - "foo": [2.5], - "bar": [1.5] + foo: [2.5], + bar: [1.5], }); expect(actual).toFrameEqual(expected); }); test("min", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 11] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 11], + }) + .lazy() .min() .collectSync(); const expected = pl.DataFrame({ - "foo": [2], - "bar": [1] + foo: [2], + bar: [1], }); expect(actual).toFrameEqual(expected); }); test("quantile", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .lazy() .quantile(0.5) .collectSync(); expect(actual.row(0)).toEqual([2, 7, null]); }); test("rename", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "bar": [6, 7, 8], - "ham": ["a", "b", "c"] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + bar: [6, 7, 8], + ham: ["a", "b", "c"], + }) + .lazy() .rename({ - "foo": "foo_new", - "bar": "bar_new", - "ham": "ham_new" + foo: "foo_new", + bar: "bar_new", + ham: "ham_new", }) .collectSync(); expect(actual.columns).toEqual(["foo_new", "bar_new", "ham_new"]); }); test("reverse", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .reverse() .collectSync(); const expected = pl.DataFrame({ - "foo": [4, 3, 2, 2], - "bar": [1, 1, 3, 2] + foo: [4, 3, 2, 2], + bar: [1, 1, 3, 2], }); expect(actual).toFrameEqual(expected); }); test("tail", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3], - "ham": ["a", "b", "c"] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3], + ham: ["a", "b", "c"], + }) + .lazy() .tail(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [3], - "ham": ["c"] + foo: [3], + ham: ["c"], }); expect(actual).toFrameEqual(expected); }); test("select:single", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .lazy() .select("ham") .collectSync(); const ham = pl.Series("ham", ["a", "b", "c", null]); @@ -714,11 +797,13 @@ describe("lazyframe", () => { expect(actual.getColumn("ham")).toSeriesEqual(ham); }); test("select:strings", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .lazy() .select("ham", "foo") .collectSync(); const foo = pl.Series("foo", [1, 2, 3, 1]); @@ -728,11 +813,13 @@ describe("lazyframe", () => { expect(actual.getColumn("ham")).toSeriesEqual(ham); }); test("select:expr", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - "ham": ["a", "b", "c", null] - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + ham: ["a", "b", "c", null], + }) + .lazy() .select(pl.col("foo"), "ham") .collectSync(); const foo = pl.Series("foo", [1, 2, 3, 1]); @@ -742,189 +829,214 @@ describe("lazyframe", () => { expect(actual.getColumn("ham")).toSeriesEqual(ham); }); test("shift:pos", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() .shift(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [null, 1, 2, 3], - "bar": [null, 6, 7, 8], + foo: [null, 1, 2, 3], + bar: [null, 6, 7, 8], }); expect(actual).toFrameEqual(expected); }); test("shift:neg", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() .shift(-1) .collectSync(); const expected = pl.DataFrame({ - "foo": [2, 3, 1, null], - "bar": [7, 8, 1, null], + foo: [2, 3, 1, null], + bar: [7, 8, 1, null], }); expect(actual).toFrameEqual(expected); }); test("shiftAndFill:positional", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() .shiftAndFill(-1, 99) .collectSync(); const expected = pl.DataFrame({ - "foo": [2, 3, 1, 99], - "bar": [7, 8, 1, 99], + foo: [2, 3, 1, 99], + bar: [7, 8, 1, 99], }); expect(actual).toFrameEqual(expected); }); test("shiftAndFill:named", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() - .shiftAndFill({periods: -1, fillValue: 99}) + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() + .shiftAndFill({ periods: -1, fillValue: 99 }) .collectSync(); const expected = pl.DataFrame({ - "foo": [2, 3, 1, 99], - "bar": [7, 8, 1, 99], + foo: [2, 3, 1, 99], + bar: [7, 8, 1, 99], }); expect(actual).toFrameEqual(expected); }); test("shiftAndFill:expr", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() - .shiftAndFill({periods: -1, fillValue: pl.lit(99)}) + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() + .shiftAndFill({ periods: -1, fillValue: pl.lit(99) }) .collectSync(); const expected = pl.DataFrame({ - "foo": [2, 3, 1, 99], - "bar": [7, 8, 1, 99], + foo: [2, 3, 1, 99], + bar: [7, 8, 1, 99], }); expect(actual).toFrameEqual(expected); }); test("slice:positional", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() .slice(0, 2) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": [6, 7], + foo: [1, 2], + bar: [6, 7], }); expect(actual).toFrameEqual(expected); }); test("slice:named", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() - .slice({offset: 0, length: 2}) + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() + .slice({ offset: 0, length: 2 }) .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 2], - "bar": [6, 7], + foo: [1, 2], + bar: [6, 7], }); expect(actual).toFrameEqual(expected); }); test("sort:positional", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() .sort("bar") .collectSync(); const expected = pl.DataFrame({ - "foo": [1, 1, 2, 3], - "bar": [1, 6, 7, 8], + foo: [1, 1, 2, 3], + bar: [1, 6, 7, 8], }); expect(actual).toFrameEqual(expected); }); test("sort:named", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, 1], - "bar": [6, 7, 8, 1], - }).lazy() - .sort({by: "bar", reverse: true}) + const actual = pl + .DataFrame({ + foo: [1, 2, 3, 1], + bar: [6, 7, 8, 1], + }) + .lazy() + .sort({ by: "bar", reverse: true }) .collectSync(); const expected = pl.DataFrame({ - "foo": [3, 2, 1, 1], - "bar": [8, 7, 6, 1], + foo: [3, 2, 1, 1], + bar: [8, 7, 6, 1], }); expect(actual).toFrameEqual(expected); }); test("sort:multi-args", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 3, -1], - "bar": [6, 7, 8, 2], - "baz": ["a", "b", "d", "A"], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 3, -1], + bar: [6, 7, 8, 2], + baz: ["a", "b", "d", "A"], + }) + .lazy() .sort({ - by: [ - pl.col("baz"), - pl.col("bar") - ] + by: [pl.col("baz"), pl.col("bar")], }) .collectSync(); const expected = pl.DataFrame({ - "foo": [-1, 1, 2, 3], - "bar": [2, 6, 7, 8], - "baz": ["A", "a", "b", "d"], + foo: [-1, 1, 2, 3], + bar: [2, 6, 7, 8], + baz: ["A", "a", "b", "d"], }); expect(actual).toFrameEqual(expected); }); test("sum", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 11] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 11], + }) + .lazy() .sum() .collectSync(); const expected = pl.DataFrame({ - "foo": [11], - "bar": [17] + foo: [11], + bar: [17], }); expect(actual).toFrameEqual(expected); }); test("std", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .std() .collectSync(); const expected = pl.DataFrame({ - "foo": [0.9574271077563381], - "bar": [0.9574271077563381] + foo: [0.9574271077563381], + bar: [0.9574271077563381], }); expect(actual).toFrameEqual(expected); }); test("var", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .var() .collectSync(); const expected = pl.DataFrame({ - "foo": [0.9166666666666666], - "bar": [0.9166666666666666] + foo: [0.9166666666666666], + bar: [0.9166666666666666], }); expect(actual).toFrameEqual(expected); }); test("withColumn", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .lazy() .withColumn(pl.lit("a").alias("col_a")) .collectSync(); @@ -936,14 +1048,13 @@ describe("lazyframe", () => { expect(actual).toFrameEqualIgnoringOrder(expected); }); test("withColumns", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).lazy() - .withColumns( - pl.lit("a").alias("col_a"), - pl.lit("b").alias("col_b") - ) + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .lazy() + .withColumns(pl.lit("a").alias("col_a"), pl.lit("b").alias("col_b")) .collectSync(); const expected = pl.DataFrame([ pl.Series("foo", [1, 2, 9], pl.Int16), @@ -954,14 +1065,13 @@ describe("lazyframe", () => { expect(actual).toFrameEqualIgnoringOrder(expected); }); test("withColumns:array", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).lazy() - .withColumns([ - pl.lit("a").alias("col_a"), - pl.lit("b").alias("col_b") - ]) + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .lazy() + .withColumns([pl.lit("a").alias("col_a"), pl.lit("b").alias("col_b")]) .collectSync(); const expected = pl.DataFrame([ pl.Series("foo", [1, 2, 9], pl.Int16), @@ -972,10 +1082,12 @@ describe("lazyframe", () => { expect(actual).toFrameEqualIgnoringOrder(expected); }); test("withColumnRenamed", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .lazy() .withColumnRenamed("foo", "apple") .collectSync(); @@ -986,10 +1098,12 @@ describe("lazyframe", () => { expect(actual).toFrameEqual(expected); }); test("withRowCount", () => { - const actual = pl.DataFrame({ - "foo": [1, 2, 9], - "bar": [6, 2, 8], - }).lazy() + const actual = pl + .DataFrame({ + foo: [1, 2, 9], + bar: [6, 2, 8], + }) + .lazy() .withRowCount() .collectSync(); @@ -1001,83 +1115,89 @@ describe("lazyframe", () => { expect(actual).toFrameEqual(expected); }); test("tail", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .tail(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [4], - "bar": [1] + foo: [4], + bar: [1], }); expect(actual).toFrameEqual(expected); }); test("head", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .head(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [2], - "bar": [2] + foo: [2], + bar: [2], }); expect(actual).toFrameEqual(expected); }); test("limit", () => { - const actual = pl.DataFrame({ - "foo": [2, 2, 3, 4], - "bar": [2, 3, 1, 1] - }).lazy() + const actual = pl + .DataFrame({ + foo: [2, 2, 3, 4], + bar: [2, 3, 1, 1], + }) + .lazy() .limit(1) .collectSync(); const expected = pl.DataFrame({ - "foo": [2], - "bar": [2] + foo: [2], + bar: [2], }); expect(actual).toFrameEqual(expected); }); test("str:padStart", () => { - const actual = pl.DataFrame({ - "ham": ["a", "b", "c"] - }).lazy() - .withColumn( - pl.col("ham").str.padStart(3, "-") - ) + const actual = pl + .DataFrame({ + ham: ["a", "b", "c"], + }) + .lazy() + .withColumn(pl.col("ham").str.padStart(3, "-")) .collectSync(); const expected = pl.DataFrame({ - "ham": ["--a", "--b", "--c"] + ham: ["--a", "--b", "--c"], }); expect(actual).toFrameEqual(expected); }); test("str:padEnd", () => { - const actual = pl.DataFrame({ - "ham": ["a", "b", "c"] - }).lazy() - .withColumn( - pl.col("ham").str.padEnd(3, "-") - ) + const actual = pl + .DataFrame({ + ham: ["a", "b", "c"], + }) + .lazy() + .withColumn(pl.col("ham").str.padEnd(3, "-")) .collectSync(); const expected = pl.DataFrame({ - "ham": ["a--", "b--", "c--"] + ham: ["a--", "b--", "c--"], }); expect(actual).toFrameEqual(expected); }); test("str:zFill", () => { - const actual = pl.DataFrame({ - "ham": ["a", "b", "c"] - }).lazy() - .withColumn( - pl.col("ham").str.zFill(3) - ) + const actual = pl + .DataFrame({ + ham: ["a", "b", "c"], + }) + .lazy() + .withColumn(pl.col("ham").str.zFill(3)) .collectSync(); const expected = pl.DataFrame({ - "ham": ["00a", "00b", "00c"] + ham: ["00a", "00b", "00c"], }); expect(actual).toFrameEqual(expected); }); diff --git a/__tests__/serde.test.ts b/__tests__/serde.test.ts index 5734cb57d..7cb596c7e 100644 --- a/__tests__/serde.test.ts +++ b/__tests__/serde.test.ts @@ -38,7 +38,7 @@ describe("serde", () => { test("dataframe:json", () => { const df = pl.DataFrame({ foo: [1, 2], - bar: [2, 3] + bar: [2, 3], }); const buf = df.serialize("json"); const expected = pl.DataFrame.deserialize(buf, "json"); @@ -47,7 +47,7 @@ describe("serde", () => { test("dataframe:bincode", () => { const df = pl.DataFrame({ foo: [1, 2], - bar: [2, 3] + bar: [2, 3], }); const buf = df.serialize("bincode"); const expected = pl.DataFrame.deserialize(buf, "bincode"); @@ -57,7 +57,7 @@ describe("serde", () => { test("dataframe:unsupported", () => { const df = pl.DataFrame({ foo: [1, 2], - bar: [2, 3] + bar: [2, 3], }); const ser = () => df.serialize("yaml" as any); const buf = df.serialize("bincode"); diff --git a/__tests__/series.test.ts b/__tests__/series.test.ts index b204ebec0..c2237004e 100644 --- a/__tests__/series.test.ts +++ b/__tests__/series.test.ts @@ -1,9 +1,8 @@ /* eslint-disable newline-per-chained-call */ import pl from "@polars"; -import {InvalidOperationError} from "../polars/error"; +import { InvalidOperationError } from "../polars/error"; import Chance from "chance"; - describe("from lists", () => { test("bool", () => { const expected = [[true, false], [true], [null], []]; @@ -34,11 +33,8 @@ describe("typedArrays", () => { expect(actual).toEqual(expected); }); test("int8:list", () => { - const int8Arrays = [ - new Int8Array([1, 2, 3]), - new Int8Array([33, 44, 55]), - ]; - const expected = int8Arrays.map(i => [...i]); + const int8Arrays = [new Int8Array([1, 2, 3]), new Int8Array([33, 44, 55])]; + const expected = int8Arrays.map((i) => [...i]); const actual = pl.Series(int8Arrays).toArray(); expect(actual).toEqual(expected); }); @@ -54,7 +50,7 @@ describe("typedArrays", () => { new Int16Array([33, 44, 55]), ]; const actual = pl.Series(int16Arrays).toArray(); - const expected = int16Arrays.map(i => [...i]); + const expected = int16Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); test("int32", () => { @@ -68,7 +64,7 @@ describe("typedArrays", () => { new Int32Array([33, 44, 55]), ]; const actual = pl.Series(int32Arrays).toArray(); - const expected = int32Arrays.map(i => [...i]); + const expected = int32Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); @@ -91,7 +87,7 @@ describe("typedArrays", () => { const actual = pl.Series(int64Arrays).toArray(); const expected = [ [1, 2, 3], - [33, 44, 55] + [33, 44, 55], ]; expect(actual).toEqual(expected); }); @@ -107,7 +103,7 @@ describe("typedArrays", () => { new Uint8Array([33, 44, 55]), ]; const actual = pl.Series(uint8Arrays).toArray(); - const expected = uint8Arrays.map(i => [...i]); + const expected = uint8Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); test("uint16", () => { @@ -122,7 +118,7 @@ describe("typedArrays", () => { new Uint16Array([33, 44, 55]), ]; const actual = pl.Series(uint16Arrays).toArray(); - const expected = uint16Arrays.map(i => [...i]); + const expected = uint16Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); test("uint32", () => { @@ -137,7 +133,7 @@ describe("typedArrays", () => { new Uint32Array([33, 44, 55]), ]; const actual = pl.Series(uint32Arrays).toArray(); - const expected = uint32Arrays.map(i => [...i]); + const expected = uint32Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); test("uint64", () => { @@ -152,7 +148,7 @@ describe("typedArrays", () => { new BigUint64Array([33n, 44n, 55n]), ]; const actual = pl.Series(uint64Arrays).toArray(); - const expected = uint64Arrays.map(i => [...i]); + const expected = uint64Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); test("float32", () => { @@ -167,7 +163,7 @@ describe("typedArrays", () => { new Float32Array([33, 44, 55]), ]; const actual = pl.Series(float32Arrays).toArray(); - const expected = float32Arrays.map(i => [...i]); + const expected = float32Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); test("float64", () => { @@ -182,26 +178,23 @@ describe("typedArrays", () => { new Float64Array([33, 44, 55]), ]; const actual = pl.Series(float64Arrays).toArray(); - const expected = float64Arrays.map(i => [...i]); + const expected = float64Arrays.map((i) => [...i]); expect(actual).toEqual(expected); }); - }); describe("series", () => { const chance = new Chance(); describe("create series", () => { - - it.each` - values | dtype | type - ${["foo", "bar", "baz"]} | ${pl.Utf8} | ${"string"} - ${[1, 2, 3]} | ${pl.Float64} | ${"number"} - ${[1n, 2n, 3n]} | ${pl.UInt64} | ${"bigint"} - ${[true, false]} | ${pl.Bool} | ${"boolean"} - ${[]} | ${pl.Float64} | ${"empty"} + values | dtype | type + ${["foo", "bar", "baz"]} | ${pl.Utf8} | ${"string"} + ${[1, 2, 3]} | ${pl.Float64} | ${"number"} + ${[1n, 2n, 3n]} | ${pl.UInt64} | ${"bigint"} + ${[true, false]} | ${pl.Bool} | ${"boolean"} + ${[]} | ${pl.Float64} | ${"empty"} ${[new Date(Date.now())]} | ${pl.Datetime("ms")} | ${"Date"} - `("defaults to $dtype for \"$type\"", ({ values, dtype}) => { + `('defaults to $dtype for "$type"', ({ values, dtype }) => { const name = chance.string(); const s = pl.Series(name, values); expect(s.name).toStrictEqual(name); @@ -222,12 +215,11 @@ describe("series", () => { expect(s.dtype).toStrictEqual(dtype); }); }); - }); describe("series", () => { const numSeries = () => pl.Series("foo", [1, 2, 3], pl.Int32); const fltSeries = () => pl.Series("float", [1, 2, 3], pl.Float64); - const boolSeries = () => pl.Series("bool", [true, false, false]); + const boolSeries = () => pl.Series("bool", [true, false, false]); const other = () => pl.Series("bar", [3, 4, 5], pl.Int32); const chance = new Chance(); @@ -239,11 +231,11 @@ describe("series", () => { // expect(s).toStrictEqual(actual); // }); it.each` - series | getter - ${numSeries()} | ${"dtype"} - ${numSeries()} | ${"name"} - ${numSeries()} | ${"length"} - `("$# $getter does not error", ({series, getter}) => { + series | getter + ${numSeries()} | ${"dtype"} + ${numSeries()} | ${"name"} + ${numSeries()} | ${"length"} + `("$# $getter does not error", ({ series, getter }) => { try { series[getter]; } catch (err) { @@ -251,172 +243,172 @@ describe("series", () => { } }); it.each` - series | method | args - ${numSeries()} | ${"abs"} | ${[]} - ${numSeries()} | ${"as"} | ${[chance.string()]} - ${numSeries()} | ${"alias"} | ${[chance.string()]} - ${numSeries()} | ${"append"} | ${[other()]} - ${numSeries()} | ${"argMax"} | ${[]} - ${numSeries()} | ${"argMin"} | ${[]} - ${numSeries()} | ${"argSort"} | ${[]} - ${boolSeries()} | ${"argTrue"} | ${[]} - ${numSeries()} | ${"argUnique"} | ${[]} - ${numSeries()} | ${"cast"} | ${[pl.UInt32]} - ${numSeries()} | ${"chunkLengths"} | ${[]} - ${numSeries()} | ${"clone"} | ${[]} - ${numSeries()} | ${"cumMax"} | ${[]} - ${numSeries()} | ${"cumMin"} | ${[]} - ${numSeries()} | ${"cumProd"} | ${[]} - ${numSeries()} | ${"cumSum"} | ${[]} - ${numSeries()} | ${"describe"} | ${[]} - ${numSeries()} | ${"diff"} | ${[]} - ${numSeries()} | ${"diff"} | ${[{n: 1, nullBehavior: "drop"}]} - ${numSeries()} | ${"diff"} | ${[{nullBehavior: "drop"}]} - ${numSeries()} | ${"diff"} | ${[1, "drop"]} - ${numSeries()} | ${"dot"} | ${[other()]} - ${numSeries()} | ${"dropNulls"} | ${[]} - ${numSeries()} | ${"fillNull"} | ${["zero"]} - ${numSeries()} | ${"fillNull"} | ${[{strategy: "zero"}]} - ${numSeries()} | ${"filter"} | ${[boolSeries()]} - ${fltSeries()} | ${"floor"} | ${[]} - ${numSeries()} | ${"hasValidity"} | ${[]} - ${numSeries()} | ${"hash"} | ${[]} - ${numSeries()} | ${"hash"} | ${[{k0: 10}]} - ${numSeries()} | ${"hash"} | ${[{k0: 10, k1: 29}]} - ${numSeries()} | ${"hash"} | ${[{k0: 10, k1: 29, k2: 3}]} - ${numSeries()} | ${"hash"} | ${[{k0: 10, k1: 29, k3: 1, k2: 3}]} - ${numSeries()} | ${"hash"} | ${[1]} - ${numSeries()} | ${"hash"} | ${[1, 2]} - ${numSeries()} | ${"hash"} | ${[1, 2, 3]} - ${numSeries()} | ${"hash"} | ${[1, 2, 3, 4]} - ${numSeries()} | ${"head"} | ${[]} - ${numSeries()} | ${"head"} | ${[1]} - ${numSeries()} | ${"inner"} | ${[]} - ${numSeries()} | ${"interpolate"} | ${[]} - ${numSeries()} | ${"isBoolean"} | ${[]} - ${numSeries()} | ${"isDateTime"} | ${[]} - ${numSeries()} | ${"isDuplicated"} | ${[]} - ${fltSeries()} | ${"isFinite"} | ${[]} - ${numSeries()} | ${"isFirst"} | ${[]} - ${numSeries()} | ${"isFloat"} | ${[]} - ${numSeries()} | ${"isIn"} | ${[other()]} - ${numSeries()} | ${"isIn"} | ${[[1, 2, 3]]} - ${fltSeries()} | ${"isInfinite"} | ${[]} - ${numSeries()} | ${"isNotNull"} | ${[]} - ${numSeries()} | ${"isNull"} | ${[]} - ${numSeries()} | ${"isNumeric"} | ${[]} - ${numSeries()} | ${"isUnique"} | ${[]} - ${numSeries()} | ${"isUtf8"} | ${[]} - ${numSeries()} | ${"kurtosis"} | ${[]} - ${numSeries()} | ${"kurtosis"} | ${[{fisher: true, bias: true}]} - ${numSeries()} | ${"kurtosis"} | ${[{bias: false}]} - ${numSeries()} | ${"kurtosis"} | ${[{fisher: false}]} - ${numSeries()} | ${"kurtosis"} | ${[false, false]} - ${numSeries()} | ${"kurtosis"} | ${[false]} - ${numSeries()} | ${"len"} | ${[]} - ${numSeries()} | ${"limit"} | ${[]} - ${numSeries()} | ${"limit"} | ${[2]} - ${numSeries()} | ${"max"} | ${[]} - ${numSeries()} | ${"mean"} | ${[]} - ${numSeries()} | ${"median"} | ${[]} - ${numSeries()} | ${"min"} | ${[]} - ${numSeries()} | ${"mode"} | ${[]} - ${numSeries()} | ${"nChunks"} | ${[]} - ${numSeries()} | ${"nUnique"} | ${[]} - ${numSeries()} | ${"nullCount"} | ${[]} - ${numSeries()} | ${"peakMax"} | ${[]} - ${numSeries()} | ${"peakMin"} | ${[]} - ${numSeries()} | ${"quantile"} | ${[0.4]} - ${numSeries()} | ${"rank"} | ${[]} - ${numSeries()} | ${"rank"} | ${["average"]} - ${numSeries()} | ${"rechunk"} | ${[]} - ${numSeries()} | ${"rechunk"} | ${[true]} - ${numSeries()} | ${"rename"} | ${["new name"]} - ${numSeries()} | ${"rename"} | ${["new name", true]} - ${numSeries()} | ${"rename"} | ${[{name: "new name"}]} - ${numSeries()} | ${"rename"} | ${[{name: "new name", inPlace: true}]} - ${numSeries()} | ${"rename"} | ${[{name: "new name"}]} - ${numSeries()} | ${"rollingMax"} | ${[{windowSize: 1}]} - ${numSeries()} | ${"rollingMax"} | ${[{windowSize: 1, weights: [.33]}]} - ${numSeries()} | ${"rollingMax"} | ${[{windowSize: 1, weights: [.11], minPeriods: 1}]} - ${numSeries()} | ${"rollingMax"} | ${[{windowSize: 1, weights: [.44], minPeriods: 1, center: false}]} - ${numSeries()} | ${"rollingMax"} | ${[1]} - ${numSeries()} | ${"rollingMax"} | ${[1, [.11]]} - ${numSeries()} | ${"rollingMax"} | ${[1, [.11], 1]} - ${numSeries()} | ${"rollingMax"} | ${[1, [.23], 1, true]} - ${numSeries()} | ${"rollingMean"} | ${[{windowSize: 1}]} - ${numSeries()} | ${"rollingMean"} | ${[{windowSize: 1, weights: [.33]}]} - ${numSeries()} | ${"rollingMean"} | ${[{windowSize: 1, weights: [.11], minPeriods: 1}]} - ${numSeries()} | ${"rollingMean"} | ${[{windowSize: 1, weights: [.44], minPeriods: 1, center: false}]} - ${numSeries()} | ${"rollingMean"} | ${[1]} - ${numSeries()} | ${"rollingMean"} | ${[1, [.11]]} - ${numSeries()} | ${"rollingMean"} | ${[1, [.11], 1]} - ${numSeries()} | ${"rollingMean"} | ${[1, [.23], 1, true]} - ${numSeries()} | ${"rollingMin"} | ${[{windowSize: 1}]} - ${numSeries()} | ${"rollingMin"} | ${[{windowSize: 1, weights: [.33]}]} - ${numSeries()} | ${"rollingMin"} | ${[{windowSize: 1, weights: [.11], minPeriods: 1}]} - ${numSeries()} | ${"rollingMin"} | ${[{windowSize: 1, weights: [.44], minPeriods: 1, center: false}]} - ${numSeries()} | ${"rollingMin"} | ${[1]} - ${numSeries()} | ${"rollingMin"} | ${[1, [.11]]} - ${numSeries()} | ${"rollingMin"} | ${[1, [.11], 1]} - ${numSeries()} | ${"rollingMin"} | ${[1, [.23], 1, true]} - ${numSeries()} | ${"rollingSum"} | ${[{windowSize: 1}]} - ${numSeries()} | ${"rollingSum"} | ${[{windowSize: 1, weights: [.33]}]} - ${numSeries()} | ${"rollingSum"} | ${[{windowSize: 1, weights: [.11], minPeriods: 1}]} - ${numSeries()} | ${"rollingSum"} | ${[{windowSize: 1, weights: [.44], minPeriods: 1, center: false}]} - ${numSeries()} | ${"rollingSum"} | ${[1]} - ${numSeries()} | ${"rollingSum"} | ${[1, [.11]]} - ${numSeries()} | ${"rollingSum"} | ${[1, [.11], 1]} - ${numSeries()} | ${"rollingSum"} | ${[1, [.23], 1, true]} - ${numSeries()} | ${"rollingVar"} | ${[{windowSize: 1}]} - ${numSeries()} | ${"rollingVar"} | ${[{windowSize: 1, weights: [.33]}]} - ${numSeries()} | ${"rollingVar"} | ${[{windowSize: 1, weights: [.11], minPeriods: 1}]} - ${numSeries()} | ${"rollingVar"} | ${[{windowSize: 1, weights: [.44], minPeriods: 1, center: false}]} - ${numSeries()} | ${"rollingVar"} | ${[1]} - ${numSeries()} | ${"rollingVar"} | ${[1, [.11]]} - ${numSeries()} | ${"rollingVar"} | ${[1, [.11], 1]} - ${numSeries()} | ${"rollingVar"} | ${[1, [.23], 1, true]} - ${fltSeries()} | ${"round"} | ${[1]} - ${numSeries()} | ${"sample"} | ${[]} - ${numSeries()} | ${"sample"} | ${[1, null, true]} - ${numSeries()} | ${"sample"} | ${[null, 1]} - ${numSeries()} | ${"sample"} | ${[{n: 1}]} - ${numSeries()} | ${"sample"} | ${[{frac: 0.5}]} - ${numSeries()} | ${"sample"} | ${[{n: 1, withReplacement: true}]} - ${numSeries()} | ${"sample"} | ${[{frac: 0.1, withReplacement: true}]} - ${numSeries()} | ${"sample"} | ${[{frac: 0.1, withReplacement: true, seed: 1n}]} - ${numSeries()} | ${"sample"} | ${[{frac: 0.1, withReplacement: true, seed: 1}]} - ${numSeries()} | ${"sample"} | ${[{n: 1, withReplacement: true, seed: 1}]} - ${numSeries()} | ${"seriesEqual"} | ${[other()]} - ${numSeries()} | ${"seriesEqual"} | ${[other(), true]} - ${numSeries()} | ${"seriesEqual"} | ${[other(), false]} - ${numSeries()} | ${"set"} | ${[boolSeries(), 2]} - ${fltSeries()} | ${"setAtIdx"} | ${[[0, 1], 1]} - ${numSeries()} | ${"shift"} | ${[]} - ${numSeries()} | ${"shift"} | ${[1]} - ${numSeries()} | ${"shiftAndFill"} | ${[1, 2]} - ${numSeries()} | ${"shiftAndFill"} | ${[{periods: 1, fillValue: 2}]} - ${numSeries()} | ${"skew"} | ${[]} - ${numSeries()} | ${"skew"} | ${[true]} - ${numSeries()} | ${"skew"} | ${[false]} - ${numSeries()} | ${"skew"} | ${[{bias: true}]} - ${numSeries()} | ${"skew"} | ${[{bias: false}]} - ${numSeries()} | ${"slice"} | ${[1, 2]} - ${numSeries()} | ${"slice"} | ${[{offset: 1, length: 2}]} - ${numSeries()} | ${"sort"} | ${[]} - ${numSeries()} | ${"sort"} | ${[false]} - ${numSeries()} | ${"sort"} | ${[true]} - ${numSeries()} | ${"sort"} | ${[{reverse: true}]} - ${numSeries()} | ${"sort"} | ${[{reverse: false}]} - ${numSeries()} | ${"sum"} | ${[]} - ${numSeries()} | ${"tail"} | ${[]} - ${numSeries()} | ${"take"} | ${[[1, 2]]} - ${numSeries()} | ${"takeEvery"} | ${[1]} - ${numSeries()} | ${"toArray"} | ${[]} - ${numSeries()} | ${"unique"} | ${[]} - ${numSeries()} | ${"valueCounts"} | ${[]} - ${numSeries()} | ${"zipWith"} | ${[boolSeries(), other()]} - `("$# $method is callable", ({series, method, args}) => { + series | method | args + ${numSeries()} | ${"abs"} | ${[]} + ${numSeries()} | ${"as"} | ${[chance.string()]} + ${numSeries()} | ${"alias"} | ${[chance.string()]} + ${numSeries()} | ${"append"} | ${[other()]} + ${numSeries()} | ${"argMax"} | ${[]} + ${numSeries()} | ${"argMin"} | ${[]} + ${numSeries()} | ${"argSort"} | ${[]} + ${boolSeries()} | ${"argTrue"} | ${[]} + ${numSeries()} | ${"argUnique"} | ${[]} + ${numSeries()} | ${"cast"} | ${[pl.UInt32]} + ${numSeries()} | ${"chunkLengths"} | ${[]} + ${numSeries()} | ${"clone"} | ${[]} + ${numSeries()} | ${"cumMax"} | ${[]} + ${numSeries()} | ${"cumMin"} | ${[]} + ${numSeries()} | ${"cumProd"} | ${[]} + ${numSeries()} | ${"cumSum"} | ${[]} + ${numSeries()} | ${"describe"} | ${[]} + ${numSeries()} | ${"diff"} | ${[]} + ${numSeries()} | ${"diff"} | ${[{ n: 1, nullBehavior: "drop" }]} + ${numSeries()} | ${"diff"} | ${[{ nullBehavior: "drop" }]} + ${numSeries()} | ${"diff"} | ${[1, "drop"]} + ${numSeries()} | ${"dot"} | ${[other()]} + ${numSeries()} | ${"dropNulls"} | ${[]} + ${numSeries()} | ${"fillNull"} | ${["zero"]} + ${numSeries()} | ${"fillNull"} | ${[{ strategy: "zero" }]} + ${numSeries()} | ${"filter"} | ${[boolSeries()]} + ${fltSeries()} | ${"floor"} | ${[]} + ${numSeries()} | ${"hasValidity"} | ${[]} + ${numSeries()} | ${"hash"} | ${[]} + ${numSeries()} | ${"hash"} | ${[{ k0: 10 }]} + ${numSeries()} | ${"hash"} | ${[{ k0: 10, k1: 29 }]} + ${numSeries()} | ${"hash"} | ${[{ k0: 10, k1: 29, k2: 3 }]} + ${numSeries()} | ${"hash"} | ${[{ k0: 10, k1: 29, k3: 1, k2: 3 }]} + ${numSeries()} | ${"hash"} | ${[1]} + ${numSeries()} | ${"hash"} | ${[1, 2]} + ${numSeries()} | ${"hash"} | ${[1, 2, 3]} + ${numSeries()} | ${"hash"} | ${[1, 2, 3, 4]} + ${numSeries()} | ${"head"} | ${[]} + ${numSeries()} | ${"head"} | ${[1]} + ${numSeries()} | ${"inner"} | ${[]} + ${numSeries()} | ${"interpolate"} | ${[]} + ${numSeries()} | ${"isBoolean"} | ${[]} + ${numSeries()} | ${"isDateTime"} | ${[]} + ${numSeries()} | ${"isDuplicated"} | ${[]} + ${fltSeries()} | ${"isFinite"} | ${[]} + ${numSeries()} | ${"isFirst"} | ${[]} + ${numSeries()} | ${"isFloat"} | ${[]} + ${numSeries()} | ${"isIn"} | ${[other()]} + ${numSeries()} | ${"isIn"} | ${[[1, 2, 3]]} + ${fltSeries()} | ${"isInfinite"} | ${[]} + ${numSeries()} | ${"isNotNull"} | ${[]} + ${numSeries()} | ${"isNull"} | ${[]} + ${numSeries()} | ${"isNumeric"} | ${[]} + ${numSeries()} | ${"isUnique"} | ${[]} + ${numSeries()} | ${"isUtf8"} | ${[]} + ${numSeries()} | ${"kurtosis"} | ${[]} + ${numSeries()} | ${"kurtosis"} | ${[{ fisher: true, bias: true }]} + ${numSeries()} | ${"kurtosis"} | ${[{ bias: false }]} + ${numSeries()} | ${"kurtosis"} | ${[{ fisher: false }]} + ${numSeries()} | ${"kurtosis"} | ${[false, false]} + ${numSeries()} | ${"kurtosis"} | ${[false]} + ${numSeries()} | ${"len"} | ${[]} + ${numSeries()} | ${"limit"} | ${[]} + ${numSeries()} | ${"limit"} | ${[2]} + ${numSeries()} | ${"max"} | ${[]} + ${numSeries()} | ${"mean"} | ${[]} + ${numSeries()} | ${"median"} | ${[]} + ${numSeries()} | ${"min"} | ${[]} + ${numSeries()} | ${"mode"} | ${[]} + ${numSeries()} | ${"nChunks"} | ${[]} + ${numSeries()} | ${"nUnique"} | ${[]} + ${numSeries()} | ${"nullCount"} | ${[]} + ${numSeries()} | ${"peakMax"} | ${[]} + ${numSeries()} | ${"peakMin"} | ${[]} + ${numSeries()} | ${"quantile"} | ${[0.4]} + ${numSeries()} | ${"rank"} | ${[]} + ${numSeries()} | ${"rank"} | ${["average"]} + ${numSeries()} | ${"rechunk"} | ${[]} + ${numSeries()} | ${"rechunk"} | ${[true]} + ${numSeries()} | ${"rename"} | ${["new name"]} + ${numSeries()} | ${"rename"} | ${["new name", true]} + ${numSeries()} | ${"rename"} | ${[{ name: "new name" }]} + ${numSeries()} | ${"rename"} | ${[{ name: "new name", inPlace: true }]} + ${numSeries()} | ${"rename"} | ${[{ name: "new name" }]} + ${numSeries()} | ${"rollingMax"} | ${[{ windowSize: 1 }]} + ${numSeries()} | ${"rollingMax"} | ${[{ windowSize: 1, weights: [0.33] }]} + ${numSeries()} | ${"rollingMax"} | ${[{ windowSize: 1, weights: [0.11], minPeriods: 1 }]} + ${numSeries()} | ${"rollingMax"} | ${[{ windowSize: 1, weights: [0.44], minPeriods: 1, center: false }]} + ${numSeries()} | ${"rollingMax"} | ${[1]} + ${numSeries()} | ${"rollingMax"} | ${[1, [0.11]]} + ${numSeries()} | ${"rollingMax"} | ${[1, [0.11], 1]} + ${numSeries()} | ${"rollingMax"} | ${[1, [0.23], 1, true]} + ${numSeries()} | ${"rollingMean"} | ${[{ windowSize: 1 }]} + ${numSeries()} | ${"rollingMean"} | ${[{ windowSize: 1, weights: [0.33] }]} + ${numSeries()} | ${"rollingMean"} | ${[{ windowSize: 1, weights: [0.11], minPeriods: 1 }]} + ${numSeries()} | ${"rollingMean"} | ${[{ windowSize: 1, weights: [0.44], minPeriods: 1, center: false }]} + ${numSeries()} | ${"rollingMean"} | ${[1]} + ${numSeries()} | ${"rollingMean"} | ${[1, [0.11]]} + ${numSeries()} | ${"rollingMean"} | ${[1, [0.11], 1]} + ${numSeries()} | ${"rollingMean"} | ${[1, [0.23], 1, true]} + ${numSeries()} | ${"rollingMin"} | ${[{ windowSize: 1 }]} + ${numSeries()} | ${"rollingMin"} | ${[{ windowSize: 1, weights: [0.33] }]} + ${numSeries()} | ${"rollingMin"} | ${[{ windowSize: 1, weights: [0.11], minPeriods: 1 }]} + ${numSeries()} | ${"rollingMin"} | ${[{ windowSize: 1, weights: [0.44], minPeriods: 1, center: false }]} + ${numSeries()} | ${"rollingMin"} | ${[1]} + ${numSeries()} | ${"rollingMin"} | ${[1, [0.11]]} + ${numSeries()} | ${"rollingMin"} | ${[1, [0.11], 1]} + ${numSeries()} | ${"rollingMin"} | ${[1, [0.23], 1, true]} + ${numSeries()} | ${"rollingSum"} | ${[{ windowSize: 1 }]} + ${numSeries()} | ${"rollingSum"} | ${[{ windowSize: 1, weights: [0.33] }]} + ${numSeries()} | ${"rollingSum"} | ${[{ windowSize: 1, weights: [0.11], minPeriods: 1 }]} + ${numSeries()} | ${"rollingSum"} | ${[{ windowSize: 1, weights: [0.44], minPeriods: 1, center: false }]} + ${numSeries()} | ${"rollingSum"} | ${[1]} + ${numSeries()} | ${"rollingSum"} | ${[1, [0.11]]} + ${numSeries()} | ${"rollingSum"} | ${[1, [0.11], 1]} + ${numSeries()} | ${"rollingSum"} | ${[1, [0.23], 1, true]} + ${numSeries()} | ${"rollingVar"} | ${[{ windowSize: 1 }]} + ${numSeries()} | ${"rollingVar"} | ${[{ windowSize: 1, weights: [0.33] }]} + ${numSeries()} | ${"rollingVar"} | ${[{ windowSize: 1, weights: [0.11], minPeriods: 1 }]} + ${numSeries()} | ${"rollingVar"} | ${[{ windowSize: 1, weights: [0.44], minPeriods: 1, center: false }]} + ${numSeries()} | ${"rollingVar"} | ${[1]} + ${numSeries()} | ${"rollingVar"} | ${[1, [0.11]]} + ${numSeries()} | ${"rollingVar"} | ${[1, [0.11], 1]} + ${numSeries()} | ${"rollingVar"} | ${[1, [0.23], 1, true]} + ${fltSeries()} | ${"round"} | ${[1]} + ${numSeries()} | ${"sample"} | ${[]} + ${numSeries()} | ${"sample"} | ${[1, null, true]} + ${numSeries()} | ${"sample"} | ${[null, 1]} + ${numSeries()} | ${"sample"} | ${[{ n: 1 }]} + ${numSeries()} | ${"sample"} | ${[{ frac: 0.5 }]} + ${numSeries()} | ${"sample"} | ${[{ n: 1, withReplacement: true }]} + ${numSeries()} | ${"sample"} | ${[{ frac: 0.1, withReplacement: true }]} + ${numSeries()} | ${"sample"} | ${[{ frac: 0.1, withReplacement: true, seed: 1n }]} + ${numSeries()} | ${"sample"} | ${[{ frac: 0.1, withReplacement: true, seed: 1 }]} + ${numSeries()} | ${"sample"} | ${[{ n: 1, withReplacement: true, seed: 1 }]} + ${numSeries()} | ${"seriesEqual"} | ${[other()]} + ${numSeries()} | ${"seriesEqual"} | ${[other(), true]} + ${numSeries()} | ${"seriesEqual"} | ${[other(), false]} + ${numSeries()} | ${"set"} | ${[boolSeries(), 2]} + ${fltSeries()} | ${"setAtIdx"} | ${[[0, 1], 1]} + ${numSeries()} | ${"shift"} | ${[]} + ${numSeries()} | ${"shift"} | ${[1]} + ${numSeries()} | ${"shiftAndFill"} | ${[1, 2]} + ${numSeries()} | ${"shiftAndFill"} | ${[{ periods: 1, fillValue: 2 }]} + ${numSeries()} | ${"skew"} | ${[]} + ${numSeries()} | ${"skew"} | ${[true]} + ${numSeries()} | ${"skew"} | ${[false]} + ${numSeries()} | ${"skew"} | ${[{ bias: true }]} + ${numSeries()} | ${"skew"} | ${[{ bias: false }]} + ${numSeries()} | ${"slice"} | ${[1, 2]} + ${numSeries()} | ${"slice"} | ${[{ offset: 1, length: 2 }]} + ${numSeries()} | ${"sort"} | ${[]} + ${numSeries()} | ${"sort"} | ${[false]} + ${numSeries()} | ${"sort"} | ${[true]} + ${numSeries()} | ${"sort"} | ${[{ reverse: true }]} + ${numSeries()} | ${"sort"} | ${[{ reverse: false }]} + ${numSeries()} | ${"sum"} | ${[]} + ${numSeries()} | ${"tail"} | ${[]} + ${numSeries()} | ${"take"} | ${[[1, 2]]} + ${numSeries()} | ${"takeEvery"} | ${[1]} + ${numSeries()} | ${"toArray"} | ${[]} + ${numSeries()} | ${"unique"} | ${[]} + ${numSeries()} | ${"valueCounts"} | ${[]} + ${numSeries()} | ${"zipWith"} | ${[boolSeries(), other()]} + `("$# $method is callable", ({ series, method, args }) => { try { series[method](...args); } catch (err) { @@ -425,107 +417,107 @@ describe("series", () => { }); it.each` - name | actual | expected - ${"dtype:Utf8"} | ${pl.Series(["foo"]).dtype} | ${pl.Utf8} - ${"dtype:UInt64"} | ${pl.Series([1n]).dtype} | ${pl.UInt64} - ${"dtype:Float64"} | ${pl.Series([1]).dtype} | ${pl.Float64} - ${"dtype"} | ${pl.Series(["foo"]).dtype} | ${pl.Utf8} - ${"name"} | ${pl.Series("a", ["foo"]).name} | ${"a"} - ${"length"} | ${pl.Series([1, 2, 3, 4]).length} | ${4} - ${"abs"} | ${pl.Series([1, 2, -3]).abs()} | ${pl.Series([1, 2, 3])} - ${"alias"} | ${pl.Series([1, 2, 3]).as("foo")} | ${pl.Series("foo", [1, 2, 3])} - ${"alias"} | ${pl.Series([1, 2, 3]).alias("foo")} | ${pl.Series("foo", [1, 2, 3])} - ${"argMax"} | ${pl.Series([1, 2, 3]).argMax()} | ${2} - ${"argMin"} | ${pl.Series([1, 2, 3]).argMin()} | ${0} - ${"argSort"} | ${pl.Series([3, 2, 1]).argSort()} | ${pl.Series([2, 1, 0])} - ${"argTrue"} | ${pl.Series([true, false]).argTrue()} | ${pl.Series([0])} - ${"argUnique"} | ${pl.Series([1, 1, 2]).argUnique()} | ${pl.Series([0, 2])} - ${"cast-Int16"} | ${pl.Series("", [1, 1, 2]).cast(pl.Int16)} | ${pl.Series("", [1, 1, 2], pl.Int16)} - ${"cast-Int32"} | ${pl.Series("", [1, 1, 2]).cast(pl.Int32)} | ${pl.Series("", [1, 1, 2], pl.Int32)} - ${"cast-Int64"} | ${pl.Series("", [1, 1, 2]).cast(pl.Int64)} | ${pl.Series("", [1, 1, 2], pl.Int64)} - ${"cast-UInt16"} | ${pl.Series("", [1, 1, 2]).cast(pl.UInt16)} | ${pl.Series("", [1, 1, 2], pl.UInt16)} - ${"cast-UInt32"} | ${pl.Series("", [1, 1, 2]).cast(pl.UInt32)} | ${pl.Series("", [1, 1, 2], pl.UInt32)} - ${"cast-UInt64"} | ${pl.Series("", [1, 1, 2]).cast(pl.UInt64)} | ${pl.Series("", [1n, 1n, 2n])} - ${"cast-Utf8"} | ${pl.Series("", [1, 1, 2]).cast(pl.Utf8)} | ${pl.Series("", ["1.0", "1.0", "2.0"])} - ${"chunkLengths"} | ${pl.Series([1, 2, 3]).chunkLengths()[0]} | ${3} - ${"clone"} | ${pl.Series([1, 2, 3]).clone()} | ${pl.Series([1, 2, 3])} - ${"concat"} | ${pl.Series([1]).concat(pl.Series([2, 3]))} | ${pl.Series([1, 2, 3])} - ${"cumMax"} | ${pl.Series([3, 2, 4]).cumMax()} | ${pl.Series([3, 3, 4])} - ${"cumMin"} | ${pl.Series([3, 2, 4]).cumMin()} | ${pl.Series([3, 2, 2])} - ${"cumProd"} | ${pl.Series("", [1, 2, 3], pl.Int32).cumProd()} | ${pl.Series("", [1, 2, 6], pl.Int64)} - ${"cumSum"} | ${pl.Series("", [1, 2, 3], pl.Int32).cumSum()} | ${pl.Series("", [1, 3, 6], pl.Int32)} - ${"diff"} | ${pl.Series([1, 2, 12]).diff(1, "drop").toObject()} | ${pl.Series([1, 10]).toObject()} - ${"diff"} | ${pl.Series([1, 11]).diff(1, "ignore")} | ${pl.Series("", [null, 10], pl.Float64)} - ${"dropNulls"} | ${pl.Series([1, null, 2]).dropNulls()} | ${pl.Series([1, 2])} - ${"dropNulls"} | ${pl.Series([1, undefined, 2]).dropNulls()} | ${pl.Series([1, 2])} - ${"dropNulls"} | ${pl.Series(["a", null, "f"]).dropNulls()} | ${pl.Series(["a", "f"])} - ${"fillNull:zero"} | ${pl.Series([1, null, 2]).fillNull("zero")} | ${pl.Series([1, 0, 2])} - ${"fillNull:one"} | ${pl.Series([1, null, 2]).fillNull("one")} | ${pl.Series([1, 1, 2])} - ${"fillNull:max"} | ${pl.Series([1, null, 5]).fillNull("max")} | ${pl.Series([1, 5, 5])} - ${"fillNull:min"} | ${pl.Series([1, null, 5]).fillNull("min")} | ${pl.Series([1, 1, 5])} - ${"fillNull:mean"} | ${pl.Series([1, 1, null, 10]).fillNull("mean")} | ${pl.Series([1, 1, 4, 10])} - ${"fillNull:back"} | ${pl.Series([1, 1, null, 10]).fillNull("backward")} | ${pl.Series([1, 1, 10, 10])} - ${"fillNull:fwd"} | ${pl.Series([1, 1, null, 10]).fillNull("forward")} | ${pl.Series([1, 1, 1, 10])} - ${"floor"} | ${pl.Series([1.1, 2.2]).floor()} | ${pl.Series([1, 2])} - ${"get"} | ${pl.Series(["foo"]).get(0)} | ${"foo"} - ${"get"} | ${pl.Series([1, 2, 3]).get(2)} | ${3} - ${"getIndex"} | ${pl.Series(["a", "b", "c"]).getIndex(0)} | ${"a"} - ${"hasValidity"} | ${pl.Series([1, null, 2]).hasValidity()} | ${true} - ${"hasValidity"} | ${pl.Series([1, 1, 2]).hasValidity()} | ${false} - ${"hash"} | ${pl.Series([1]).hash()} | ${pl.Series([5246693565886627840n])} - ${"head"} | ${pl.Series([1, 2, 3, 4, 5, 5, 5]).head()} | ${pl.Series([1, 2, 3, 4, 5])} - ${"head"} | ${pl.Series([1, 2, 3, 4, 5, 5, 5]).head(2)} | ${pl.Series([1, 2])} - ${"interpolate"} | ${pl.Series([1, 2, null, null, 5]).interpolate()} | ${pl.Series([1, 2, 3, 4, 5])} - ${"isBoolean"} | ${pl.Series([1, 2, 3]).isBoolean()} | ${false} - ${"isBoolean"} | ${pl.Series([true, false]).isBoolean()} | ${true} - ${"isDateTime"} | ${pl.Series([new Date(Date.now())]).isDateTime()} | ${true} - ${"isDuplicated"} | ${pl.Series([1, 3, 3]).isDuplicated()} | ${pl.Series([false, true, true])} - ${"isFinite"} | ${pl.Series([1.0, 3.1]).isFinite()} | ${pl.Series([true, true])} - ${"isInfinite"} | ${pl.Series([1.0, 2]).isInfinite()} | ${pl.Series([false, false])} - ${"isNotNull"} | ${pl.Series([1, null, undefined, 2]).isNotNull()} | ${pl.Series([true, false, false, true])} - ${"isNull"} | ${pl.Series([1, null, undefined, 2]).isNull()} | ${pl.Series([false, true, true, false])} - ${"isNumeric"} | ${pl.Series([1, 2, 3]).isNumeric()} | ${true} - ${"isUnique"} | ${pl.Series([1, 2, 3, 1]).isUnique()} | ${pl.Series([false, true, true, false])} - ${"isUtf8"} | ${pl.Series([1, 2, 3, 1]).isUtf8()} | ${false} - ${"kurtosis"} | ${pl.Series([1, 2, 3, 3, 4]).kurtosis()?.toFixed(6)} | ${"-1.044379"} - ${"isUtf8"} | ${pl.Series(["foo"]).isUtf8()} | ${true} - ${"len"} | ${pl.Series([1, 2, 3, 4, 5]).len()} | ${5} - ${"limit"} | ${pl.Series([1, 2, 3, 4, 5, 5, 5]).limit(2)} | ${pl.Series([1, 2])} - ${"max"} | ${pl.Series([-1, 10, 3]).max()} | ${10} - ${"mean"} | ${pl.Series([1, 1, 10]).mean()} | ${4} - ${"median"} | ${pl.Series([1, 1, 10]).median()} | ${1} - ${"min"} | ${pl.Series([-1, 10, 3]).min()} | ${-1} - ${"nChunks"} | ${pl.Series([1, 2, 3, 4, 4]).nChunks()} | ${1} - ${"nullCount"} | ${pl.Series([1, null, null, 4, 4]).nullCount()} | ${2} - ${"peakMax"} | ${pl.Series([9, 4, 5]).peakMax()} | ${pl.Series([true, false, true])} - ${"peakMin"} | ${pl.Series([4, 1, 3, 2, 5]).peakMin()} | ${pl.Series([false, true, false, true, false])} - ${"quantile"} | ${pl.Series([1, 2, 3]).quantile(0.5)} | ${2} - ${"rank"} | ${pl.Series([1, 2, 3, 2, 2, 3, 0]).rank("dense")} | ${pl.Series("", [2, 3, 4, 3, 3, 4, 1], pl.UInt32)} - ${"rename"} | ${pl.Series([1, 3, 0]).rename("b")} | ${pl.Series("b", [1, 3, 0])} - ${"rollingMax"} | ${pl.Series([1, 2, 3, 2, 1]).rollingMax(2)} | ${pl.Series("", [null, 2, 3, 3, 2], pl.Float64)} - ${"rollingMin"} | ${pl.Series([1, 2, 3, 2, 1]).rollingMin(2)} | ${pl.Series("", [null, 1, 2, 2, 1], pl.Float64)} - ${"rollingSum"} | ${pl.Series([1, 2, 3, 2, 1]).rollingSum(2)} | ${pl.Series("", [null, 3, 5, 5, 3], pl.Float64)} - ${"rollingMean"} | ${pl.Series([1, 2, 3, 2, 1]).rollingMean(2)} | ${pl.Series("", [null, 1.5, 2.5, 2.5, 1.5], pl.Float64)} - ${"rollingVar"} | ${pl.Series([1, 2, 3, 2, 1]).rollingVar(2)[1]} | ${0.5} - ${"sample:n"} | ${pl.Series([1, 2, 3, 4, 5]).sample(2).len()} | ${2} - ${"sample:frac"} | ${pl.Series([1, 2, 3, 4, 5]).sample({frac:.4, seed:0}).len()}| ${2} - ${"shift"} | ${pl.Series([1, 2, 3]).shift(1)} | ${pl.Series([null, 1, 2])} - ${"shift"} | ${pl.Series([1, 2, 3]).shift(-1)} | ${pl.Series([2, 3, null])} - ${"skew"} | ${pl.Series([1, 2, 3, 3, 0]).skew()?.toPrecision(6)} | ${"-0.363173"} - ${"slice"} | ${pl.Series([1, 2, 3, 3, 0]).slice(-3, 3)} | ${pl.Series([3, 3, 0])} - ${"slice"} | ${pl.Series([1, 2, 3, 3, 0]).slice(1, 3)} | ${pl.Series([2, 3, 3])} - ${"sort"} | ${pl.Series([4, 2, 5, 1, 2, 3, 3, 0]).sort()} | ${pl.Series([0, 1, 2, 2, 3, 3, 4, 5])} - ${"sort"} | ${pl.Series([4, 2, 5, 0]).sort({reverse:true})} | ${pl.Series([5, 4, 2, 0])} - ${"sort"} | ${pl.Series([4, 2, 5, 0]).sort({reverse:false})} | ${pl.Series([0, 2, 4, 5])} - ${"sum"} | ${pl.Series([1, 2, 2, 1]).sum()} | ${6} - ${"tail"} | ${pl.Series([1, 2, 2, 1]).tail(2)} | ${pl.Series([2, 1])} - ${"takeEvery"} | ${pl.Series([1, 3, 2, 9, 1]).takeEvery(2)} | ${pl.Series([1, 2, 1])} - ${"take"} | ${pl.Series([1, 3, 2, 9, 1]).take([0, 1, 3])} | ${pl.Series([1, 3, 9])} - ${"toArray"} | ${pl.Series([1, 2, 3]).toArray()} | ${[1, 2, 3]} - ${"unique"} | ${pl.Series([1, 2, 3, 3]).unique().sort()} | ${pl.Series([1, 2, 3])} - ${"shiftAndFill"} | ${pl.Series("foo", [1, 2, 3]).shiftAndFill(1, 99)} | ${pl.Series("foo", [99, 1, 2])} - `("$# $name: expected matches actual ", ({expected, actual}) => { - if(pl.Series.isSeries(expected) && pl.Series.isSeries(actual)) { + name | actual | expected + ${"dtype:Utf8"} | ${pl.Series(["foo"]).dtype} | ${pl.Utf8} + ${"dtype:UInt64"} | ${pl.Series([1n]).dtype} | ${pl.UInt64} + ${"dtype:Float64"} | ${pl.Series([1]).dtype} | ${pl.Float64} + ${"dtype"} | ${pl.Series(["foo"]).dtype} | ${pl.Utf8} + ${"name"} | ${pl.Series("a", ["foo"]).name} | ${"a"} + ${"length"} | ${pl.Series([1, 2, 3, 4]).length} | ${4} + ${"abs"} | ${pl.Series([1, 2, -3]).abs()} | ${pl.Series([1, 2, 3])} + ${"alias"} | ${pl.Series([1, 2, 3]).as("foo")} | ${pl.Series("foo", [1, 2, 3])} + ${"alias"} | ${pl.Series([1, 2, 3]).alias("foo")} | ${pl.Series("foo", [1, 2, 3])} + ${"argMax"} | ${pl.Series([1, 2, 3]).argMax()} | ${2} + ${"argMin"} | ${pl.Series([1, 2, 3]).argMin()} | ${0} + ${"argSort"} | ${pl.Series([3, 2, 1]).argSort()} | ${pl.Series([2, 1, 0])} + ${"argTrue"} | ${pl.Series([true, false]).argTrue()} | ${pl.Series([0])} + ${"argUnique"} | ${pl.Series([1, 1, 2]).argUnique()} | ${pl.Series([0, 2])} + ${"cast-Int16"} | ${pl.Series("", [1, 1, 2]).cast(pl.Int16)} | ${pl.Series("", [1, 1, 2], pl.Int16)} + ${"cast-Int32"} | ${pl.Series("", [1, 1, 2]).cast(pl.Int32)} | ${pl.Series("", [1, 1, 2], pl.Int32)} + ${"cast-Int64"} | ${pl.Series("", [1, 1, 2]).cast(pl.Int64)} | ${pl.Series("", [1, 1, 2], pl.Int64)} + ${"cast-UInt16"} | ${pl.Series("", [1, 1, 2]).cast(pl.UInt16)} | ${pl.Series("", [1, 1, 2], pl.UInt16)} + ${"cast-UInt32"} | ${pl.Series("", [1, 1, 2]).cast(pl.UInt32)} | ${pl.Series("", [1, 1, 2], pl.UInt32)} + ${"cast-UInt64"} | ${pl.Series("", [1, 1, 2]).cast(pl.UInt64)} | ${pl.Series("", [1n, 1n, 2n])} + ${"cast-Utf8"} | ${pl.Series("", [1, 1, 2]).cast(pl.Utf8)} | ${pl.Series("", ["1.0", "1.0", "2.0"])} + ${"chunkLengths"} | ${pl.Series([1, 2, 3]).chunkLengths()[0]} | ${3} + ${"clone"} | ${pl.Series([1, 2, 3]).clone()} | ${pl.Series([1, 2, 3])} + ${"concat"} | ${pl.Series([1]).concat(pl.Series([2, 3]))} | ${pl.Series([1, 2, 3])} + ${"cumMax"} | ${pl.Series([3, 2, 4]).cumMax()} | ${pl.Series([3, 3, 4])} + ${"cumMin"} | ${pl.Series([3, 2, 4]).cumMin()} | ${pl.Series([3, 2, 2])} + ${"cumProd"} | ${pl.Series("", [1, 2, 3], pl.Int32).cumProd()} | ${pl.Series("", [1, 2, 6], pl.Int64)} + ${"cumSum"} | ${pl.Series("", [1, 2, 3], pl.Int32).cumSum()} | ${pl.Series("", [1, 3, 6], pl.Int32)} + ${"diff"} | ${pl.Series([1, 2, 12]).diff(1, "drop").toObject()} | ${pl.Series([1, 10]).toObject()} + ${"diff"} | ${pl.Series([1, 11]).diff(1, "ignore")} | ${pl.Series("", [null, 10], pl.Float64)} + ${"dropNulls"} | ${pl.Series([1, null, 2]).dropNulls()} | ${pl.Series([1, 2])} + ${"dropNulls"} | ${pl.Series([1, undefined, 2]).dropNulls()} | ${pl.Series([1, 2])} + ${"dropNulls"} | ${pl.Series(["a", null, "f"]).dropNulls()} | ${pl.Series(["a", "f"])} + ${"fillNull:zero"} | ${pl.Series([1, null, 2]).fillNull("zero")} | ${pl.Series([1, 0, 2])} + ${"fillNull:one"} | ${pl.Series([1, null, 2]).fillNull("one")} | ${pl.Series([1, 1, 2])} + ${"fillNull:max"} | ${pl.Series([1, null, 5]).fillNull("max")} | ${pl.Series([1, 5, 5])} + ${"fillNull:min"} | ${pl.Series([1, null, 5]).fillNull("min")} | ${pl.Series([1, 1, 5])} + ${"fillNull:mean"} | ${pl.Series([1, 1, null, 10]).fillNull("mean")} | ${pl.Series([1, 1, 4, 10])} + ${"fillNull:back"} | ${pl.Series([1, 1, null, 10]).fillNull("backward")} | ${pl.Series([1, 1, 10, 10])} + ${"fillNull:fwd"} | ${pl.Series([1, 1, null, 10]).fillNull("forward")} | ${pl.Series([1, 1, 1, 10])} + ${"floor"} | ${pl.Series([1.1, 2.2]).floor()} | ${pl.Series([1, 2])} + ${"get"} | ${pl.Series(["foo"]).get(0)} | ${"foo"} + ${"get"} | ${pl.Series([1, 2, 3]).get(2)} | ${3} + ${"getIndex"} | ${pl.Series(["a", "b", "c"]).getIndex(0)} | ${"a"} + ${"hasValidity"} | ${pl.Series([1, null, 2]).hasValidity()} | ${true} + ${"hasValidity"} | ${pl.Series([1, 1, 2]).hasValidity()} | ${false} + ${"hash"} | ${pl.Series([1]).hash()} | ${pl.Series([5246693565886627840n])} + ${"head"} | ${pl.Series([1, 2, 3, 4, 5, 5, 5]).head()} | ${pl.Series([1, 2, 3, 4, 5])} + ${"head"} | ${pl.Series([1, 2, 3, 4, 5, 5, 5]).head(2)} | ${pl.Series([1, 2])} + ${"interpolate"} | ${pl.Series([1, 2, null, null, 5]).interpolate()} | ${pl.Series([1, 2, 3, 4, 5])} + ${"isBoolean"} | ${pl.Series([1, 2, 3]).isBoolean()} | ${false} + ${"isBoolean"} | ${pl.Series([true, false]).isBoolean()} | ${true} + ${"isDateTime"} | ${pl.Series([new Date(Date.now())]).isDateTime()} | ${true} + ${"isDuplicated"} | ${pl.Series([1, 3, 3]).isDuplicated()} | ${pl.Series([false, true, true])} + ${"isFinite"} | ${pl.Series([1.0, 3.1]).isFinite()} | ${pl.Series([true, true])} + ${"isInfinite"} | ${pl.Series([1.0, 2]).isInfinite()} | ${pl.Series([false, false])} + ${"isNotNull"} | ${pl.Series([1, null, undefined, 2]).isNotNull()} | ${pl.Series([true, false, false, true])} + ${"isNull"} | ${pl.Series([1, null, undefined, 2]).isNull()} | ${pl.Series([false, true, true, false])} + ${"isNumeric"} | ${pl.Series([1, 2, 3]).isNumeric()} | ${true} + ${"isUnique"} | ${pl.Series([1, 2, 3, 1]).isUnique()} | ${pl.Series([false, true, true, false])} + ${"isUtf8"} | ${pl.Series([1, 2, 3, 1]).isUtf8()} | ${false} + ${"kurtosis"} | ${pl.Series([1, 2, 3, 3, 4]).kurtosis()?.toFixed(6)} | ${"-1.044379"} + ${"isUtf8"} | ${pl.Series(["foo"]).isUtf8()} | ${true} + ${"len"} | ${pl.Series([1, 2, 3, 4, 5]).len()} | ${5} + ${"limit"} | ${pl.Series([1, 2, 3, 4, 5, 5, 5]).limit(2)} | ${pl.Series([1, 2])} + ${"max"} | ${pl.Series([-1, 10, 3]).max()} | ${10} + ${"mean"} | ${pl.Series([1, 1, 10]).mean()} | ${4} + ${"median"} | ${pl.Series([1, 1, 10]).median()} | ${1} + ${"min"} | ${pl.Series([-1, 10, 3]).min()} | ${-1} + ${"nChunks"} | ${pl.Series([1, 2, 3, 4, 4]).nChunks()} | ${1} + ${"nullCount"} | ${pl.Series([1, null, null, 4, 4]).nullCount()} | ${2} + ${"peakMax"} | ${pl.Series([9, 4, 5]).peakMax()} | ${pl.Series([true, false, true])} + ${"peakMin"} | ${pl.Series([4, 1, 3, 2, 5]).peakMin()} | ${pl.Series([false, true, false, true, false])} + ${"quantile"} | ${pl.Series([1, 2, 3]).quantile(0.5)} | ${2} + ${"rank"} | ${pl.Series([1, 2, 3, 2, 2, 3, 0]).rank("dense")} | ${pl.Series("", [2, 3, 4, 3, 3, 4, 1], pl.UInt32)} + ${"rename"} | ${pl.Series([1, 3, 0]).rename("b")} | ${pl.Series("b", [1, 3, 0])} + ${"rollingMax"} | ${pl.Series([1, 2, 3, 2, 1]).rollingMax(2)} | ${pl.Series("", [null, 2, 3, 3, 2], pl.Float64)} + ${"rollingMin"} | ${pl.Series([1, 2, 3, 2, 1]).rollingMin(2)} | ${pl.Series("", [null, 1, 2, 2, 1], pl.Float64)} + ${"rollingSum"} | ${pl.Series([1, 2, 3, 2, 1]).rollingSum(2)} | ${pl.Series("", [null, 3, 5, 5, 3], pl.Float64)} + ${"rollingMean"} | ${pl.Series([1, 2, 3, 2, 1]).rollingMean(2)} | ${pl.Series("", [null, 1.5, 2.5, 2.5, 1.5], pl.Float64)} + ${"rollingVar"} | ${pl.Series([1, 2, 3, 2, 1]).rollingVar(2)[1]} | ${0.5} + ${"sample:n"} | ${pl.Series([1, 2, 3, 4, 5]).sample(2).len()} | ${2} + ${"sample:frac"} | ${pl.Series([1, 2, 3, 4, 5]).sample({ frac: 0.4, seed: 0 }).len()} | ${2} + ${"shift"} | ${pl.Series([1, 2, 3]).shift(1)} | ${pl.Series([null, 1, 2])} + ${"shift"} | ${pl.Series([1, 2, 3]).shift(-1)} | ${pl.Series([2, 3, null])} + ${"skew"} | ${pl.Series([1, 2, 3, 3, 0]).skew()?.toPrecision(6)} | ${"-0.363173"} + ${"slice"} | ${pl.Series([1, 2, 3, 3, 0]).slice(-3, 3)} | ${pl.Series([3, 3, 0])} + ${"slice"} | ${pl.Series([1, 2, 3, 3, 0]).slice(1, 3)} | ${pl.Series([2, 3, 3])} + ${"sort"} | ${pl.Series([4, 2, 5, 1, 2, 3, 3, 0]).sort()} | ${pl.Series([0, 1, 2, 2, 3, 3, 4, 5])} + ${"sort"} | ${pl.Series([4, 2, 5, 0]).sort({ reverse: true })} | ${pl.Series([5, 4, 2, 0])} + ${"sort"} | ${pl.Series([4, 2, 5, 0]).sort({ reverse: false })} | ${pl.Series([0, 2, 4, 5])} + ${"sum"} | ${pl.Series([1, 2, 2, 1]).sum()} | ${6} + ${"tail"} | ${pl.Series([1, 2, 2, 1]).tail(2)} | ${pl.Series([2, 1])} + ${"takeEvery"} | ${pl.Series([1, 3, 2, 9, 1]).takeEvery(2)} | ${pl.Series([1, 2, 1])} + ${"take"} | ${pl.Series([1, 3, 2, 9, 1]).take([0, 1, 3])} | ${pl.Series([1, 3, 9])} + ${"toArray"} | ${pl.Series([1, 2, 3]).toArray()} | ${[1, 2, 3]} + ${"unique"} | ${pl.Series([1, 2, 3, 3]).unique().sort()} | ${pl.Series([1, 2, 3])} + ${"shiftAndFill"} | ${pl.Series("foo", [1, 2, 3]).shiftAndFill(1, 99)} | ${pl.Series("foo", [99, 1, 2])} + `("$# $name: expected matches actual ", ({ expected, actual }) => { + if (pl.Series.isSeries(expected) && pl.Series.isSeries(actual)) { expect(actual).toSeriesEqual(expected); } else { expect(actual).toEqual(expected); @@ -559,12 +551,12 @@ describe("series", () => { expect(() => pl.Series([1, 2, 3]).set(mask, 99)).toThrow(); }); it.each` - name | fn | errorType - ${"isFinite"} | ${pl.Series(["foo"]).isFinite} | ${TypeError} - ${"isInfinite"} | ${pl.Series(["foo"]).isInfinite} | ${TypeError} - ${"rollingMax"} | ${() => pl.Series(["foo"]).rollingMax(null as any)} | ${Error} - ${"sample"} | ${() => pl.Series(["foo"]).sample(null as any)} | ${Error} - `("$# $name throws an error ", ({fn, errorType}) => { + name | fn | errorType + ${"isFinite"} | ${pl.Series(["foo"]).isFinite} | ${TypeError} + ${"isInfinite"} | ${pl.Series(["foo"]).isInfinite} | ${TypeError} + ${"rollingMax"} | ${() => pl.Series(["foo"]).rollingMax(null as any)} | ${Error} + ${"sample"} | ${() => pl.Series(["foo"]).sample(null as any)} | ${Error} + `("$# $name throws an error ", ({ fn, errorType }) => { expect(fn).toThrow(errorType); }); test("reinterpret", () => { @@ -602,7 +594,7 @@ describe("series", () => { test("round:named", () => { const s = pl.Series([1.1111, 2.2222]); const expected = pl.Series([1.11, 2.22]); - const actual = s.round({decimals: 2}); + const actual = s.round({ decimals: 2 }); expect(actual).toSeriesEqual(expected); }); }); @@ -682,16 +674,15 @@ describe("comparators & math", () => { }); describe("StringFunctions", () => { it.each` - name | actual | expected - ${"toUpperCase"} | ${pl.Series(["foo"]).str.toUpperCase()} | ${pl.Series(["FOO"])} - ${"lstrip"} | ${pl.Series([" foo"]).str.lstrip()} | ${pl.Series(["foo"])} - ${"rstrip"} | ${pl.Series(["foo "]).str.rstrip()} | ${pl.Series(["foo"])} - ${"toLowerCase"} | ${pl.Series(["FOO"]).str.toLowerCase()} | ${pl.Series(["foo"])} - ${"contains"} | ${pl.Series(["f1", "f0"]).str.contains(/[0]/)} | ${pl.Series([false, true])} - ${"lengths"} | ${pl.Series(["apple", "ham"]).str.lengths()} | ${pl.Series([5, 3])} - ${"slice"} | ${pl.Series(["apple", "ham"]).str.slice(1)} | ${pl.Series(["pple", "am"])} - `("$# $name expected matches actual", ({expected, actual}) => { - + name | actual | expected + ${"toUpperCase"} | ${pl.Series(["foo"]).str.toUpperCase()} | ${pl.Series(["FOO"])} + ${"lstrip"} | ${pl.Series([" foo"]).str.lstrip()} | ${pl.Series(["foo"])} + ${"rstrip"} | ${pl.Series(["foo "]).str.rstrip()} | ${pl.Series(["foo"])} + ${"toLowerCase"} | ${pl.Series(["FOO"]).str.toLowerCase()} | ${pl.Series(["foo"])} + ${"contains"} | ${pl.Series(["f1", "f0"]).str.contains(/[0]/)} | ${pl.Series([false, true])} + ${"lengths"} | ${pl.Series(["apple", "ham"]).str.lengths()} | ${pl.Series([5, 3])} + ${"slice"} | ${pl.Series(["apple", "ham"]).str.slice(1)} | ${pl.Series(["pple", "am"])} + `("$# $name expected matches actual", ({ expected, actual }) => { expect(expected).toStrictEqual(actual); }); @@ -709,8 +700,9 @@ describe("StringFunctions", () => { }); test("hex decode strict", () => { const s = pl.Series("encoded", ["666f6f", "626172", "invalid", null]); - const fn0 = () => s.str.decode("hex", true).alias("decoded"); - const fn1 = () => s.str.decode({encoding: "hex", strict: true}).alias("decoded"); + const fn0 = () => s.str.decode("hex", true).alias("decoded"); + const fn1 = () => + s.str.decode({ encoding: "hex", strict: true }).alias("decoded"); expect(fn0).toThrow(); expect(fn1).toThrow(); }); @@ -721,9 +713,15 @@ describe("StringFunctions", () => { expect(encoded).toSeriesEqual(expected); }); test("base64 decode strict", () => { - const s = pl.Series("encoded", ["Zm9v", "YmFy", "not base64 encoded", null]); - const fn0 = () => s.str.decode("base64", true).alias("decoded"); - const fn1 = () => s.str.decode({encoding: "base64", strict: true}).alias("decoded"); + const s = pl.Series("encoded", [ + "Zm9v", + "YmFy", + "not base64 encoded", + null, + ]); + const fn0 = () => s.str.decode("base64", true).alias("decoded"); + const fn1 = () => + s.str.decode({ encoding: "base64", strict: true }).alias("decoded"); expect(fn0).toThrow(); expect(fn1).toThrow(); }); @@ -731,7 +729,7 @@ describe("StringFunctions", () => { const s = pl.Series("encoded", ["Zm9v", "YmFy", "invalid", null]); const decoded = pl.Series("decoded", ["foo", "bar", null, null]); - const actual = s.str.decode("base64").alias("decoded"); + const actual = s.str.decode("base64").alias("decoded"); expect(actual).toSeriesEqual(decoded); }); }); diff --git a/__tests__/setup.ts b/__tests__/setup.ts index 8c6ed70da..68f0f411d 100644 --- a/__tests__/setup.ts +++ b/__tests__/setup.ts @@ -1,15 +1,14 @@ import pl from "@polars/index"; expect.extend({ - toSeriesStrictEqual(actual, expected){ + toSeriesStrictEqual(actual, expected) { const seriesEq = actual.seriesEqual(expected); const typesEq = actual.dtype.equals(expected.dtype); - if(seriesEq && typesEq) { + if (seriesEq && typesEq) { return { message: () => "series matches", - pass: true + pass: true, }; - } else { return { message: () => ` @@ -17,18 +16,17 @@ Expected: >>${expected} Received: >>${actual}`, - pass: false + pass: false, }; } }, toSeriesEqual(actual, expected) { const pass = actual.seriesEqual(expected); - if(pass) { + if (pass) { return { message: () => "series matches", - pass: true + pass: true, }; - } else { return { message: () => ` @@ -36,36 +34,35 @@ Expected: >>${expected} Received: >>${actual}`, - pass: false + pass: false, }; } }, toFrameEqual(actual, expected, nullEqual?) { const pass = actual.frameEqual(expected, nullEqual); - if(pass) { + if (pass) { return { message: () => "dataframes match", - pass: true + pass: true, }; } else { - return { message: () => ` Expected: >>${expected} Received: >>${actual}`, - pass: false + pass: false, }; } }, toFrameStrictEqual(actual, expected) { const frameEq = actual.frameEqual(expected); const dtypesEq = this.equals(actual.dtypes, expected.dtypes); - if(frameEq && dtypesEq) { + if (frameEq && dtypesEq) { return { message: () => "dataframes match", - pass: true + pass: true, }; } else { return { @@ -74,7 +71,7 @@ Expected: >>${expected} Received: >>${actual}`, - pass: false + pass: false, }; } }, @@ -82,47 +79,43 @@ Received: actual = actual.sort(actual.columns.sort()); expected = expected.sort(expected.columns.sort()); const pass = actual.frameEqual(expected); - if(pass) { + if (pass) { return { message: () => "dataframes match", - pass: true + pass: true, }; } else { - return { message: () => ` Expected: >>${expected} Received: >>${actual}`, - pass: false + pass: false, }; } - } + }, }); export const df = () => { - const df = pl.DataFrame( - { - "bools": [false, true, false], - "bools_nulls": [null, true, false], - "int": [1, 2, 3], - "int_nulls": [1, null, 3], - "bigint": [1n, 2n, 3n], - "bigint_nulls": [1n, null, 3n], - "floats": [1.0, 2.0, 3.0], - "floats_nulls": [1.0, null, 3.0], - "strings": ["foo", "bar", "ham"], - "strings_nulls": ["foo", null, "ham"], - "date": [new Date(), new Date(), new Date()], - "datetime": [13241324, 12341256, 12341234], - }); + const df = pl.DataFrame({ + bools: [false, true, false], + bools_nulls: [null, true, false], + int: [1, 2, 3], + int_nulls: [1, null, 3], + bigint: [1n, 2n, 3n], + bigint_nulls: [1n, null, 3n], + floats: [1.0, 2.0, 3.0], + floats_nulls: [1.0, null, 3.0], + strings: ["foo", "bar", "ham"], + strings_nulls: ["foo", null, "ham"], + date: [new Date(), new Date(), new Date()], + datetime: [13241324, 12341256, 12341234], + }); return df.withColumns( - pl.col("date").cast(pl.Date), pl.col("datetime").cast(pl.Datetime("ms")), - pl.col("strings").cast(pl.Categorical) - .alias("cat") + pl.col("strings").cast(pl.Categorical).alias("cat"), ); }; diff --git a/__tests__/struct.test.ts b/__tests__/struct.test.ts index e78d67a4b..8c695fa3e 100644 --- a/__tests__/struct.test.ts +++ b/__tests__/struct.test.ts @@ -1,11 +1,10 @@ - import pl from "@polars"; describe("struct", () => { test("series <--> array round trip", () => { const data = [ - {utf8: "a", f64: 1, }, - {utf8: "b", f64: 2, } + { utf8: "a", f64: 1 }, + { utf8: "b", f64: 2 }, ]; const name = "struct"; const s = pl.Series(name, data); @@ -13,10 +12,12 @@ describe("struct", () => { expect(s.toArray()).toEqual(data); }); test("pli.struct", () => { - const expected = pl.DataFrame({ - foo: [1], - bar: [2] - }).toStruct("foo"); + const expected = pl + .DataFrame({ + foo: [1], + bar: [2], + }) + .toStruct("foo"); const foo = pl.Series("foo", [1]); const bar = pl.Series("bar", [2]); const actual = pl.struct([foo, bar]).rename("foo"); @@ -25,7 +26,7 @@ describe("struct", () => { test("pli.struct dataframe", () => { const df = pl.DataFrame({ foo: [1], - bar: [2] + bar: [2], }); const actual = df .select(pl.struct(pl.cols("foo", "bar")).alias("s")) @@ -33,17 +34,18 @@ describe("struct", () => { expect(actual).toSeriesEqual(df.toStruct("s")); }); test("struct toArray", () => { - const actual = pl.DataFrame({ - foo: [1, 10, 100], - bar: [2, null, 200] - }) + const actual = pl + .DataFrame({ + foo: [1, 10, 100], + bar: [2, null, 200], + }) .toStruct("foobar") .toArray(); const expected = [ - {foo: 1, bar: 2}, - {foo: 10, bar: null}, - {foo: 100, bar: 200} + { foo: 1, bar: 2 }, + { foo: 10, bar: null }, + { foo: 100, bar: 200 }, ]; expect(actual).toEqual(expected); }); diff --git a/__tests__/whenthen.test.ts b/__tests__/whenthen.test.ts index 36d76b057..12f4ba6ea 100644 --- a/__tests__/whenthen.test.ts +++ b/__tests__/whenthen.test.ts @@ -1,4 +1,4 @@ -import pl, {col, lit, when} from "@polars"; +import pl, { col, lit, when } from "@polars"; describe("when", () => { test("when(a).then(b).otherwise(c)", () => { @@ -7,15 +7,17 @@ describe("when", () => { .otherwise(lit(-1)) .as("when"); - const actual = pl.DataFrame({ - "foo": [1, 3, 4], - "bar": [3, 4, 0] - }).withColumn(expr); + const actual = pl + .DataFrame({ + foo: [1, 3, 4], + bar: [3, 4, 0], + }) + .withColumn(expr); const expected = pl.DataFrame({ - "foo": [1, 3, 4], - "bar": [3, 4, 0], - "when": [-1, 1, 1] + foo: [1, 3, 4], + bar: [3, 4, 0], + when: [-1, 1, 1], }); expect(actual).toFrameEqual(expected); }); @@ -24,21 +26,22 @@ describe("when", () => { .then(lit("foo=1")) .when(col("foo").eq(3)) .then(lit("foo=3")) - .when(col("bar").eq(0) - .and(col("foo").eq(4))) + .when(col("bar").eq(0).and(col("foo").eq(4))) .then(lit("bar=0, foo=4")) .otherwise(lit("unknown")) .as("when"); - const actual = pl.DataFrame({ - "foo": [1, 3, 4, 5], - "bar": [3, 4, 0, 1] - }).withColumn(expr); + const actual = pl + .DataFrame({ + foo: [1, 3, 4, 5], + bar: [3, 4, 0, 1], + }) + .withColumn(expr); const expected = pl.DataFrame({ - "foo": [1, 3, 4, 5], - "bar": [3, 4, 0, 1], - "when": ["foo=1", "foo=3", "bar=0, foo=4", "unknown"] + foo: [1, 3, 4, 5], + bar: [3, 4, 0, 1], + when: ["foo=1", "foo=3", "bar=0, foo=4", "unknown"], }); expect(actual).toFrameEqual(expected); }); diff --git a/package.json b/package.json index 50f8dd4ff..2b02321c9 100644 --- a/package.json +++ b/package.json @@ -43,36 +43,27 @@ "build:ts": " rm -rf bin; tsc -p tsconfig.build.json", "cp:bin": "cp ./polars/*.node bin/", "format:rs": "cargo fmt", - "format:source": "prettier --config ./package.json --write './**/*.{js,ts}'", - "format:yaml": "prettier --parser yaml --write './**/*.{yml,yaml}'", - "lint:ts": "eslint -c ./.eslintrc.json 'polars/**/*.{ts,tsx,js}' '__tests__/*.ts'", + "lint:ts:fix": "rome check --apply-suggested {polars,__tests__} && rome format --write {polars,__tests__}", + "lint:ts": "rome check {polars,__tests__} && rome format {polars,__tests__}", + "lint": "yarn lint:ts && yarn format:rs", "prepublishOnly": "napi prepublish -t npm", "test": "jest", - "version": "napi version" + "version": "napi version", + "precommit": "yarn lint && yarn test" }, "devDependencies": { "@napi-rs/cli": "^2.14.1", "@types/chance": "^1.1.3", "@types/jest": "^27.0.3", "@types/node": "^16.11.9", - "@typescript-eslint/eslint-plugin": "^5.4.0", - "@typescript-eslint/parser": "^5.4.0", "chance": "^1.1.8", - "eslint": "^8.1.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.2.4", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^4.0.0", - "husky": "^7.0.4", "jest": "^27.3.1", - "lint-staged": "^11.2.6", - "prettier": "^2.4.1", + "rome": "^11.0.0", "source-map-support": "^0.5.21", "ts-jest": "^27.1.0", "ts-node": "^10.4.0", - "typedoc": "^0.22.9", - "typescript": "4.4.3" + "typedoc": "^0.23", + "typescript": "4.9" }, "packageManager": "yarn@3.3.1", "workspaces": [ diff --git a/polars/cfg.ts b/polars/cfg.ts index cd5a96506..ba1c6eeb8 100644 --- a/polars/cfg.ts +++ b/polars/cfg.ts @@ -1,24 +1,31 @@ import pli from "./native-polars"; + +/** + * Configure polars; offers options for table formatting and more. + */ export interface Config { /** Use utf8 characters to print tables */ - setUtf8Tables(): Config + setUtf8Tables(): Config; /** Use ascii characters to print tables */ - setAsciiTables(): Config + setAsciiTables(): Config; /** Set the number of character used to draw the table */ - setTblWidthChars(width: number): Config + setTblWidthChars(width: number): Config; /** Set the number of rows used to print tables */ - setTblRows(n: number): Config + setTblRows(n: number): Config; /** Set the number of columns used to print tables */ - setTblCols(n: number): Config + setTblCols(n: number): Config; /** Turn on the global string cache */ - setGlobalStringCache(): Config + setGlobalStringCache(): Config; /** Turn off the global string cache */ - unsetGlobalStringCache(): Config + unsetGlobalStringCache(): Config; } + +/** + * @ignore + */ export const Config: Config = { setUtf8Tables() { - - delete process.env["POLARS_FMT_NO_UTF8"]; + process.env["POLARS_FMT_NO_UTF8"] = undefined; return this; }, @@ -28,7 +35,6 @@ export const Config: Config = { return this; }, setTblWidthChars(width) { - process.env["POLARS_TABLE_WIDTH"] = String(width); return this; @@ -52,5 +58,5 @@ export const Config: Config = { pli.toggleStringCache(false); return this; - } + }, }; diff --git a/polars/dataframe.ts b/polars/dataframe.ts index feab1a7ae..de1e53921 100644 --- a/polars/dataframe.ts +++ b/polars/dataframe.ts @@ -1,19 +1,26 @@ import pli from "./internals/polars_internal"; -import {arrayToJsDataFrame} from "./internals/construction"; -import {DynamicGroupBy, GroupBy, RollingGroupBy} from "./groupby"; -import {LazyDataFrame, _LazyDataFrame} from "./lazy/dataframe"; -import {concat} from "./functions"; -import {Expr} from "./lazy/expr"; -import {Series, _Series} from "./series/series"; -import {Stream, Writable} from "stream"; +import { arrayToJsDataFrame } from "./internals/construction"; +import { DynamicGroupBy, _GroupBy, GroupBy, RollingGroupBy } from "./groupby"; +import { LazyDataFrame, _LazyDataFrame } from "./lazy/dataframe"; +import { concat } from "./functions"; +import { Expr } from "./lazy/expr"; +import { Series, _Series } from "./series"; +import { Stream, Writable } from "stream"; +import { + WriteCsvOptions, + WriteIPCOptions, + WriteParquetOptions, + WriteAvroOptions, + FillNullStrategy, + JoinOptions, +} from "./types"; -import {DataType, JoinBaseOptions} from "./datatypes"; +import { DataType } from "./datatypes"; import { columnOrColumns, columnOrColumnsStrict, ColumnSelection, - FillNullStrategy, isSeriesArray, ColumnsOrExpr, ValueOrArray, @@ -27,39 +34,14 @@ import { Sample, Serialize, } from "./shared_traits"; -import {col, element} from "./lazy/functions"; -const inspect = Symbol.for("nodejs.util.inspect.custom"); - -type WriteCsvOptions = { - hasHeader?: boolean; - sep?: string; -}; +import { col, element } from "./lazy/functions"; -type WriteJsonOptions = { - orient?: "row" | "col" | "dataframe"; - multiline?: boolean; -}; - -type WriteParquetOptions = { - compression?: - | "uncompressed" - | "snappy" - | "gzip" - | "lzo" - | "brotli" - | "lz4" - | "zstd"; -}; - -type WriteIPCOptions = { - compression?: "uncompressed" | "lz4" | "zstd"; -}; - -type WriteAvroOptions = { - compression?: "uncompressed" | "snappy" | "deflate"; -}; +const inspect = Symbol.for("nodejs.util.inspect.custom"); +/** + * Write methods for DataFrame + */ interface WriteMethods { /** * __Write DataFrame to comma-separated values file (csv).__ @@ -72,33 +54,34 @@ interface WriteMethods { * @param options.sep - Separate CSV fields with this symbol. _defaults to `,`_ * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.writeCSV() + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.writeCSV() * foo,bar,ham * 1,6,a * 2,7,b * 3,8,c * * // using a file path - * >>> df.head(1).writeCSV("./foo.csv") + * > df.head(1).writeCSV("./foo.csv") * // foo.csv * foo,bar,ham * 1,6,a * * // using a write stream - * >>> const writeStream = new Stream.Writable({ - * >>> write(chunk, encoding, callback) { - * >>> console.log("writeStream: %O', chunk.toString()); - * >>> callback(null); - * >>> } - * >>> }); - * >>> df.head(1).writeCSV(writeStream, {hasHeader: false}) + * > const writeStream = new Stream.Writable({ + * ... write(chunk, encoding, callback) { + * ... console.log("writeStream: %O', chunk.toString()); + * ... callback(null); + * ... } + * ... }); + * > df.head(1).writeCSV(writeStream, {hasHeader: false}) * writeStream: '1,6,a' * ``` + * @category IO */ writeCSV(): Buffer; writeCSV(options: WriteCsvOptions): Buffer; @@ -110,34 +93,36 @@ interface WriteMethods { * @param options.format - json | lines * @example * ``` - * >>> const df = pl.DataFrame({ - * >>> foo: [1,2,3], - * >>> bar: ['a','b','c'] - * >>> }) + * > const df = pl.DataFrame({ + * ... foo: [1,2,3], + * ... bar: ['a','b','c'] + * ... }) * * - * >>> df.writeJSON({format:"json"}) + * > df.writeJSON({format:"json"}) * `[ {"foo":1.0,"bar":"a"}, {"foo":2.0,"bar":"b"}, {"foo":3.0,"bar":"c"}]` * - * >>> df.writeJSON({format:"lines"}) + * > df.writeJSON({format:"lines"}) * `{"foo":1.0,"bar":"a"} * {"foo":2.0,"bar":"b"} * {"foo":3.0,"bar":"c"}` * * // writing to a file - * >>> df.writeJSON("/path/to/file.json", {format:'lines'}) + * > df.writeJSON("/path/to/file.json", {format:'lines'}) * ``` + * @category IO */ - writeJSON(options?: {format: "lines" | "json"}): Buffer; + writeJSON(options?: { format: "lines" | "json" }): Buffer; writeJSON( destination: string | Writable, - options?: {format: "lines" | "json"} + options?: { format: "lines" | "json" }, ): void; /** * Write to Arrow IPC binary stream, or a feather file. * @param file File path to which the file should be written. * @param options.compression Compression method *defaults to "uncompressed"* - * */ + * @category IO + */ writeIPC(options?: WriteIPCOptions): Buffer; writeIPC(destination: string | Writable, options?: WriteIPCOptions): void; @@ -145,110 +130,108 @@ interface WriteMethods { * Write the DataFrame disk in parquet format. * @param file File path to which the file should be written. * @param options.compression Compression method *defaults to "uncompressed"* - * */ + * @category IO + */ writeParquet(options?: WriteParquetOptions): Buffer; writeParquet( destination: string | Writable, - options?: WriteParquetOptions + options?: WriteParquetOptions, ): void; /** * Write the DataFrame disk in avro format. * @param file File path to which the file should be written. * @param options.compression Compression method *defaults to "uncompressed"* - * + * @category IO */ writeAvro(options?: WriteAvroOptions): Buffer; writeAvro(destination: string | Writable, options?: WriteAvroOptions): void; } /** + * A DataFrame is a two-dimensional data structure that represents data as a table + * with rows and columns. + * + * @param data - Object, Array, or Series + * Two-dimensional data in various forms. object must contain Arrays. + * Array may contain Series or other Arrays. + * @param columns - Array of str, default undefined + * Column labels to use for resulting DataFrame. If specified, overrides any + * labels already present in the data. Must match data dimensions. + * @param orient - 'col' | 'row' default undefined + * Whether to interpret two-dimensional data as columns or as rows. If None, + * the orientation is inferred by matching the columns and data dimensions. If + * this does not yield conclusive results, column orientation is used. + * @example + * Constructing a DataFrame from an object : + * ``` + * > data = {'a': [1n, 2n], 'b': [3, 4]} + * > df = pl.DataFrame(data) + * > df + * shape: (2, 2) + * ╭─────┬─────╮ + * │ a ┆ b │ + * │ --- ┆ --- │ + * │ u64 ┆ i64 │ + * ╞═════╪═════╡ + * │ 1 ┆ 3 │ + * ├╌╌╌╌╌┼╌╌╌╌╌┤ + * │ 2 ┆ 4 │ + * ╰─────┴─────╯ + * ``` + * Notice that the dtype is automatically inferred as a polars Int64: + * ``` + * > df.dtypes + * ['UInt64', `Int64'] + * ``` + * In order to specify dtypes for your columns, initialize the DataFrame with a list + * of Series instead: + * ``` + * > data = [pl.Series('col1', [1, 2], pl.Float32), + * ... pl.Series('col2', [3, 4], pl.Int64)] + * > df2 = pl.DataFrame(series) + * > df2 + * shape: (2, 2) + * ╭──────┬──────╮ + * │ col1 ┆ col2 │ + * │ --- ┆ --- │ + * │ f32 ┆ i64 │ + * ╞══════╪══════╡ + * │ 1 ┆ 3 │ + * ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2 ┆ 4 │ + * ╰──────┴──────╯ + * ``` * - A DataFrame is a two-dimensional data structure that represents data as a table - with rows and columns. - - Parameters - ---------- - @param data - Object, Array, or Series - Two-dimensional data in various forms. object must contain Arrays. - Array may contain Series or other Arrays. - @param columns - Array of str, default undefined - Column labels to use for resulting DataFrame. If specified, overrides any - labels already present in the data. Must match data dimensions. - @param orient - 'col' | 'row' default undefined - Whether to interpret two-dimensional data as columns or as rows. If None, - the orientation is inferred by matching the columns and data dimensions. If - this does not yield conclusive results, column orientation is used. - Examples - -------- - Constructing a DataFrame from an object : - ``` - data = {'a': [1n, 2n], 'b': [3, 4]} - df = pl.DataFrame(data) - df - shape: (2, 2) - ╭─────┬─────╮ - │ a ┆ b │ - │ --- ┆ --- │ - │ u64 ┆ i64 │ - ╞═════╪═════╡ - │ 1 ┆ 3 │ - ├╌╌╌╌╌┼╌╌╌╌╌┤ - │ 2 ┆ 4 │ - ╰─────┴─────╯ - ``` - Notice that the dtype is automatically inferred as a polars Int64: - ``` - df.dtypes - ['UInt64', `Int64'] - ``` - In order to specify dtypes for your columns, initialize the DataFrame with a list - of Series instead: - ``` - data = [pl.Series('col1', [1, 2], pl.Float32), - ... pl.Series('col2', [3, 4], pl.Int64)] - df2 = pl.DataFrame(series) - df2 - shape: (2, 2) - ╭──────┬──────╮ - │ col1 ┆ col2 │ - │ --- ┆ --- │ - │ f32 ┆ i64 │ - ╞══════╪══════╡ - │ 1 ┆ 3 │ - ├╌╌╌╌╌╌┼╌╌╌╌╌╌┤ - │ 2 ┆ 4 │ - ╰──────┴──────╯ - ``` - - Constructing a DataFrame from a list of lists, row orientation inferred: - ``` - data = [[1, 2, 3], [4, 5, 6]] - df4 = pl.DataFrame(data, ['a', 'b', 'c']) - df4 - shape: (2, 3) - ╭─────┬─────┬─────╮ - │ a ┆ b ┆ c │ - │ --- ┆ --- ┆ --- │ - │ i64 ┆ i64 ┆ i64 │ - ╞═════╪═════╪═════╡ - │ 1 ┆ 2 ┆ 3 │ - ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┤ - │ 4 ┆ 5 ┆ 6 │ - ╰─────┴─────┴─────╯ - ``` + * Constructing a DataFrame from a list of lists, row orientation inferred: + * ``` + * > data = [[1, 2, 3], [4, 5, 6]] + * > df4 = pl.DataFrame(data, ['a', 'b', 'c']) + * > df4 + * shape: (2, 3) + * ╭─────┬─────┬─────╮ + * │ a ┆ b ┆ c │ + * │ --- ┆ --- ┆ --- │ + * │ i64 ┆ i64 ┆ i64 │ + * ╞═════╪═════╪═════╡ + * │ 1 ┆ 2 ┆ 3 │ + * ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┤ + * │ 4 ┆ 5 ┆ 6 │ + * ╰─────┴─────┴─────╯ + * ``` */ export interface DataFrame extends Arithmetic, - Sample, - WriteMethods, - Serialize, - GroupByOps { + Sample, + Arithmetic, + WriteMethods, + Serialize, + GroupByOps { /** @ignore */ _df: any; dtypes: DataType[]; height: number; - shape: {height: number; width: number}; + shape: { height: number; width: number }; width: number; get columns(): string[]; set columns(cols: string[]); @@ -265,12 +248,12 @@ export interface DataFrame * ___ * Example * ``` - * > df = pl.DataFrame({ - * > 'a': [1.0, 2.8, 3.0], - * > 'b': [4, 5, 6], - * > "c": [True, False, True] - * > }) - * > df.describe() + * > df = pl.DataFrame({ + * ... 'a': [1.0, 2.8, 3.0], + * ... 'b': [4, 5, 6], + * ... "c": [True, False, True] + * ... }) + * ... df.describe() * shape: (5, 4) * ╭──────────┬───────┬─────┬──────╮ * │ describe ┆ a ┆ b ┆ c │ @@ -298,13 +281,13 @@ export interface DataFrame * @param name * @example * ``` - * > df = pl.DataFrame({ - * > "foo": [1, 2, 3], - * > "bar": [6.0, 7.0, 8.0], - * > "ham": ['a', 'b', 'c'], - * > "apple": ['a', 'b', 'c'] - * > }) - * > df.drop(['ham', 'apple']) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6.0, 7.0, 8.0], + * ... "ham": ['a', 'b', 'c'], + * ... "apple": ['a', 'b', 'c'] + * ... }) + * > df.drop(['ham', 'apple']) * shape: (3, 2) * ╭─────┬─────╮ * │ foo ┆ bar │ @@ -317,9 +300,7 @@ export interface DataFrame * ├╌╌╌╌╌┼╌╌╌╌╌┤ * │ 3 ┆ 8 │ * ╰─────┴─────╯ - * * ``` - * */ drop(name: string): DataFrame; drop(names: string[]): DataFrame; @@ -331,12 +312,12 @@ export interface DataFrame * ___ * @example * ``` - * > df = pl.DataFrame({ - * > "foo": [1, 2, 3], - * > "bar": [6, null, 8], - * > "ham": ['a', 'b', 'c'] - * > }) - * > df.dropNulls() + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, null, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.dropNulls() * shape: (2, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -358,11 +339,11 @@ export interface DataFrame * @param columns - column or columns to explode * @example * ``` - * > df = pl.DataFrame({ - * > "letters": ["c", "c", "a", "c", "a", "b"], - * > "nrs": [[1, 2], [1, 3], [4, 3], [5, 5, 5], [6], [2, 1, 2]] - * > }) - * > console.log(df) + * > df = pl.DataFrame({ + * ... "letters": ["c", "c", "a", "c", "a", "b"], + * ... "nrs": [[1, 2], [1, 3], [4, 3], [5, 5, 5], [6], [2, 1, 2]] + * ... }) + * > df * shape: (6, 2) * ╭─────────┬────────────╮ * │ letters ┆ nrs │ @@ -381,7 +362,7 @@ export interface DataFrame * ├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ * │ "b" ┆ [2, 1, 2] │ * ╰─────────┴────────────╯ - * > df.explode("nrs") + * > df.explode("nrs") * shape: (13, 2) * ╭─────────┬─────╮ * │ letters ┆ nrs │ @@ -455,13 +436,13 @@ export interface DataFrame * @param predicate - Expression that evaluates to a boolean Series. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> // Filter on one condition - * >>> df.filter(pl.col("foo").lt(3)) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * // Filter on one condition + * > df.filter(pl.col("foo").lt(3)) * shape: (2, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -472,11 +453,11 @@ export interface DataFrame * ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┤ * │ 2 ┆ 7 ┆ b │ * └─────┴─────┴─────┘ - * >>> // Filter on multiple conditions - * >>> df.filter( - * pl.col("foo").lt(3) - * .and(pl.col("ham").eq("a")) - * ) + * // Filter on multiple conditions + * > df.filter( + * ... pl.col("foo").lt(3) + * ... .and(pl.col("ham").eq("a")) + * ... ) * shape: (1, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -494,12 +475,12 @@ export interface DataFrame * @param name -Name of the column to find. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.findIdxByName("ham")) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.findIdxByName("ham")) * 2 * ``` */ @@ -519,39 +500,39 @@ export interface DataFrame * @returns Series * @example * ``` - * >>> // A horizontal sum operation - * >>> df = pl.DataFrame({ - * >>> "a": [2, 1, 3], - * >>> "b": [1, 2, 3], - * >>> "c": [1.0, 2.0, 3.0] - * >>> }) - * >>> df.fold((s1, s2) => s1.plus(s2)) + * > // A horizontal sum operation + * > df = pl.DataFrame({ + * ... "a": [2, 1, 3], + * ... "b": [1, 2, 3], + * ... "c": [1.0, 2.0, 3.0] + * ... }) + * > df.fold((s1, s2) => s1.plus(s2)) * Series: 'a' [f64] * [ * 4 * 5 * 9 * ] - * >>> // A horizontal minimum operation - * >>> df = pl.DataFrame({ - * >>> "a": [2, 1, 3], - * >>> "b": [1, 2, 3], - * >>> "c": [1.0, 2.0, 3.0] - * >>> }) - * >>> df.fold((s1, s2) => s1.zipWith(s1.lt(s2), s2)) + * > // A horizontal minimum operation + * > df = pl.DataFrame({ + * ... "a": [2, 1, 3], + * ... "b": [1, 2, 3], + * ... "c": [1.0, 2.0, 3.0] + * ... }) + * > df.fold((s1, s2) => s1.zipWith(s1.lt(s2), s2)) * Series: 'a' [f64] * [ * 1 * 1 * 3 * ] - * >>> // A horizontal string concatenation - * >>> df = pl.DataFrame({ - * >>> "a": ["foo", "bar", 2], - * >>> "b": [1, 2, 3], - * >>> "c": [1.0, 2.0, 3.0] - * >>> }) - * >>> df.fold((s1, s2) => s.plus(s2)) + * > // A horizontal string concatenation + * > df = pl.DataFrame({ + * ... "a": ["foo", "bar", 2], + * ... "b": [1, 2, 3], + * ... "c": [1.0, 2.0, 3.0] + * ... }) + * > df.fold((s1, s2) => s.plus(s2)) * Series: '' [f64] * [ * "foo11" @@ -569,19 +550,19 @@ export interface DataFrame * @param options.nullEqual Consider null values as equal. * @example * ``` - * >>> df1 = pl.DataFrame({ - * >> "foo": [1, 2, 3], - * >> "bar": [6.0, 7.0, 8.0], - * >> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df2 = pl.DataFrame({ - * >>> "foo": [3, 2, 1], - * >>> "bar": [8.0, 7.0, 6.0], - * >>> "ham": ['c', 'b', 'a'] - * >>> }) - * >>> df1.frameEqual(df1) + * > df1 = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6.0, 7.0, 8.0], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df2 = pl.DataFrame({ + * ... "foo": [3, 2, 1], + * ... "bar": [8.0, 7.0, 6.0], + * ... "ham": ['c', 'b', 'a'] + * ... }) + * > df1.frameEqual(df1) * true - * >>> df1.frameEqual(df2) + * > df1.frameEqual(df2) * false * ``` */ @@ -621,12 +602,12 @@ export interface DataFrame * @param length - Length of the head. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3, 4, 5], - * >>> "bar": [6, 7, 8, 9, 10], - * >>> "ham": ['a', 'b', 'c', 'd','e'] - * >>> }) - * >>> df.head(3) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3, 4, 5], + * ... "bar": [6, 7, 8, 9, 10], + * ... "ham": ['a', 'b', 'c', 'd','e'] + * ... }) + * > df.head(3) * shape: (3, 3) * ╭─────┬─────┬─────╮ * │ foo ┆ bar ┆ ham │ @@ -648,13 +629,13 @@ export interface DataFrame * @param inPlace - Modify in place * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> x = pl.Series("apple", [10, 20, 30]) - * >>> df.hStack([x]) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > x = pl.Series("apple", [10, 20, 30]) + * > df.hStack([x]) * shape: (3, 4) * ╭─────┬─────┬─────┬───────╮ * │ foo ┆ bar ┆ ham ┆ apple │ @@ -705,16 +686,16 @@ export interface DataFrame * @see {@link JoinOptions} * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6.0, 7.0, 8.0], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> otherDF = pl.DataFrame({ - * >>> "apple": ['x', 'y', 'z'], - * >>> "ham": ['a', 'b', 'd'] - * >>> }) - * >>> df.join(otherDF, {on: 'ham'}) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6.0, 7.0, 8.0], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > otherDF = pl.DataFrame({ + * ... "apple": ['x', 'y', 'z'], + * ... "ham": ['a', 'b', 'd'] + * ... }) + * > df.join(otherDF, {on: 'ham'}) * shape: (2, 4) * ╭─────┬─────┬─────┬───────╮ * │ foo ┆ bar ┆ ham ┆ apple │ @@ -729,106 +710,105 @@ export interface DataFrame */ join( other: DataFrame, - options: {on: ValueOrArray} & JoinBaseOptions + options: { on: ValueOrArray } & Omit< + JoinOptions, + "leftOn" | "rightOn" + >, ): DataFrame; join( other: DataFrame, options: { leftOn: ValueOrArray; rightOn: ValueOrArray; - } & JoinBaseOptions + } & Omit, ): DataFrame; - join(other: DataFrame, options: {how: "cross"; suffix?: string}): DataFrame; - - /** - * Perform an asof join. This is similar to a left-join except that we - * match on nearest key rather than equal keys. - * - * Both DataFrames must be sorted by the asof_join key. - * - For each row in the left DataFrame: - - - A "backward" search selects the last row in the right DataFrame whose - 'on' key is less than or equal to the left's key. - - - A "forward" search selects the first row in the right DataFrame whose - 'on' key is greater than or equal to the left's key. - - The default is "backward". - - Parameters - ---------- - @param other DataFrame to join with. - @param options.leftOn Join column of the left DataFrame. - @param options.rightOn Join column of the right DataFrame. - @param options.on Join column of both DataFrames. If set, `leftOn` and `rightOn` should be undefined. - @param options.byLeft join on these columns before doing asof join - @param options.byRight join on these columns before doing asof join - @param options.strategy One of {'forward', 'backward'} - @param options.suffix Suffix to append to columns with a duplicate name. - @param options.tolerance - Numeric tolerance. By setting this the join will only be done if the near keys are within this distance. - If an asof join is done on columns of dtype "Date", "Datetime" you - use the following string language: - - - 1ns *(1 nanosecond)* - - 1us *(1 microsecond)* - - 1ms *(1 millisecond)* - - 1s *(1 second)* - - 1m *(1 minute)* - - 1h *(1 hour)* - - 1d *(1 day)* - - 1w *(1 week)* - - 1mo *(1 calendar month)* - - 1y *(1 calendar year)* - - 1i *(1 index count)* - - Or combine them: - - "3d12h4m25s" # 3 days, 12 hours, 4 minutes, and 25 seconds - @param options.allowParallel Allow the physical plan to optionally evaluate the computation of both DataFrames up to the join in parallel. - @param options.forceParallel Force the physical plan to evaluate the computation of both DataFrames up to the join in parallel. + join(other: DataFrame, options: { how: "cross"; suffix?: string }): DataFrame; - - @example - ``` - >>> const gdp = pl.DataFrame({ - ... date: [ - ... new Date('2016-01-01'), - ... new Date('2017-01-01'), - ... new Date('2018-01-01'), - ... new Date('2019-01-01'), - ... ], // note record date: Jan 1st (sorted!) - ... gdp: [4164, 4411, 4566, 4696], - ... }) - >>> const population = pl.DataFrame({ - ... date: [ - ... new Date('2016-05-12'), - ... new Date('2017-05-12'), - ... new Date('2018-05-12'), - ... new Date('2019-05-12'), - ... ], // note record date: May 12th (sorted!) - ... "population": [82.19, 82.66, 83.12, 83.52], - ... }) - >>> population.joinAsof( - ... gdp, - ... {leftOn:"date", rightOn:"date", strategy:"backward"} - ... ) - shape: (4, 3) - ┌─────────────────────┬────────────┬──────┐ - │ date ┆ population ┆ gdp │ - │ --- ┆ --- ┆ --- │ - │ datetime[μs] ┆ f64 ┆ i64 │ - ╞═════════════════════╪════════════╪══════╡ - │ 2016-05-12 00:00:00 ┆ 82.19 ┆ 4164 │ - ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ - │ 2017-05-12 00:00:00 ┆ 82.66 ┆ 4411 │ - ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ - │ 2018-05-12 00:00:00 ┆ 83.12 ┆ 4566 │ - ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ - │ 2019-05-12 00:00:00 ┆ 83.52 ┆ 4696 │ - └─────────────────────┴────────────┴──────┘ - ``` - */ + /** + * Perform an asof join. This is similar to a left-join except that we + * match on nearest key rather than equal keys. + * + * Both DataFrames must be sorted by the asofJoin key. + * + * For each row in the left DataFrame: + * - A "backward" search selects the last row in the right DataFrame whose + * 'on' key is less than or equal to the left's key. + * + * - A "forward" search selects the first row in the right DataFrame whose + * 'on' key is greater than or equal to the left's key. + * + * The default is "backward". + * + * @param other DataFrame to join with. + * @param options.leftOn Join column of the left DataFrame. + * @param options.rightOn Join column of the right DataFrame. + * @param options.on Join column of both DataFrames. If set, `leftOn` and `rightOn` should be undefined. + * @param options.byLeft join on these columns before doing asof join + * @param options.byRight join on these columns before doing asof join + * @param options.strategy One of 'forward', 'backward' + * @param options.suffix Suffix to append to columns with a duplicate name. + * @param options.tolerance + * Numeric tolerance. By setting this the join will only be done if the near keys are within this distance. + * If an asof join is done on columns of dtype "Date", "Datetime" you + * use the following string language: + * + * - 1ns *(1 nanosecond)* + * - 1us *(1 microsecond)* + * - 1ms *(1 millisecond)* + * - 1s *(1 second)* + * - 1m *(1 minute)* + * - 1h *(1 hour)* + * - 1d *(1 day)* + * - 1w *(1 week)* + * - 1mo *(1 calendar month)* + * - 1y *(1 calendar year)* + * - 1i *(1 index count)* + * + * Or combine them: + * - "3d12h4m25s" # 3 days, 12 hours, 4 minutes, and 25 seconds + * @param options.allowParallel Allow the physical plan to optionally evaluate the computation of both DataFrames up to the join in parallel. + * @param options.forceParallel Force the physical plan to evaluate the computation of both DataFrames up to the join in parallel. + * + * @example + * ``` + * > const gdp = pl.DataFrame({ + * ... date: [ + * ... new Date('2016-01-01'), + * ... new Date('2017-01-01'), + * ... new Date('2018-01-01'), + * ... new Date('2019-01-01'), + * ... ], // note record date: Jan 1st (sorted!) + * ... gdp: [4164, 4411, 4566, 4696], + * ... }) + * > const population = pl.DataFrame({ + * ... date: [ + * ... new Date('2016-05-12'), + * ... new Date('2017-05-12'), + * ... new Date('2018-05-12'), + * ... new Date('2019-05-12'), + * ... ], // note record date: May 12th (sorted!) + * ... "population": [82.19, 82.66, 83.12, 83.52], + * ... }) + * > population.joinAsof( + * ... gdp, + * ... {leftOn:"date", rightOn:"date", strategy:"backward"} + * ... ) + * shape: (4, 3) + * ┌─────────────────────┬────────────┬──────┐ + * │ date ┆ population ┆ gdp │ + * │ --- ┆ --- ┆ --- │ + * │ datetime[μs] ┆ f64 ┆ i64 │ + * ╞═════════════════════╪════════════╪══════╡ + * │ 2016-05-12 00:00:00 ┆ 82.19 ┆ 4164 │ + * ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2017-05-12 00:00:00 ┆ 82.66 ┆ 4411 │ + * ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2018-05-12 00:00:00 ┆ 83.12 ┆ 4566 │ + * ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2019-05-12 00:00:00 ┆ 83.52 ┆ 4696 │ + * └─────────────────────┴────────────┴──────┘ + * ``` + */ joinAsof( other: DataFrame, options: { @@ -843,7 +823,7 @@ export interface DataFrame tolerance?: number | string; allowParallel?: boolean; forceParallel?: boolean; - } + }, ): DataFrame; lazy(): LazyDataFrame; /** @@ -859,12 +839,12 @@ export interface DataFrame * @param axis - either 0 or 1 * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.max() + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.max() * shape: (1, 3) * ╭─────┬─────┬──────╮ * │ foo ┆ bar ┆ ham │ @@ -894,12 +874,12 @@ export interface DataFrame * ___ * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.median() + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.median() * shape: (1, 3) * ╭─────┬─────┬──────╮ * │ foo ┆ bar ┆ ham │ @@ -919,13 +899,13 @@ export interface DataFrame * @param valueVars - Values to use as value variables. * @example * ``` - * >>> df1 = pl.DataFrame({ - * >>> 'id': [1], - * >>> 'asset_key_1': ['123'], - * >>> 'asset_key_2': ['456'], - * >>> 'asset_key_3': ['abc'], - * >>> }) - * >>> df1.melt('id', ['asset_key_1', 'asset_key_2', 'asset_key_3']) + * > df1 = pl.DataFrame({ + * ... 'id': [1], + * ... 'asset_key_1': ['123'], + * ... 'asset_key_2': ['456'], + * ... 'asset_key_3': ['abc'], + * ... }) + * > df1.melt('id', ['asset_key_1', 'asset_key_2', 'asset_key_3']) * shape: (3, 3) * ┌─────┬─────────────┬───────┐ * │ id ┆ variable ┆ value │ @@ -947,12 +927,12 @@ export interface DataFrame * @param axis - either 0 or 1 * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.min() + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.min() * shape: (1, 3) * ╭─────┬─────┬──────╮ * │ foo ┆ bar ┆ ham │ @@ -975,12 +955,12 @@ export interface DataFrame * ___ * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, null, 3], - * >>> "bar": [6, 7, null], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.nullCount() + * > df = pl.DataFrame({ + * ... "foo": [1, null, 3], + * ... "bar": [6, 7, null], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.nullCount() * shape: (1, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -996,53 +976,49 @@ export interface DataFrame partitionBy( cols: string | string[], stable: boolean, - mapFn: (df: DataFrame) => T + mapFn: (df: DataFrame) => T, ): T[]; /** * - Create a spreadsheet-style pivot table as a DataFrame. - - Parameters - ---------- - @param values Column values to aggregate. Can be multiple columns if the *columns* arguments contains multiple columns as well - @param options.index One or multiple keys to group by - @param options.columns Columns whose values will be used as the header of the output DataFrame - @param options.aggregateFunc - Any of: - - - "sum" - - "max" - - "min" - - "mean" - - "median" - - "first" - - "last" - - "count" - Defaults to "first" - @param options.maintainOrder Sort the grouped keys so that the output order is predictable. - @param options.sortColumns Sort the transposed columns by name. Default is by order of discovery. - @example - -``` - >>> df = pl.DataFrame( - ... { - ... "foo": ["one", "one", "one", "two", "two", "two"], - ... "bar": ["A", "B", "C", "A", "B", "C"], - ... "baz": [1, 2, 3, 4, 5, 6], - ... } - ... ) - >>> df.pivot({values:"baz", index:"foo", columns:"bar"}) - shape: (2, 4) - ┌─────┬─────┬─────┬─────┐ - │ foo ┆ A ┆ B ┆ C │ - │ --- ┆ --- ┆ --- ┆ --- │ - │ str ┆ i64 ┆ i64 ┆ i64 │ - ╞═════╪═════╪═════╪═════╡ - │ one ┆ 1 ┆ 2 ┆ 3 │ - ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┤ - │ two ┆ 4 ┆ 5 ┆ 6 │ - └─────┴─────┴─────┴─────┘ - ``` + * Create a spreadsheet-style pivot table as a DataFrame. + * + * @param values Column values to aggregate. Can be multiple columns if the *columns* arguments contains multiple columns as well + * @param options.index One or multiple keys to group by + * @param options.columns Columns whose values will be used as the header of the output DataFrame + * @param options.aggregateFunc + * Any of: + * - "sum" + * - "max" + * - "min" + * - "mean" + * - "median" + * - "first" + * - "last" + * - "count" + * Defaults to "first" + * @param options.maintainOrder Sort the grouped keys so that the output order is predictable. + * @param options.sortColumns Sort the transposed columns by name. Default is by order of discovery. + * @example + * ``` + * > df = pl.DataFrame( + * ... { + * ... "foo": ["one", "one", "one", "two", "two", "two"], + * ... "bar": ["A", "B", "C", "A", "B", "C"], + * ... "baz": [1, 2, 3, 4, 5, 6], + * ... } + * ... ) + * > df.pivot({values:"baz", index:"foo", columns:"bar"}) + * shape: (2, 4) + * ┌─────┬─────┬─────┬─────┐ + * │ foo ┆ A ┆ B ┆ C │ + * │ --- ┆ --- ┆ --- ┆ --- │ + * │ str ┆ i64 ┆ i64 ┆ i64 │ + * ╞═════╪═════╪═════╪═════╡ + * │ one ┆ 1 ┆ 2 ┆ 3 │ + * ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┤ + * │ two ┆ 4 ┆ 5 ┆ 6 │ + * └─────┴─────┴─────┴─────┘ + * ``` */ pivot( values: string | string[], @@ -1050,6 +1026,24 @@ export interface DataFrame index: string | string[]; columns: string | string[]; aggregateFunc?: + | "sum" + | "max" + | "min" + | "mean" + | "median" + | "first" + | "last" + | "count" + | Expr; + maintainOrder?: boolean; + sortColumns?: boolean; + }, + ): DataFrame; + pivot(options: { + values: string | string[]; + index: string | string[]; + columns: string | string[]; + aggregateFunc?: | "sum" | "max" | "min" @@ -1059,24 +1053,6 @@ export interface DataFrame | "last" | "count" | Expr; - maintainOrder?: boolean; - sortColumns?: boolean; - } - ): DataFrame; - pivot(options: { - values: string | string[]; - index: string | string[]; - columns: string | string[]; - aggregateFunc?: - | "sum" - | "max" - | "min" - | "mean" - | "median" - | "first" - | "last" - | "count" - | Expr; maintainOrder?: boolean; sortColumns?: boolean; }): DataFrame; @@ -1089,12 +1065,12 @@ export interface DataFrame * Aggregate the columns of this DataFrame to their quantile value. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.quantile(0.5) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.quantile(0.5) * shape: (1, 3) * ╭─────┬─────┬──────╮ * │ foo ┆ bar ┆ ham │ @@ -1119,12 +1095,12 @@ export interface DataFrame * @param mapping - Key value pairs that map from old name to new name. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.rename({"foo": "apple"}) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.rename({"foo": "apple"}) * ╭───────┬─────┬─────╮ * │ apple ┆ bar ┆ ham │ * │ --- ┆ --- ┆ --- │ @@ -1146,13 +1122,13 @@ export interface DataFrame * @param newColumn - New column to insert * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> x = pl.Series("apple", [10, 20, 30]) - * >>> df.replaceAtIdx(0, x) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > x = pl.Series("apple", [10, 20, 30]) + * > df.replaceAtIdx(0, x) * shape: (3, 3) * ╭───────┬─────┬─────╮ * │ apple ┆ bar ┆ ham │ @@ -1173,12 +1149,12 @@ export interface DataFrame * @param index - row index * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.row(2) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.row(2) * [3, 8, 'c'] * ``` */ @@ -1194,12 +1170,12 @@ export interface DataFrame * @param columns - Column or columns to select. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.select('foo') + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.select('foo') * shape: (3, 1) * ┌─────┐ * │ foo │ @@ -1222,12 +1198,12 @@ export interface DataFrame * @param periods - Number of places to shift (may be negative). * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.shift(1) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.shift(1) * shape: (3, 3) * ┌──────┬──────┬──────┐ * │ foo ┆ bar ┆ ham │ @@ -1240,7 +1216,7 @@ export interface DataFrame * ├╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤ * │ 2 ┆ 7 ┆ "b" │ * └──────┴──────┴──────┘ - * >>> df.shift(-1) + * > df.shift(-1) * shape: (3, 3) * ┌──────┬──────┬──────┐ * │ foo ┆ bar ┆ ham │ @@ -1256,7 +1232,7 @@ export interface DataFrame * ``` */ shift(periods: number): DataFrame; - shift({periods}: {periods: number}): DataFrame; + shift({ periods }: { periods: number }): DataFrame; /** * Shift the values by a given period and fill the parts that will be empty due to this operation * with the result of the `fill_value` expression. @@ -1266,12 +1242,12 @@ export interface DataFrame * @param opts.fillValue - fill null values with this value. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.shiftAndFill({periods:1, fill_value:0}) + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.shiftAndFill({periods:1, fill_value:0}) * shape: (3, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -1299,7 +1275,7 @@ export interface DataFrame */ shrinkToFit(): DataFrame; shrinkToFit(inPlace: true): void; - shrinkToFit({inPlace}: {inPlace: true}): void; + shrinkToFit({ inPlace }: { inPlace: true }): void; /** * Slice this DataFrame over the rows direction. * ___ @@ -1308,12 +1284,12 @@ export interface DataFrame * @param opts.length - Length of the slice * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6.0, 7.0, 8.0], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.slice(1, 2) // Alternatively `df.slice({offset:1, length:2})` + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6.0, 7.0, 8.0], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.slice(1, 2) // Alternatively `df.slice({offset:1, length:2})` * shape: (2, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -1326,7 +1302,7 @@ export interface DataFrame * └─────┴─────┴─────┘ * ``` */ - slice({offset, length}: {offset: number; length: number}): DataFrame; + slice({ offset, length }: { offset: number; length: number }): DataFrame; slice(offset: number, length: number): DataFrame; /** * Sort the DataFrame by column. @@ -1335,18 +1311,18 @@ export interface DataFrame * @param reverse - Reverse/descending sort. */ sort(by: ColumnsOrExpr, reverse?: boolean): DataFrame; - sort({by, reverse}: {by: ColumnsOrExpr; reverse?: boolean}): DataFrame; + sort({ by, reverse }: { by: ColumnsOrExpr; reverse?: boolean }): DataFrame; /** * Aggregate the columns of this DataFrame to their standard deviation value. * ___ * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.std() + * > df = pl.DataFrame({ + * ... "foo": [1, 2, 3], + * ... "bar": [6, 7, 8], + * ... "ham": ['a', 'b', 'c'] + * ... }) + * > df.std() * shape: (1, 3) * ╭─────┬─────┬──────╮ * │ foo ┆ bar ┆ ham │ @@ -1373,11 +1349,11 @@ export interface DataFrame * * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "letters": ["c", "c", "a", "c", "a", "b"], - * >>> "nrs": [1, 2, 3, 4, 5, 6] - * >>> }) - * >>> df + * > df = pl.DataFrame({ + * ... "letters": ["c", "c", "a", "c", "a", "b"], + * ... "nrs": [1, 2, 3, 4, 5, 6] + * ... }) + * > df * shape: (6, 2) * ╭─────────┬─────╮ * │ letters ┆ nrs │ @@ -1396,10 +1372,9 @@ export interface DataFrame * ├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┤ * │ "b" ┆ 6 │ * ╰─────────┴─────╯ - * >>> df.groupby("letters") - * >>> .tail(2) - * >>> .sort("letters") - * >>> + * > df.groupby("letters") + * ... .tail(2) + * ... .sort("letters") * shape: (5, 2) * ╭─────────┬─────╮ * │ letters ┆ nrs │ @@ -1419,78 +1394,92 @@ export interface DataFrame * ``` */ tail(length?: number): DataFrame; - /** @deprecated *since 0.4.0* use {@link writeCSV} */ + /** + * @deprecated *since 0.4.0* use {@link writeCSV} + * @category Deprecated + */ toCSV(destOrOptions?, options?); /** * Converts dataframe object into row oriented javascript objects * @example * ``` - * >>> df.toRecords() + * > df.toRecords() * [ * {"foo":1.0,"bar":"a"}, * {"foo":2.0,"bar":"b"}, * {"foo":3.0,"bar":"c"} * ] * ``` + * @category IO */ toRecords(): Record[]; - /** compat with `JSON.stringify` */ + /** + * compat with `JSON.stringify` + * @category IO + */ toJSON(): string; /** * Converts dataframe object into column oriented javascript objects * @example * ``` - * >>> df.toObject() + * > df.toObject() * { * "foo": [1,2,3], * "bar": ["a", "b", "c"] * } * ``` + * @category IO */ toObject(): Record; - /** @deprecated *since 0.4.0* use {@link writeIPC} */ + /** + * @deprecated *since 0.4.0* use {@link writeIPC} + * @category IO Deprecated + */ toIPC(destination?, options?); - /** @deprecated *since 0.4.0* use {@link writeParquet} */ + /** + * @deprecated *since 0.4.0* use {@link writeParquet} + * @category IO Deprecated + */ toParquet(destination?, options?); toSeries(index?: number): Series; toString(): string; /** - Convert a ``DataFrame`` to a ``Series`` of type ``Struct`` - @param name Name for the struct Series - @example - ``` - >>> df = pl.DataFrame({ - ... "a": [1, 2, 3, 4, 5], - ... "b": ["one", "two", "three", "four", "five"], - ... }) - >>> df.toStruct("nums") - shape: (5,) - Series: 'nums' [struct[2]{'a': i64, 'b': str}] - [ - {1,"one"} - {2,"two"} - {3,"three"} - {4,"four"} - {5,"five"} - ] - ``` - */ + * Convert a ``DataFrame`` to a ``Series`` of type ``Struct`` + * @param name Name for the struct Series + * @example + * ``` + * > df = pl.DataFrame({ + * ... "a": [1, 2, 3, 4, 5], + * ... "b": ["one", "two", "three", "four", "five"], + * ... }) + * > df.toStruct("nums") + * shape: (5,) + * Series: 'nums' [struct[2]{'a': i64, 'b': str}] + * [ + * {1,"one"} + * {2,"two"} + * {3,"three"} + * {4,"four"} + * {5,"five"} + * ] + * ``` + */ toStruct(name: string): Series; /** * Transpose a DataFrame over the diagonal. * - * @note This is a very expensive operation. Perhaps you can do it differently. + * @remarks This is a very expensive operation. Perhaps you can do it differently. * @param options * @param options.includeHeader If set, the column names will be added as first column. * @param options.headerName If `includeHeader` is set, this determines the name of the column that will be inserted * @param options.columnNames Optional generator/iterator that yields column names. Will be used to replace the columns in the DataFrame. * * @example - * >>> df = pl.DataFrame({"a": [1, 2, 3], "b": [1, 2, 3]}) - * >>> df.transpose({includeHeader:true}) + * > df = pl.DataFrame({"a": [1, 2, 3], "b": [1, 2, 3]}) + * > df.transpose({includeHeader:true}) * shape: (2, 4) * ┌────────┬──────────┬──────────┬──────────┐ * │ column ┆ column_0 ┆ column_1 ┆ column_2 │ @@ -1502,7 +1491,7 @@ export interface DataFrame * │ b ┆ 1 ┆ 2 ┆ 3 │ * └────────┴──────────┴──────────┴──────────┘ * // replace the auto generated column names with a list - * >>> df.transpose({includeHeader:false, columnNames:["a", "b", "c"]}) + * > df.transpose({includeHeader:false, columnNames:["a", "b", "c"]}) * shape: (2, 3) * ┌─────┬─────┬─────┐ * │ a ┆ b ┆ c │ @@ -1515,7 +1504,7 @@ export interface DataFrame * └─────┴─────┴─────┘ * * // Include the header as a separate column - * >>> df.transpose({ + * > df.transpose({ * ... includeHeader:true, * ... headerName:"foo", * ... columnNames:["a", "b", "c"] @@ -1532,14 +1521,14 @@ export interface DataFrame * └─────┴─────┴─────┴─────┘ * * // Replace the auto generated column with column names from a generator function - * >>> function *namesGenerator() { + * > function *namesGenerator() { * ... const baseName = "my_column_"; * ... let count = 0; * ... let name = `${baseName}_${count}`; * ... count++; * ... yield name; * ... } - * >>> df.transpose({includeHeader:false, columnNames:namesGenerator}) + * > df.transpose({includeHeader:false, columnNames:namesGenerator}) * shape: (2, 3) * ┌─────────────┬─────────────┬─────────────┐ * │ my_column_0 ┆ my_column_1 ┆ my_column_2 │ @@ -1566,7 +1555,7 @@ export interface DataFrame unique( maintainOrder?: boolean, subset?: ColumnSelection, - keep?: "first" | "last" + keep?: "first" | "last", ): DataFrame; unique(opts: { maintainOrder?: boolean; @@ -1579,7 +1568,7 @@ export interface DataFrame @param names Names of the struct columns that will be decomposed by its fields @example ``` - >>> df = pl.DataFrame({ + > df = pl.DataFrame({ ... "int": [1, 2], ... "str": ["a", "b"], ... "bool": [true, null], @@ -1587,7 +1576,7 @@ export interface DataFrame ... }) ... .toStruct("my_struct") ... .toFrame() - >>> df + > df shape: (2, 1) ┌─────────────────────────────┐ │ my_struct │ @@ -1598,7 +1587,7 @@ export interface DataFrame ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ {2,"b",null,[3]} │ └─────────────────────────────┘ - >>> df.unnest("my_struct") + > df.unnest("my_struct") shape: (2, 4) ┌─────┬─────┬──────┬────────────┐ │ int ┆ str ┆ bool ┆ list │ @@ -1616,12 +1605,12 @@ export interface DataFrame * Aggregate the columns of this DataFrame to their variance value. * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.var() + * > df = pl.DataFrame({ + * > "foo": [1, 2, 3], + * > "bar": [6, 7, 8], + * > "ham": ['a', 'b', 'c'] + * > }) + * > df.var() * shape: (1, 3) * ╭─────┬─────┬──────╮ * │ foo ┆ bar ┆ ham │ @@ -1638,17 +1627,17 @@ export interface DataFrame * @param df - DataFrame to stack. * @example * ``` - * >>> df1 = pl.DataFrame({ - * >>> "foo": [1, 2], - * >>> "bar": [6, 7], - * >>> "ham": ['a', 'b'] - * >>> }) - * >>> df2 = pl.DataFrame({ - * >>> "foo": [3, 4], - * >>> "bar": [8 , 9], - * >>> "ham": ['c', 'd'] - * >>> }) - * >>> df1.vstack(df2) + * > df1 = pl.DataFrame({ + * ... "foo": [1, 2], + * ... "bar": [6, 7], + * ... "ham": ['a', 'b'] + * ... }) + * > df2 = pl.DataFrame({ + * ... "foo": [3, 4], + * ... "bar": [8 , 9], + * ... "ham": ['c', 'd'] + * ... }) + * > df1.vstack(df2) * shape: (4, 3) * ╭─────┬─────┬─────╮ * │ foo ┆ bar ┆ ham │ @@ -1679,7 +1668,7 @@ export interface DataFrame * @param newName */ withColumnRenamed(existing: string, replacement: string): DataFrame; - withColumnRenamed(opts: {existing: string; replacement: string}): DataFrame; + withColumnRenamed(opts: { existing: string; replacement: string }): DataFrame; /** * Add a column at index 0 that counts the rows. * @param name - name of the column to add @@ -1768,7 +1757,7 @@ export const _DataFrame = (_df: any): DataFrame => { } else { return s; } - }) + }), ); }; const summary = concat([ @@ -1780,7 +1769,7 @@ export const _DataFrame = (_df: any): DataFrame => { ]); summary.insertAtIdx( 0, - Series("describe", ["mean", "std", "min", "max", "median"]) + Series("describe", ["mean", "std", "min", "max", "median"]), ); return summary; @@ -1824,7 +1813,7 @@ export const _DataFrame = (_df: any): DataFrame => { if (opts.subset) { opts.subset = [opts.subset].flat(3); } - const o = {...defaultOptions, ...opts}; + const o = { ...defaultOptions, ...opts }; return wrap("unique", o.maintainOrder, o.subset, o.keep); }, @@ -1832,14 +1821,13 @@ export const _DataFrame = (_df: any): DataFrame => { return _DataFrame(_df) .lazy() .explode(columns) - .collectSync({noOptimization: true}); + .collectSync({ noOptimization: true }); }, extend(other) { return wrap("extend", (other as any).inner()); }, filter(predicate) { - return this.lazy().filter(predicate) - .collectSync(); + return this.lazy().filter(predicate).collectSync(); }, fillNull(strategy) { return wrap("fillNull", strategy); @@ -1864,7 +1852,7 @@ export const _DataFrame = (_df: any): DataFrame => { return _df.getColumns().map(_Series) as any; }, groupBy(...by) { - return GroupBy(_df as any, columnOrColumnsStrict(by)); + return _GroupBy(_df as any, columnOrColumnsStrict(by)); }, groupByRolling(opts) { return RollingGroupBy( @@ -1873,7 +1861,7 @@ export const _DataFrame = (_df: any): DataFrame => { opts.period, opts.offset, opts.closed, - opts.by + opts.by, ); }, groupByDynamic({ @@ -1895,19 +1883,19 @@ export const _DataFrame = (_df: any): DataFrame => { truncate, includeBoundaries, closed, - by + by, ); }, hashRows(obj: any = 0n, k1 = 1n, k2 = 2n, k3 = 3n) { if (typeof obj === "number" || typeof obj === "bigint") { return _Series( - _df.hashRows(BigInt(obj), BigInt(k1), BigInt(k2), BigInt(k3)) + _df.hashRows(BigInt(obj), BigInt(k1), BigInt(k2), BigInt(k3)), ); } - const o = {k0: obj, k1: k1, k2: k2, k3: k3, ...obj}; + const o = { k0: obj, k1: k1, k2: k2, k3: k3, ...obj }; return _Series( - _df.hashRows(BigInt(o.k0), BigInt(o.k1), BigInt(o.k2), BigInt(o.k3)) + _df.hashRows(BigInt(o.k0), BigInt(o.k1), BigInt(o.k2), BigInt(o.k3)), ) as any; }, head(length = 5) { @@ -1921,7 +1909,7 @@ export const _DataFrame = (_df: any): DataFrame => { return wrap( method, - columns.map((col) => col.inner()) + columns.map((col) => col.inner()), ); }, insertAtIdx(idx, series) { @@ -1934,7 +1922,7 @@ export const _DataFrame = (_df: any): DataFrame => { isEmpty: () => _df.height === 0, isUnique: () => _Series(_df.isUnique()) as any, join(other: DataFrame, options): DataFrame { - options = {how: "inner", suffix: "right", ...options}; + options = { how: "inner", suffix: "right", ...options }; const on = columnOrColumns(options.on); const how = options.how; const suffix = options.suffix; @@ -1950,7 +1938,7 @@ export const _DataFrame = (_df: any): DataFrame => { } if ((leftOn && !rightOn) || (rightOn && !leftOn)) { throw new TypeError( - "You should pass the column to join on as an argument." + "You should pass the column to join on as an argument.", ); } @@ -2019,30 +2007,25 @@ export const _DataFrame = (_df: any): DataFrame => { if (Expr.isExpr(aggregateFunc)) { fn = aggregateFunc; } else { - fn = { - first: element().first(), - sum: element().sum(), - max: element().max(), - min: element().min(), - mean: element().mean(), - median: element().median(), - last: element().last(), - count: element().count() - }[aggregateFunc] ?? new Error(`Unknown aggregate function ${aggregateFunc}`); + fn = + { + first: element().first(), + sum: element().sum(), + max: element().max(), + min: element().min(), + mean: element().mean(), + median: element().median(), + last: element().last(), + count: element().count(), + }[aggregateFunc] ?? + new Error(`Unknown aggregate function ${aggregateFunc}`); if (fn instanceof Error) { throw fn; } } return _DataFrame( - _df.pivotExpr( - values, - index, - columns, - fn, - maintainOrder, - sortColumns - ) + _df.pivotExpr(values, index, columns, fn, maintainOrder, sortColumns), ); }, quantile(quantile, interpolation = "nearest") { @@ -2072,6 +2055,7 @@ export const _DataFrame = (_df: any): DataFrame => { return _df.toRows(); }, sample(opts?, frac?, withReplacement = false, seed?) { + // rome-ignore lint/style/noArguments: if (arguments.length === 0) { return wrap("sampleN", 1, withReplacement, false, seed); } @@ -2091,9 +2075,7 @@ export const _DataFrame = (_df: any): DataFrame => { select(...selection) { const hasExpr = selection.flat().some((s) => Expr.isExpr(s)); if (hasExpr) { - return _DataFrame(_df).lazy() - .select(selection) - .collectSync(); + return _DataFrame(_df).lazy().select(selection).collectSync(); } else { return wrap("select", columnOrColumnsStrict(selection as any)); } @@ -2130,7 +2112,7 @@ export const _DataFrame = (_df: any): DataFrame => { return _DataFrame(_df) .lazy() .sort(arg, reverse) - .collectSync({noOptimization: true, stringCache: false}); + .collectSync({ noOptimization: true, stringCache: false }); } return wrap("sort", arg, reverse, true); @@ -2186,7 +2168,7 @@ export const _DataFrame = (_df: any): DataFrame => { return acc; }, {}); }, - writeJSON(dest?, options = {format: "lines"}) { + writeJSON(dest?, options = { format: "lines" }) { if (dest instanceof Writable || typeof dest === "string") { return _df.writeJson(dest, options) as any; } @@ -2198,7 +2180,7 @@ export const _DataFrame = (_df: any): DataFrame => { }, }); - _df.writeJson(writeStream, {...options, ...dest}); + _df.writeJson(writeStream, { ...options, ...dest }); writeStream.end(""); return Buffer.concat(buffers); @@ -2206,7 +2188,7 @@ export const _DataFrame = (_df: any): DataFrame => { toParquet(dest?, options?) { return this.writeParquet(dest, options); }, - writeParquet(dest?, options = {compression: "uncompressed"}) { + writeParquet(dest?, options = { compression: "uncompressed" }) { if (dest instanceof Writable || typeof dest === "string") { return _df.writeParquet(dest, options.compression) as any; } @@ -2223,7 +2205,7 @@ export const _DataFrame = (_df: any): DataFrame => { return Buffer.concat(buffers); }, - writeAvro(dest?, options = {compression: "uncompressed"}) { + writeAvro(dest?, options = { compression: "uncompressed" }) { if (dest instanceof Writable || typeof dest === "string") { return _df.writeAvro(dest, options.compression) as any; } @@ -2243,7 +2225,7 @@ export const _DataFrame = (_df: any): DataFrame => { toIPC(dest?, options?) { return this.writeIPC(dest, options); }, - writeIPC(dest?, options = {compression: "uncompressed"}) { + writeIPC(dest?, options = { compression: "uncompressed" }) { if (dest instanceof Writable || typeof dest === "string") { return _df.writeIpc(dest, options.compression) as any; } @@ -2271,7 +2253,7 @@ export const _DataFrame = (_df: any): DataFrame => { let df = wrap( "transpose", options?.includeHeader ?? false, - options?.headerName + options?.headerName, ); if (options?.columnNames) { function* namesIter() { @@ -2287,11 +2269,11 @@ export const _DataFrame = (_df: any): DataFrame => { } const newColumns = Array.from( - {length: df.width}, + { length: df.width }, ( (i) => () => i.next().value - )(namesIter()) + )(namesIter()), ); df.columns = newColumns; @@ -2325,19 +2307,19 @@ export const _DataFrame = (_df: any): DataFrame => { if (isSeriesArray(columns)) { return columns.reduce( (acc, curr) => acc.withColumn(curr), - _DataFrame(_df) + _DataFrame(_df), ); } else { return this.lazy() .withColumns(columns) - .collectSync({noOptimization: true, stringCache: false}); + .collectSync({ noOptimization: true, stringCache: false }); } }, withColumnRenamed(opt, replacement?) { if (typeof opt === "string") { - return this.rename({[opt]: replacement}); + return this.rename({ [opt]: replacement }); } else { - return this.rename({[opt.existing]: opt.replacement}); + return this.rename({ [opt.existing]: opt.replacement }); } }, withRowCount(name = "row_nr") { @@ -2400,8 +2382,33 @@ export const _DataFrame = (_df: any): DataFrame => { }); }; +/** + * DataFrame constructor + */ export interface DataFrameConstructor extends Deserialize { + /** + * Create an empty DataFrame + */ (): DataFrame; + /** + * Create a DataFrame from a JavaScript object + * @example + * ``` + * data = {'a': [1n, 2n], 'b': [3, 4]} + * df = pl.DataFrame(data) + * df + * shape: (2, 2) + * ╭─────┬─────╮ + * │ a ┆ b │ + * │ --- ┆ --- │ + * │ u64 ┆ i64 │ + * ╞═════╪═════╡ + * │ 1 ┆ 3 │ + * ├╌╌╌╌╌┼╌╌╌╌╌┤ + * │ 2 ┆ 4 │ + * ╰─────┴─────╯ + * ``` + */ ( data: any, options?: { @@ -2409,7 +2416,7 @@ export interface DataFrameConstructor extends Deserialize { orient?: "row" | "col"; schema?: Record; inferSchemaLength?: number; - } + }, ): DataFrame; isDataFrame(arg: any): arg is DataFrame; } @@ -2445,5 +2452,5 @@ export const DataFrame: DataFrameConstructor = Object.assign( isDataFrame, deserialize: (buf, fmt) => _DataFrame(pli.JsDataFrame.deserialize(buf, fmt)), - } + }, ); diff --git a/polars/datatypes/datatype.ts b/polars/datatypes/datatype.ts index de9ec7541..77663f6e3 100644 --- a/polars/datatypes/datatype.ts +++ b/polars/datatypes/datatype.ts @@ -1,4 +1,4 @@ -import {Field} from "./field"; +import { Field } from "./field"; export abstract class DataType { get variant() { @@ -90,7 +90,10 @@ export abstract class DataType { */ public static Datetime(timeUnit: TimeUnit, timeZone?): DataType; public static Datetime(timeUnit: "ms" | "ns" | "us", timeZone?): DataType; - public static Datetime(timeUnit, timeZone: string | null | undefined = null): DataType { + public static Datetime( + timeUnit, + timeZone: string | null | undefined = null, + ): DataType { return new _Datetime(timeUnit, timeZone as any); } /** @@ -102,10 +105,13 @@ export abstract class DataType { public static List(inner: DataType): DataType { return new _List(inner); } + /** + * Struct type + */ public static Struct(fields: Field[]): DataType; - public static Struct(fields: {[key: string]: DataType}): DataType; + public static Struct(fields: { [key: string]: DataType }): DataType; public static Struct( - fields: Field[] | {[key: string]: DataType} + fields: Field[] | { [key: string]: DataType }, ): DataType { return new _Struct(fields); } @@ -115,11 +121,10 @@ export abstract class DataType { } toString() { - if(this.inner) { + if (this.inner) { return `${this.identity}(${this.variant}(${this.inner}))`; } else { return `${this.identity}(${this.variant})`; - } } toJSON() { @@ -141,38 +146,37 @@ export abstract class DataType { return this.toJSON(); } static from(obj): DataType { - return null as any; } } -class _Null extends DataType { } -class _Bool extends DataType { } -class _Int8 extends DataType { } -class _Int16 extends DataType { } -class _Int32 extends DataType { } -class _Int64 extends DataType { } -class _UInt8 extends DataType { } -class _UInt16 extends DataType { } -class _UInt32 extends DataType { } -class _UInt64 extends DataType { } -class _Float32 extends DataType { } -class _Float64 extends DataType { } -class _Date extends DataType { } -class _Time extends DataType { } -class _Object extends DataType { } -class _Utf8 extends DataType { } +class _Null extends DataType {} +class _Bool extends DataType {} +class _Int8 extends DataType {} +class _Int16 extends DataType {} +class _Int32 extends DataType {} +class _Int64 extends DataType {} +class _UInt8 extends DataType {} +class _UInt16 extends DataType {} +class _UInt32 extends DataType {} +class _UInt64 extends DataType {} +class _Float32 extends DataType {} +class _Float64 extends DataType {} +class _Date extends DataType {} +class _Time extends DataType {} +class _Object extends DataType {} +class _Utf8 extends DataType {} + +class _Categorical extends DataType {} -class _Categorical extends DataType { } +/** + * Datetime type + */ class _Datetime extends DataType { - constructor( - private timeUnit: TimeUnit, - private timeZone?: string - ) { + constructor(private timeUnit: TimeUnit, private timeZone?: string) { super(); } override get inner() { - return [this.timeUnit, this.timeZone]; } @@ -210,9 +214,9 @@ class _Struct extends DataType { constructor( inner: | { - [name: string]: DataType; - } - | Field[] + [name: string]: DataType; + } + | Field[], ) { super(); if (Array.isArray(inner)) { @@ -240,44 +244,72 @@ class _Struct extends DataType { override toJSON() { return { [this.identity]: { - [this.variant]: this.fields - } + [this.variant]: this.fields, + }, } as any; } } +/** + * Datetime time unit + */ export enum TimeUnit { Nanoseconds = "ns", Microseconds = "us", Milliseconds = "ms", } +/** + * @ignore + * Timeunit namespace + */ export namespace TimeUnit { export function from(s: "ms" | "ns" | "us"): TimeUnit { return TimeUnit[s]; } } -import util from "util"; + +/** + * Datatype namespace + */ export namespace DataType { + /** Null */ export type Null = _Null; - + /** Boolean */ export type Bool = _Bool; + /** Int8 */ export type Int8 = _Int8; + /** Int16 */ export type Int16 = _Int16; + /** Int32 */ export type Int32 = _Int32; + /** Int64 */ export type Int64 = _Int64; + /** UInt8 */ export type UInt8 = _UInt8; + /** UInt16 */ export type UInt16 = _UInt16; + /** UInt32 */ export type UInt32 = _UInt32; + /** UInt64 */ export type UInt64 = _UInt64; + /** Float32 */ export type Float32 = _Float32; + /** Float64 */ export type Float64 = _Float64; + /** Date dtype */ export type Date = _Date; + /** Datetime */ export type Datetime = _Datetime; + /** Utf8 */ export type Utf8 = _Utf8; + /** Categorical */ export type Categorical = _Categorical; + /** List */ export type List = _List; + /** Struct */ export type Struct = _Struct; + /** * deserializes a datatype from the serde output of rust polars `DataType` * @param dtype dtype object @@ -287,15 +319,16 @@ export namespace DataType { return DataType[dtype]; } - let {variant, inner} = dtype; - if(variant === "Struct") { - inner = [inner[0].map(fld => Field.from(fld.name, deserialize(fld.dtype)))]; + let { variant, inner } = dtype; + if (variant === "Struct") { + inner = [ + inner[0].map((fld) => Field.from(fld.name, deserialize(fld.dtype))), + ]; } - if(variant === "List") { + if (variant === "List") { inner = [deserialize(inner[0])]; } return DataType[variant](...inner); - } } diff --git a/polars/datatypes/field.ts b/polars/datatypes/field.ts index 0e9b04162..05d43321f 100644 --- a/polars/datatypes/field.ts +++ b/polars/datatypes/field.ts @@ -1,12 +1,15 @@ import { DataType } from "./datatype"; +/** + * A field is a name and a datatype. + */ export interface Field { - name: string, - dtype: DataType + name: string; + dtype: DataType; } -export class Field { - constructor(public name: string, public dtype: DataType) { } +export class Field implements Field { + constructor(public name: string, public dtype: DataType) {} toString() { return `Field("${this.name}", ${this.dtype})`; } @@ -24,7 +27,7 @@ export class Field { export namespace Field { export function from(name: string, dtype: DataType): Field; export function from([string, DataType]): Field; - export function from(obj: {name: string; dtype: DataType}): Field; + export function from(obj: { name: string; dtype: DataType }): Field; export function from(nameOrObj, dtype?: DataType): Field { if (typeof nameOrObj === "string") { return new Field(nameOrObj, dtype!); @@ -32,7 +35,6 @@ export namespace Field { return new Field(nameOrObj[0], nameOrObj[1]); } else { return new Field(nameOrObj.name, nameOrObj.dtype); - } } } diff --git a/polars/datatypes/index.ts b/polars/datatypes/index.ts index a476af932..6a8e0c773 100644 --- a/polars/datatypes/index.ts +++ b/polars/datatypes/index.ts @@ -1,53 +1,36 @@ -import {DataType} from "./datatype"; -export {DataType}; +import { DataType, TimeUnit } from "./datatype"; +export { DataType, TimeUnit }; +export { Field } from "./field"; import pli from "../internals/polars_internal"; +/** @ignore */ +export type TypedArray = + | Int8Array + | Int16Array + | Int32Array + | BigInt64Array + | Uint8Array + | Uint16Array + | Uint32Array + | BigInt64Array + | Float32Array + | Float64Array; -export type TypedArray = Int8Array | Int16Array | Int32Array | BigInt64Array | Uint8Array | Uint16Array | Uint32Array | BigInt64Array | Float32Array | Float64Array; - - +/** + * @ignore + */ export type Optional = T | undefined | null; -export enum _DataType { - Int8, - Int16, - Int32, - Int64, - UInt8, - UInt16, - UInt32, - UInt64, - Float32, - Float64, - Bool, - Utf8, - List, - Date, - Datetime, - Time, - Object, - Categorical, - Struct -} - +/** + * @ignore + */ export type JsDataFrame = any; export type NullValues = string | Array | Record; -export type JoinBaseOptions = { - how?: "left" | "inner" | "outer" | "semi" | "anti" | "cross" - suffix?: string; -} - -export type JoinOptions = { - leftOn?: string | Array; - rightOn?: string | Array; - on?: string | Array; - how?: "left" | "inner" | "outer" | "semi" | "anti" | "cross" - suffix?: string; -}; - - +/** + * @ignore + */ export const DTYPE_TO_FFINAME = { Int8: "I8", Int16: "I16", @@ -121,12 +104,12 @@ const POLARS_TYPE_TO_CONSTRUCTOR: Record = { }, }; +/** @ignore */ export const polarsTypeToConstructor = (dtype: DataType): CallableFunction => { - const constructor = POLARS_TYPE_TO_CONSTRUCTOR[dtype.variant]; - if (!constructor) { + const ctor = POLARS_TYPE_TO_CONSTRUCTOR[dtype.variant]; + if (!ctor) { throw new Error(`Cannot construct Series for type ${dtype.variant}.`); } - - return constructor; + return ctor; }; diff --git a/polars/error.ts b/polars/error.ts index 68640932f..ba8fbf02f 100644 --- a/polars/error.ts +++ b/polars/error.ts @@ -1,12 +1,10 @@ export class InvalidOperationError extends RangeError { - constructor(method, dtype) { super(`Invalid operation: ${method} is not supported for ${dtype}`); } } export class NotImplemented extends Error { - constructor(method, dtype) { super(`Invalid operation: ${method} is not supported for ${dtype}`); } diff --git a/polars/functions.ts b/polars/functions.ts index 80bfed7fd..815b571f2 100644 --- a/polars/functions.ts +++ b/polars/functions.ts @@ -1,11 +1,10 @@ /* eslint-disable no-redeclare */ -import {jsTypeToPolarsType} from "./internals/construction"; -import {Series, _Series} from "./series/series"; -import {DataFrame, _DataFrame} from "./dataframe"; +import { jsTypeToPolarsType } from "./internals/construction"; +import { Series, _Series } from "./series"; +import { DataFrame, _DataFrame } from "./dataframe"; import pli from "./internals/polars_internal"; -import {isDataFrameArray, isSeriesArray} from "./utils"; - -type ConcatOptions = {rechunk?: boolean, how?: "vertical" | "horizontal"} +import { isDataFrameArray, isSeriesArray } from "./utils"; +import { ConcatOptions } from "./types"; /** * _Repeat a single value n times and collect into a Series._ @@ -16,13 +15,13 @@ type ConcatOptions = {rechunk?: boolean, how?: "vertical" | "horizontal"} * * ``` * - * > const s = pl.repeat("a", 5) - * > s.toArray() + * > const s = pl.repeat("a", 5) + * > s.toArray() * ["a", "a", "a", "a", "a"] * * ``` */ -export function repeat(value: V, n: number, name= ""): Series { +export function repeat(value: V, n: number, name = ""): Series { const dtype = jsTypeToPolarsType(value); const s = pli.JsSeries.repeat(name, value, n, dtype); @@ -38,9 +37,9 @@ export function repeat(value: V, n: number, name= ""): Series { * - Horizontal: Stacks Series horizontally and fills with nulls if the lengths don't match. * * @example - * >>> const df1 = pl.DataFrame({"a": [1], "b": [3]}) - * >>> const df2 = pl.DataFrame({"a": [2], "b": [4]}) - * >>> pl.concat([df1, df2]) + * > const df1 = pl.DataFrame({"a": [1], "b": [3]}) + * > const df2 = pl.DataFrame({"a": [2], "b": [4]}) + * > pl.concat([df1, df2]) * shape: (2, 2) * ┌─────┬─────┐ * │ a ┆ b │ @@ -52,20 +51,28 @@ export function repeat(value: V, n: number, name= ""): Series { * │ 2 ┆ 4 │ * └─────┴─────┘ */ -export function concat(items: Array, options?: ConcatOptions): DataFrame; -export function concat(items: Array, options?: {rechunk: boolean}): Series; -export function concat(items, options: ConcatOptions = {rechunk: true, how: "vertical"}) { - const {rechunk, how} = options; +export function concat( + items: Array, + options?: ConcatOptions, +): DataFrame; +export function concat( + items: Array, + options?: { rechunk: boolean }, +): Series; +export function concat( + items, + options: ConcatOptions = { rechunk: true, how: "vertical" }, +) { + const { rechunk, how } = options; - if(!items.length) { + if (!items.length) { throw new RangeError("cannot concat empty list"); } - if(isDataFrameArray(items)) { + if (isDataFrameArray(items)) { let df; - if(how === "vertical") { - df = items.reduce((acc, curr) => acc.vstack(curr)); - + if (how === "vertical") { + df = items.reduce((acc, curr) => acc.vstack(curr)); } else { df = _DataFrame(pli.horizontalConcat(items.map((i: any) => i.inner()))); } @@ -73,8 +80,8 @@ export function concat(items, options: ConcatOptions = {rechunk: true, how: return rechunk ? df.rechunk() : df; } - if(isSeriesArray(items)) { - const s = items.reduce((acc, curr) => acc.concat(curr)); + if (isSeriesArray(items)) { + const s = items.reduce((acc, curr) => acc.concat(curr)); return rechunk ? s.rechunk() : s; } diff --git a/polars/groupby.ts b/polars/groupby.ts index ed69ee11b..a3a2f034e 100644 --- a/polars/groupby.ts +++ b/polars/groupby.ts @@ -1,24 +1,22 @@ -import {DataFrame, _DataFrame} from "./dataframe"; +import { DataFrame, _DataFrame } from "./dataframe"; import * as utils from "./utils"; import util from "util"; -import {Expr} from "./lazy/expr"; -import {col, exclude} from "./lazy/functions"; -import pli from "./internals/polars_internal"; -import {ColumnsOrExpr, selectionToExprList} from "./utils"; - +import { Expr } from "./lazy/expr"; +import { col, exclude } from "./lazy/functions"; +import { ColumnsOrExpr } from "./utils"; const inspect = Symbol.for("nodejs.util.inspect.custom"); -const inspectOpts = {colors:true, depth:null}; +const inspectOpts = { colors: true, depth: null }; /** * Starts a new GroupBy operation. */ export interface GroupBy { - [inspect](): string, + [inspect](): string; /** * Aggregate the groups into Series. */ - aggList(): DataFrame + aggList(): DataFrame; /** * __Use multiple aggregations on columns.__ * This can be combined with complete lazy API and is considered idiomatic polars. @@ -31,46 +29,46 @@ export interface GroupBy { * @example * ``` * // use lazy api rest parameter style - * >>> df.groupBy('foo', 'bar') - * >>> .agg(pl.sum('ham'), col('spam').tail(4).sum()) + * > df.groupBy('foo', 'bar') + * > .agg(pl.sum('ham'), col('spam').tail(4).sum()) * * // use lazy api array style - * >>> df.groupBy('foo', 'bar') - * >>> .agg([pl.sum('ham'), col('spam').tail(4).sum()]) + * > df.groupBy('foo', 'bar') + * > .agg([pl.sum('ham'), col('spam').tail(4).sum()]) * * // use a mapping - * >>> df.groupBy('foo', 'bar') - * >>> .agg({'spam': ['sum', 'min']}) + * > df.groupBy('foo', 'bar') + * > .agg({'spam': ['sum', 'min']}) * * ``` */ - agg(...columns: Expr[]): DataFrame - agg(columns: Record): DataFrame + agg(...columns: Expr[]): DataFrame; + agg(columns: Record): DataFrame; /** * Count the number of values in each group. */ - count(): DataFrame + count(): DataFrame; /** * Aggregate the first values in the group. */ - first(): DataFrame + first(): DataFrame; /** * Return a `DataFrame` with: * - the groupby keys * - the group indexes aggregated as lists */ - groups(): DataFrame + groups(): DataFrame; /** * Return first n rows of each group. * @param n -Number of values of the group to select * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "letters": ["c", "c", "a", "c", "a", "b"], - * >>> "nrs": [1, 2, 3, 4, 5, 6] - * >>> }) - * >>> df + * > df = pl.DataFrame({ + * > "letters": ["c", "c", "a", "c", "a", "b"], + * > "nrs": [1, 2, 3, 4, 5, 6] + * > }) + * > df * shape: (6, 2) * ╭─────────┬─────╮ * │ letters ┆ nrs │ @@ -89,10 +87,10 @@ export interface GroupBy { * ├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┤ * │ "b" ┆ 6 │ * ╰─────────┴─────╯ - * >>> df.groupby("letters") - * >>> .head(2) - * >>> .sort("letters"); - * >>> + * > df.groupby("letters") + * > .head(2) + * > .sort("letters"); + * > >> * shape: (5, 2) * ╭─────────┬─────╮ * │ letters ┆ nrs │ @@ -111,75 +109,71 @@ export interface GroupBy { * ╰─────────┴─────╯ * ``` */ - head(n?: number): DataFrame + head(n?: number): DataFrame; /** * Aggregate the last values in the group. */ - last(): DataFrame + last(): DataFrame; /** * Reduce the groups to the maximal value. */ - max(): DataFrame + max(): DataFrame; /** * Reduce the groups to the mean values. */ - mean(): DataFrame + mean(): DataFrame; /** * Return the median per group. */ - median(): DataFrame + median(): DataFrame; /** * Reduce the groups to the minimal value. */ - min(): DataFrame + min(): DataFrame; /** * Count the unique values per group. */ - nUnique(): DataFrame + nUnique(): DataFrame; /** * Do a pivot operation based on the group key, a pivot column and an aggregation function on the values column. * @param pivotCol - Column to pivot. * @param valuesCol - Column that will be aggregated. * */ - pivot({pivotCol, valuesCol}: {pivotCol: string, valuesCol: string}): PivotOps - pivot(pivotCol: string, valuesCol: string): PivotOps + pivot({ + pivotCol, + valuesCol, + }: { pivotCol: string; valuesCol: string }): PivotOps; + pivot(pivotCol: string, valuesCol: string): PivotOps; /** * Compute the quantile per group. */ - quantile(quantile: number): DataFrame + quantile(quantile: number): DataFrame; /** * Reduce the groups to the sum. */ - sum(): DataFrame - tail(n?: number): DataFrame - toString(): string - - + sum(): DataFrame; + tail(n?: number): DataFrame; + toString(): string; } +export type PivotOps = Pick< + GroupBy, + "count" | "first" | "max" | "mean" | "median" | "min" | "sum" +> & { [inspect](): string }; -export type PivotOps = Pick & {[inspect](): string} +/** @ignore */ +export function _GroupBy(df: any, by: string[], maintainOrder = false) { + const customInspect = () => + util.formatWithOptions(inspectOpts, "GroupBy {by: %O}", by); -export function GroupBy( - df: any, - by: string[], - maintainOrder = false -) { - const customInspect = () => util.formatWithOptions(inspectOpts, "GroupBy {by: %O}", by); - - const pivot = (opts: {pivotCol: string, valuesCol: string} | string, valuesCol?: string): PivotOps => { - if(typeof opts === "string") { - if(valuesCol) { - return pivot({pivotCol: opts, valuesCol}); + const pivot = ( + opts: { pivotCol: string; valuesCol: string } | string, + valuesCol?: string, + ): PivotOps => { + if (typeof opts === "string") { + if (valuesCol) { + return pivot({ pivotCol: opts, valuesCol }); } else { throw new Error("must specify both pivotCol and valuesCol"); } @@ -189,29 +183,27 @@ export function GroupBy( }; const agg = (...aggs): DataFrame => { - if(utils.isExprArray(aggs)) { + if (utils.isExprArray(aggs)) { aggs = [aggs].flat(2); return _DataFrame(df) .lazy() .groupBy(by, maintainOrder) .agg(...aggs) - .collectSync({noOptimization:true}); + .collectSync({ noOptimization: true }); } else { - let pairs = Object.entries(aggs[0]) - .flatMap(([key, values]) => { - return [values].flat(2).map(v => col(key)[v as any]()); - }); + let pairs = Object.entries(aggs[0]).flatMap(([key, values]) => { + return [values].flat(2).map((v) => col(key)[v as any]()); + }); return _DataFrame(df) .lazy() .groupBy(by, maintainOrder) .agg(...pairs) - .collectSync({noOptimization:true}); + .collectSync({ noOptimization: true }); } }; - return Object.seal({ [inspect]: customInspect, agg, @@ -224,16 +216,16 @@ export function GroupBy( groups() { return _DataFrame(df.groupby([by].flat(), null, "groups")); }, - head: (n=5) => agg(exclude(by as any).head(n)), + head: (n = 5) => agg(exclude(by as any).head(n)), last: () => agg(exclude(by as any).last()), max: () => agg(exclude(by as any).max()), mean: () => agg(exclude(by as any).mean()), median: () => agg(exclude(by as any).median()), min: () => agg(exclude(by as any).min()), nUnique: () => agg(exclude(by as any).nUnique()), - quantile: (q: number) => agg(exclude(by as any).quantile(q)), + quantile: (q: number) => agg(exclude(by as any).quantile(q)), sum: () => agg(exclude(by as any).sum()), - tail: (n=5) => agg(exclude(by as any).tail(n)), + tail: (n = 5) => agg(exclude(by as any).tail(n)), toString: () => "GroupBy", }) as GroupBy; } @@ -242,11 +234,12 @@ function PivotOps( df: any, by: string | string[], pivotCol: string, - valueCol: string + valueCol: string, ): PivotOps { - - const pivot = (agg) => () => _DataFrame(df.pivot([by].flat(), [pivotCol], [valueCol], agg)); - const customInspect = () => util.formatWithOptions(inspectOpts, "PivotOps {by: %O}", by); + const pivot = (agg) => () => + _DataFrame(df.pivot([by].flat(), [pivotCol], [valueCol], agg)); + const customInspect = () => + util.formatWithOptions(inspectOpts, "PivotOps {by: %O}", by); return { [inspect]: customInspect, @@ -260,37 +253,40 @@ function PivotOps( }; } - +/** + * intermediate state of a rolling groupby + */ export interface RollingGroupBy { - agg(column: ColumnsOrExpr, ...columns: ColumnsOrExpr[]): DataFrame + agg(column: ColumnsOrExpr, ...columns: ColumnsOrExpr[]): DataFrame; } +/** @ignore */ export function RollingGroupBy( df: any, indexColumn: string, period: string, offset?: string, closed?, - by?: ColumnsOrExpr + by?: ColumnsOrExpr, ): RollingGroupBy { - return { agg(column: ColumnsOrExpr, ...columns: ColumnsOrExpr[]) { - - return df .lazy() - .groupByRolling({indexColumn, period, offset, closed, by} as any) + .groupByRolling({ indexColumn, period, offset, closed, by } as any) .agg(column as any, ...columns) .collectSync(); - } + }, }; } - +/** + * intermediate state of a dynamic groupby + */ export interface DynamicGroupBy { - agg(column: ColumnsOrExpr, ...columns: ColumnsOrExpr[]): DataFrame + agg(column: ColumnsOrExpr, ...columns: ColumnsOrExpr[]): DataFrame; } +/** @ignore */ export function DynamicGroupBy( df: any, indexColumn: string, @@ -300,18 +296,24 @@ export function DynamicGroupBy( truncate?: boolean, includeBoundaries?: boolean, closed?: string, - by?: ColumnsOrExpr + by?: ColumnsOrExpr, ): DynamicGroupBy { - return { agg(column: ColumnsOrExpr, ...columns: ColumnsOrExpr[]) { - - return df .lazy() - .groupByDynamic({indexColumn, every, period, offset, truncate, includeBoundaries, closed, by} as any) + .groupByDynamic({ + indexColumn, + every, + period, + offset, + truncate, + includeBoundaries, + closed, + by, + } as any) .agg(column as any, ...columns) - .collectSync({noOptimizations: true}); - } + .collectSync({ noOptimizations: true }); + }, }; } diff --git a/polars/index.ts b/polars/index.ts index a1c912e7a..66d3f40d2 100644 --- a/polars/index.ts +++ b/polars/index.ts @@ -1,31 +1,35 @@ -import * as series from "./series/series"; +import * as series from "./series"; import * as df from "./dataframe"; -import {DataType} from "./datatypes"; +import { DataType, Field as _field } from "./datatypes"; import * as func from "./functions"; import * as io from "./io"; import * as cfg from "./cfg"; import * as ldf from "./lazy/dataframe"; import pli from "./internals/polars_internal"; -import { - funcs as lazy, - Expr as lazyExpr, - GroupBy as lazyGroupBy, - when as _when -} from "./lazy"; - - -namespace pl { - export import Expr = lazyExpr.Expr - export import DataFrame = df.DataFrame - export import LazyDataFrame = ldf.LazyDataFrame +export { DataType, Field, TimeUnit } from "./datatypes"; +export * from "./series"; +export { Expr } from "./lazy/expr"; +export * from "./dataframe"; +export * from "./functions"; +export * from "./io"; +export * from "./cfg"; +export * from "./lazy/dataframe"; +export * from "./lazy"; +import * as lazy from "./lazy"; +export * from "./types"; +export type { GroupBy } from "./groupby"; +export namespace pl { + export import Expr = lazy.Expr; + export import DataFrame = df.DataFrame; + export import LazyDataFrame = ldf.LazyDataFrame; export import Series = series.Series; - export type LazyGroupBy = lazyGroupBy; - export type When = _when.When; - export type WhenThen = _when.WhenThen; - export type WhenThenThen = _when.WhenThenThen; + export type LazyGroupBy = lazy.LazyGroupBy; + export type When = lazy.When; + export type WhenThen = lazy.WhenThen; + export type WhenThenThen = lazy.WhenThenThen; export import Config = cfg.Config; - export import Int8 = DataType.Int8 - export import Int16 = DataType.Int16 + export import Int8 = DataType.Int8; + export import Int16 = DataType.Int16; export import Int32 = DataType.Int32; export import Int64 = DataType.Int64; export import UInt8 = DataType.UInt8; @@ -37,13 +41,16 @@ namespace pl { export import Bool = DataType.Bool; export import Utf8 = DataType.Utf8; export import List = DataType.List; + // rome-ignore lint/suspicious/noShadowRestrictedNames: pl.Date export import Date = DataType.Date; export import Datetime = DataType.Datetime; export import Time = DataType.Time; + // rome-ignore lint/suspicious/noShadowRestrictedNames: pl.Object export import Object = DataType.Object; export import Null = DataType.Null; export import Struct = DataType.Struct; export import Categorical = DataType.Categorical; + export import Field = _field; export import repeat = func.repeat; export import concat = func.concat; @@ -64,39 +71,34 @@ namespace pl { export import readJSONStream = io.readJSONStream; // lazy - export import col = lazy.col - export import cols = lazy.cols - export import lit = lazy.lit - export import arange = lazy.arange - export import argSortBy = lazy.argSortBy - export import avg = lazy.avg - export import concatList = lazy.concatList - export import concatString = lazy.concatString - export import count = lazy.count - export import cov = lazy.cov - export import exclude = lazy.exclude - export import element = lazy.element - export import first = lazy.first - export import format = lazy.format - export import groups = lazy.groups - export import head = lazy.head - export import last = lazy.last - export import mean = lazy.mean - export import median = lazy.median - export import nUnique = lazy.nUnique - export import pearsonCorr = lazy.pearsonCorr - export import quantile = lazy.quantile - export import select = lazy.select - export import struct = lazy.struct - export import spearmanRankCorr = lazy.spearmanRankCorr - export import tail = lazy.tail - export import list = lazy.list - export import when = _when.when; + export import col = lazy.col; + export import cols = lazy.cols; + export import lit = lazy.lit; + export import arange = lazy.arange; + export import argSortBy = lazy.argSortBy; + export import avg = lazy.avg; + export import concatList = lazy.concatList; + export import concatString = lazy.concatString; + export import count = lazy.count; + export import cov = lazy.cov; + export import exclude = lazy.exclude; + export import element = lazy.element; + export import first = lazy.first; + export import format = lazy.format; + export import groups = lazy.groups; + export import head = lazy.head; + export import last = lazy.last; + export import mean = lazy.mean; + export import median = lazy.median; + export import nUnique = lazy.nUnique; + export import pearsonCorr = lazy.pearsonCorr; + export import quantile = lazy.quantile; + export import select = lazy.select; + export import struct = lazy.struct; + export import spearmanRankCorr = lazy.spearmanRankCorr; + export import tail = lazy.tail; + export import list = lazy.list; export const version = pli.version(); } - -// add this globally so packages can reuse it. -// eslint-disable-next-line no-undef -global[Symbol.for("__pl__")] = pl; // eslint-disable-next-line no-undef -export = pl; +export default pl; diff --git a/polars/internals/construction.ts b/polars/internals/construction.ts index 1e364702d..44955f4a2 100644 --- a/polars/internals/construction.ts +++ b/polars/internals/construction.ts @@ -1,51 +1,49 @@ import pli from "./polars_internal"; import { DataType, polarsTypeToConstructor } from "../datatypes"; import { isTypedArray } from "util/types"; -import {Series} from "../series/series"; -import {_DataFrame} from "../dataframe"; -import {TimeUnit} from "../datatypes/datatype"; -import {Field} from "../datatypes/field"; - +import { Series } from "../series"; +import { _DataFrame } from "../dataframe"; +import { TimeUnit } from "../datatypes/datatype"; +import { Field } from "../datatypes/field"; export const jsTypeToPolarsType = (value: unknown): DataType => { - if(value === null) { + if (value === null) { return DataType.Float64; } if (Array.isArray(value)) { return jsTypeToPolarsType(firstNonNull(value)); } - if(isTypedArray(value)) { + if (isTypedArray(value)) { switch (value.constructor.name) { - case Int8Array.name: - return DataType.Int8; - case Int16Array.name: - return DataType.Int16; - case Int32Array.name: - return DataType.Int32; - case BigInt64Array.name: - return DataType.Int64; - case Uint8Array.name: - return DataType.UInt8; - case Uint16Array.name: - return DataType.UInt16; - case Uint32Array.name: - return DataType.UInt32; - case BigUint64Array.name: - return DataType.UInt64; - case Float32Array.name: - return DataType.Float32; - case Float64Array.name: - return DataType.Float64; - default: - throw new Error(`unknown typed array type: ${value.constructor.name}`); + case Int8Array.name: + return DataType.Int8; + case Int16Array.name: + return DataType.Int16; + case Int32Array.name: + return DataType.Int32; + case BigInt64Array.name: + return DataType.Int64; + case Uint8Array.name: + return DataType.UInt8; + case Uint16Array.name: + return DataType.UInt16; + case Uint32Array.name: + return DataType.UInt32; + case BigUint64Array.name: + return DataType.UInt64; + case Float32Array.name: + return DataType.Float32; + case Float64Array.name: + return DataType.Float64; + default: + throw new Error(`unknown typed array type: ${value.constructor.name}`); } } if (value instanceof Date) { - return DataType.Datetime(TimeUnit.Milliseconds); } - if(typeof value === "object" && (value as any).constructor === Object) { + if (typeof value === "object" && (value as any).constructor === Object) { const flds = Object.entries(value as any).map(([name, value]) => { let dtype = jsTypeToPolarsType(value); @@ -56,16 +54,16 @@ export const jsTypeToPolarsType = (value: unknown): DataType => { } switch (typeof value) { - case "bigint": - return DataType.UInt64; - case "number": - return DataType.Float64; - case "string": - return DataType.Utf8; - case "boolean": - return DataType.Bool; - default: - return DataType.Float64; + case "bigint": + return DataType.UInt64; + case "number": + return DataType.Float64; + case "string": + return DataType.Utf8; + case "boolean": + return DataType.Bool; + default: + return DataType.Float64; } }; @@ -77,17 +75,17 @@ export const jsTypeToPolarsType = (value: unknown): DataType => { * * @example * ``` - * >>> const input = [null, [], [null, "a", "b"]] - * >>> firstNonNull(input) + * > const input = [null, [], [null, "a", "b"]] + * > firstNonNull(input) * ["a"] - * >>> const ints = [null, 1] - * >>> firstNonNull(ints) + * > const ints = [null, 1] + * > firstNonNull(ints) * 1 * ``` */ const firstNonNull = (arr: any[]): any => { - const first = arr.find(x => x !== null && x !== undefined); - if(Array.isArray(first)) { + const first = arr.find((x) => x !== null && x !== undefined); + if (Array.isArray(first)) { return [firstNonNull(arr.flat())]; } @@ -96,78 +94,84 @@ const firstNonNull = (arr: any[]): any => { const fromTypedArray = (name, value) => { switch (value.constructor.name) { - case Int8Array.name: - return pli.JsSeries.newInt8Array(name, value); - case Int16Array.name: - return pli.JsSeries.newInt16Array(name, value); - case Int32Array.name: - return pli.JsSeries.newInt32Array(name, value); - case BigInt64Array.name: - return pli.JsSeries.newBigint64Array(name, value); - case Uint8Array.name: - return pli.JsSeries.newUint8Array(name, value); - case Uint8ClampedArray.name: - return pli.JsSeries.newUint8ClampedArray(name, value); - case Uint16Array.name: - return pli.JsSeries.newUint16Array(name, value); - case Uint32Array.name: - return pli.JsSeries.newUint32Array(name, value); - case BigUint64Array.name: - return pli.JsSeries.newBiguint64Array(name, value); - case Float32Array.name: - return pli.JsSeries.newFloat32Array(name, value); - case Float64Array.name: - return pli.JsSeries.newFloat64Array(name, value); - default: - throw new Error(`unknown typed array type: ${value.constructor.name}`); + case Int8Array.name: + return pli.JsSeries.newInt8Array(name, value); + case Int16Array.name: + return pli.JsSeries.newInt16Array(name, value); + case Int32Array.name: + return pli.JsSeries.newInt32Array(name, value); + case BigInt64Array.name: + return pli.JsSeries.newBigint64Array(name, value); + case Uint8Array.name: + return pli.JsSeries.newUint8Array(name, value); + case Uint8ClampedArray.name: + return pli.JsSeries.newUint8ClampedArray(name, value); + case Uint16Array.name: + return pli.JsSeries.newUint16Array(name, value); + case Uint32Array.name: + return pli.JsSeries.newUint32Array(name, value); + case BigUint64Array.name: + return pli.JsSeries.newBiguint64Array(name, value); + case Float32Array.name: + return pli.JsSeries.newFloat32Array(name, value); + case Float64Array.name: + return pli.JsSeries.newFloat64Array(name, value); + default: + throw new Error(`unknown typed array type: ${value.constructor.name}`); } }; /** * Construct an internal `JsSeries` from an array */ -export function arrayToJsSeries(name: string = "", values: any[] = [], dtype?: any, strict = false): any { +export function arrayToJsSeries( + name: string = "", + values: any[] = [], + dtype?: any, + strict = false, +): any { if (isTypedArray(values)) { return fromTypedArray(name, values); } //Empty sequence defaults to Float64 type - if (!values?.length && !dtype) { + if (!(values?.length || dtype)) { dtype = DataType.Float64; } const firstValue = firstNonNull(values); - if(Array.isArray(firstValue) || isTypedArray(firstValue)) { + if (Array.isArray(firstValue) || isTypedArray(firstValue)) { const listDtype = jsTypeToPolarsType(firstValue); - const constructor = polarsTypeToConstructor(DataType.List(listDtype)); + const ctor = polarsTypeToConstructor(DataType.List(listDtype)); - return constructor(name, values, strict, listDtype); + return ctor(name, values, strict, listDtype); } dtype = dtype ?? jsTypeToPolarsType(firstValue); let series: any; - if(dtype?.variant === "Struct") { + if (dtype?.variant === "Struct") { const df = pli.fromRows(values, null, 1); return df.toStruct(name); } - if(firstValue instanceof Date) { - + if (firstValue instanceof Date) { series = pli.JsSeries.newOptDate(name, values, strict); } else { - const constructor = polarsTypeToConstructor(dtype); - series = constructor(name, values, strict); - } - if ([ - "Datetime", - "Date", - "Categorical", - "Int8", - "Int16", - "UInt8", - "UInt16", - "Float32", - ].includes(dtype.variant)) { + const ctor = polarsTypeToConstructor(dtype); + series = ctor(name, values, strict); + } + if ( + [ + "Datetime", + "Date", + "Categorical", + "Int8", + "Int16", + "UInt8", + "UInt16", + "Float32", + ].includes(dtype.variant) + ) { series = series.cast(dtype, strict); } @@ -178,50 +182,41 @@ export function arrayToJsDataFrame(data: any[], options?): any { let columns = options?.columns; let orient = options?.orient; - let dataSeries: any[]; - if(!data.length) { + if (!data.length) { dataSeries = []; - } - else if (data[0]?._s) { + } else if (data[0]?._s) { dataSeries = []; data.forEach((series: any, idx) => { - if(!series.name) { + if (!series.name) { series.rename(`column_${idx}`, true); } dataSeries.push(series._s); }); - } - else if(data[0].constructor.name === "Object") { - - const df = pli.fromRows( data, options); + } else if (data[0].constructor.name === "Object") { + const df = pli.fromRows(data, options); - if(columns) { + if (columns) { df.columns = columns; } return df; - } - else if (Array.isArray(data[0])) { - if(!orient && columns) { + } else if (Array.isArray(data[0])) { + if (!orient && columns) { orient = columns.length === data.length ? "col" : "row"; } - if(orient === "row") { + if (orient === "row") { const df = pli.fromRows(data); columns && (df.columns = columns); return df; } else { - dataSeries = data.map((s, idx) => (Series(`column_${idx}`, s) as any)._s); - } - - } - else { + } else { dataSeries = [(Series("column_0", data) as any)._s]; } dataSeries = handleColumnsArg(dataSeries, columns); @@ -230,12 +225,12 @@ export function arrayToJsDataFrame(data: any[], options?): any { } function handleColumnsArg(data: any[], columns?: string[]) { - if(!columns) { + if (!columns) { return data; } else { - if(!data) { - return columns.map(c => (Series.from(c, []) as any)._s); - } else if(data.length === columns.length) { + if (!data) { + return columns.map((c) => (Series.from(c, []) as any)._s); + } else if (data.length === columns.length) { columns.forEach((name, i) => { data[i].rename(name); }); diff --git a/polars/io.ts b/polars/io.ts index 7e3912c5b..2be3aa3a5 100644 --- a/polars/io.ts +++ b/polars/io.ts @@ -80,7 +80,7 @@ class LineBatcher extends Stream.Transform { if (chunk[i] === 10) { // '\n' this.#accumulatedLines++; - if (this.#accumulatedLines == this.#batchSize) { + if (this.#accumulatedLines === this.#batchSize) { this.#lines.push(chunk.subarray(begin, i + 1)); this.push(Buffer.concat(this.#lines)); this.#lines = []; @@ -106,27 +106,27 @@ class LineBatcher extends Stream.Transform { function readCSVBuffer(buff, options) { return _DataFrame( - pli.readCsv(buff, { ...readCsvDefaultOptions, ...options }) + pli.readCsv(buff, { ...readCsvDefaultOptions, ...options }), ); } export function readRecords( records: Record[], - options?: { schema: Record } + options?: { schema: Record }, ): DataFrame; export function readRecords( records: Record[], - options?: { inferSchemaLength?: number } + options?: { inferSchemaLength?: number }, ): DataFrame; export function readRecords( records: Record[], - options + options, ): DataFrame { if (options?.schema) { return _DataFrame(pli.fromRows(records, options.schema)); } else { return _DataFrame( - pli.fromRows(records, undefined, options?.inferSchemaLength) + pli.fromRows(records, undefined, options?.inferSchemaLength), ); } } @@ -170,7 +170,7 @@ export function readRecords( */ export function readCSV( pathOrBody: string | Buffer, - options?: Partial + options?: Partial, ): DataFrame; export function readCSV(pathOrBody, options?) { options = { ...readCsvDefaultOptions, ...options }; @@ -257,7 +257,7 @@ const scanCsvDefaultOptions: Partial = { */ export function scanCSV( path: string, - options?: Partial + options?: Partial, ): LazyDataFrame; export function scanCSV(path, options?) { options = { ...scanCsvDefaultOptions, ...options }; @@ -298,11 +298,11 @@ export function scanCSV(path, options?) { */ export function readJSON( pathOrBody: string | Buffer, - options?: Partial + options?: Partial, ): DataFrame; export function readJSON( pathOrBody, - options: Partial = readJsonDefaultOptions + options: Partial = readJsonDefaultOptions, ) { options = { ...readJsonDefaultOptions, ...options }; let method = options.format === "lines" ? pli.readJsonLines : pli.readJson; @@ -366,7 +366,7 @@ interface ScanJsonOptions { */ export function scanJson( path: string, - options?: Partial + options?: Partial, ): LazyDataFrame; export function scanJson(path: string, options?: Partial) { options = { ...readJsonDefaultOptions, ...options }; @@ -396,7 +396,7 @@ interface ReadParquetOptions { */ export function readParquet( pathOrBody: string | Buffer, - options?: Partial + options?: Partial, ): DataFrame { const pliOptions: any = {}; @@ -418,7 +418,7 @@ export function readParquet( const inline = !isPath(pathOrBody, [".parquet"]); if (inline) { return _DataFrame( - pli.readParquet(Buffer.from(pathOrBody), pliOptions, parallel) + pli.readParquet(Buffer.from(pathOrBody), pliOptions, parallel), ); } else { return _DataFrame(pli.readParquet(pathOrBody, pliOptions, parallel)); @@ -445,7 +445,7 @@ export interface ReadAvroOptions { */ export function readAvro( pathOrBody: string | Buffer, - options?: Partial + options?: Partial, ): DataFrame; export function readAvro(pathOrBody, options = {}) { if (Buffer.isBuffer(pathOrBody)) { @@ -515,7 +515,7 @@ export interface ReadIPCOptions { */ export function readIPC( pathOrBody: string | Buffer, - options?: Partial + options?: Partial, ): DataFrame; export function readIPC(pathOrBody, options = {}) { if (Buffer.isBuffer(pathOrBody)) { @@ -550,7 +550,7 @@ export interface ScanIPCOptions { */ export function scanIPC( path: string, - options?: Partial + options?: Partial, ): LazyDataFrame; export function scanIPC(path, options = {}) { return _LazyDataFrame(pli.scanIpc(path, options)); @@ -623,7 +623,7 @@ export function scanIPC(path, options = {}) { */ export function readCSVStream( stream: Readable, - options?: Partial + options?: Partial, ): Promise; export function readCSVStream(stream, options?) { let batchSize = options?.batchSize ?? 10000; @@ -690,7 +690,7 @@ export function readCSVStream(stream, options?) { */ export function readJSONStream( stream: Readable, - options?: Partial + options?: Partial, ): Promise; export function readJSONStream(stream, options = readJsonDefaultOptions) { options = { ...readJsonDefaultOptions, ...options }; diff --git a/polars/lazy/dataframe.ts b/polars/lazy/dataframe.ts index 8efb7576e..de57b2012 100644 --- a/polars/lazy/dataframe.ts +++ b/polars/lazy/dataframe.ts @@ -1,5 +1,5 @@ -import {DataFrame, _DataFrame} from "../dataframe"; -import {Expr, exprToLitOrExpr} from "./expr"; +import { DataFrame, _DataFrame } from "../dataframe"; +import { Expr, exprToLitOrExpr } from "./expr"; import pli from "../internals/polars_internal"; import { columnOrColumnsStrict, @@ -9,27 +9,12 @@ import { selectionToExprList, ValueOrArray, } from "../utils"; -import {LazyGroupBy} from "./groupby"; -import {Deserialize, GroupByOps, Serialize} from "../shared_traits"; +import { _LazyGroupBy, LazyGroupBy } from "./groupby"; +import { Deserialize, GroupByOps, Serialize } from "../shared_traits"; +import { LazyOptions, LazyJoinOptions } from "../types"; const inspect = Symbol.for("nodejs.util.inspect.custom"); -type LazyJoinOptions = { - how?: "left" | "inner" | "outer" | "semi" | "anti" | "cross"; - suffix?: string; - allowParallel?: boolean; - forceParallel?: boolean; -}; - -type LazyOptions = { - typeCoercion?: boolean; - predicatePushdown?: boolean; - projectionPushdown?: boolean; - simplifyExpression?: boolean; - stringCache?: boolean; - noOptimization?: boolean; -}; - /** * Representation of a Lazy computation graph / query. */ @@ -89,7 +74,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { distinct( maintainOrder?: boolean, subset?: ColumnSelection, - keep?: "first" | "last" + keep?: "first" | "last", ): LazyDataFrame; distinct(opts: { maintainOrder?: boolean; @@ -138,13 +123,13 @@ export interface LazyDataFrame extends Serialize, GroupByOps { * @param predicate - Expression that evaluates to a boolean Series. * @example * ``` - * >>> lf = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }).lazy() - * >>> // Filter on one condition - * >>> lf.filter(pl.col("foo").lt(3)).collect() + * > lf = pl.DataFrame({ + * > "foo": [1, 2, 3], + * > "bar": [6, 7, 8], + * > "ham": ['a', 'b', 'c'] + * > }).lazy() + * > // Filter on one condition + * > lf.filter(pl.col("foo").lt(3)).collect() * shape: (2, 3) * ┌─────┬─────┬─────┐ * │ foo ┆ bar ┆ ham │ @@ -166,7 +151,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { * Start a groupby operation. */ groupBy(by: ColumnsOrExpr, maintainOrder?: boolean): LazyGroupBy; - groupBy(by: ColumnsOrExpr, opts: {maintainOrder: boolean}): LazyGroupBy; + groupBy(by: ColumnsOrExpr, opts: { maintainOrder: boolean }): LazyGroupBy; /** * Gets the first `n` rows of the DataFrame. You probably don't want to use this! @@ -214,22 +199,24 @@ export interface LazyDataFrame extends Serialize, GroupByOps { */ join( other: LazyDataFrame, - joinOptions: {on: ValueOrArray} & LazyJoinOptions + joinOptions: { on: ValueOrArray } & LazyJoinOptions, ): LazyDataFrame; join( other: LazyDataFrame, joinOptions: { leftOn: ValueOrArray; rightOn: ValueOrArray; - } & LazyJoinOptions + } & LazyJoinOptions, + ): LazyDataFrame; + join( + other: LazyDataFrame, + options: { + how: "cross"; + suffix?: string; + allowParallel?: boolean; + forceParallel?: boolean; + }, ): LazyDataFrame; - join(other: LazyDataFrame, options: { - how: "cross", - suffix?: string, - allowParallel?: boolean, - forceParallel?: boolean - }): LazyDataFrame - /** * Perform an asof join. This is similar to a left-join except that we @@ -282,7 +269,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { @example ``` - >>> const gdp = pl.DataFrame({ + >const gdp = pl.DataFrame({ ... date: [ ... new Date('2016-01-01'), ... new Date('2017-01-01'), @@ -291,7 +278,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { ... ], // note record date: Jan 1st (sorted!) ... gdp: [4164, 4411, 4566, 4696], ... }) - >>> const population = pl.DataFrame({ + >const population = pl.DataFrame({ ... date: [ ... new Date('2016-05-12'), ... new Date('2017-05-12'), @@ -300,7 +287,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { ... ], // note record date: May 12th (sorted!) ... "population": [82.19, 82.66, 83.12, 83.52], ... }) - >>> population.joinAsof( + >population.joinAsof( ... gdp, ... {leftOn:"date", rightOn:"date", strategy:"backward"} ... ) @@ -334,7 +321,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { tolerance?: number | string; allowParallel?: boolean; forceParallel?: boolean; - } + }, ): LazyDataFrame; /** * Get the last row of the DataFrame. @@ -386,13 +373,13 @@ export interface LazyDataFrame extends Serialize, GroupByOps { * @see {@link DataFrame.shift} */ shift(periods: number): LazyDataFrame; - shift(opts: {periods: number}): LazyDataFrame; + shift(opts: { periods: number }): LazyDataFrame; /** * @see {@link DataFrame.shiftAndFill} */ shiftAndFill( periods: number, - fillValue: number | string | Expr + fillValue: number | string | Expr, ): LazyDataFrame; shiftAndFill(opts: { periods: number; @@ -402,7 +389,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { * @see {@link DataFrame.slice} */ slice(offset: number, length: number): LazyDataFrame; - slice(opts: {offset: number; length: number}): LazyDataFrame; + slice(opts: { offset: number; length: number }): LazyDataFrame; /** * @see {@link DataFrame.sort} */ @@ -438,7 +425,7 @@ export interface LazyDataFrame extends Serialize, GroupByOps { unique( maintainOrder?: boolean, subset?: ColumnSelection, - keep?: "first" | "last" + keep?: "first" | "last", ): LazyDataFrame; unique(opts: { maintainOrder?: boolean; @@ -489,6 +476,7 @@ const prepareGroupbyInputs = (by) => { } }; +/** @ignore */ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { const unwrap = (method: string, ...args: any[]) => { return _ldf[method as any](...args); @@ -537,17 +525,17 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { }; if (typeof opts === "boolean") { - const o = {...defaultOptions, maintainOrder: opts, subset, keep}; + const o = { ...defaultOptions, maintainOrder: opts, subset, keep }; return _LazyDataFrame( - _ldf.unique(o.maintainOrder, o?.subset?.flat(2), o.keep) + _ldf.unique(o.maintainOrder, o?.subset?.flat(2), o.keep), ); } if (opts.subset) { opts.subset = [opts.subset].flat(3); } - const o = {...defaultOptions, ...opts}; + const o = { ...defaultOptions, ...opts }; return _LazyDataFrame(_ldf.unique(o.maintainOrder, o.subset, o.keep)); }, @@ -580,7 +568,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { opts.projectionPushdown, opts.simplifyExpr, opts.stringCache, - opts.slicePushdown + opts.slicePushdown, ); } @@ -598,7 +586,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { opts.projectionPushdown, opts.simplifyExpr, opts.stringCache, - opts.slicePushdown + opts.slicePushdown, ); } @@ -617,23 +605,23 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { return _LazyDataFrame(_ldf.filter(predicate)); }, - groupBy(opt, maintainOrder: any = true) { + groupBy(opt, maintainOrder: any = true): LazyGroupBy { if (opt?.by !== undefined) { const by = selectionToExprList([opt.by], false); - return LazyGroupBy(_ldf.groupby(by, opt.maintainOrder)); + return _LazyGroupBy(_ldf.groupby(by, opt.maintainOrder)); } const by = selectionToExprList([opt], false); - return LazyGroupBy(_ldf.groupby(by, maintainOrder)); + return _LazyGroupBy(_ldf.groupby(by, maintainOrder)); }, - groupByRolling({indexColumn, by, period, offset, closed}) { + groupByRolling({ indexColumn, by, period, offset, closed }) { offset = offset ?? `-${period}`; closed = closed ?? "right"; by = prepareGroupbyInputs(by); const lgb = _ldf.groupbyRolling(indexColumn, period, offset, closed, by); - return LazyGroupBy(lgb); + return _LazyGroupBy(lgb); }, groupByDynamic({ indexColumn, @@ -660,10 +648,10 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { truncate, includeBoundaries, closed, - by + by, ); - return LazyGroupBy(lgb); + return _LazyGroupBy(lgb); }, head(len = 5) { return _LazyDataFrame(_ldf.slice(0, len)); @@ -676,10 +664,21 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { forceParallel: false, ...options, }; - const {how, suffix, allowParallel, forceParallel} = options; + const { how, suffix, allowParallel, forceParallel } = options; if (how === "cross") { - - return _LazyDataFrame(_ldf.join(df._ldf, [], [], allowParallel, forceParallel, how, suffix, [], [])); + return _LazyDataFrame( + _ldf.join( + df._ldf, + [], + [], + allowParallel, + forceParallel, + how, + suffix, + [], + [], + ), + ); } let leftOn; let rightOn; @@ -692,7 +691,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { (options.rightOn && !options.leftOn) ) { throw new TypeError( - "You should pass the column to join on as an argument." + "You should pass the column to join on as an argument.", ); } else { leftOn = selectionToExprList(options.leftOn, false); @@ -708,7 +707,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { how, suffix, [], - [] + [], ); return _LazyDataFrame(ldf); @@ -721,10 +720,10 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { strategy: "backward", ...options, }; - const {suffix, strategy, allowParallel, forceParallel} = options; + const { suffix, strategy, allowParallel, forceParallel } = options; let leftOn; let rightOn; - if (!(other?._ldf)) { + if (!other?._ldf) { throw new TypeError("Expected a 'lazyFrame' as join table"); } if (options.on) { @@ -734,7 +733,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { (options.rightOn && !options.leftOn) ) { throw new TypeError( - "You should pass the column to join on as an argument." + "You should pass the column to join on as an argument.", ); } else { leftOn = options.leftOn; @@ -755,11 +754,11 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { if (typeof options.by === "string") { byLeft = byRight = [options.by]; - } else if (Array.isArray(options.by)) { byLeft = byRight = options.by; } - let toleranceStr, toleranceNum; + let toleranceStr; + let toleranceNum; if (typeof options.tolerance === "string") { toleranceStr = options.tolerance; } else { @@ -777,7 +776,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { suffix, strategy, toleranceNum, - toleranceStr + toleranceStr, ); return _LazyDataFrame(ldf); @@ -799,7 +798,7 @@ export const _LazyDataFrame = (_ldf: any): LazyDataFrame => { }, melt(ids, values) { return _LazyDataFrame( - _ldf.melt(columnOrColumnsStrict(ids), columnOrColumnsStrict(values)) + _ldf.melt(columnOrColumnsStrict(ids), columnOrColumnsStrict(values)), ); }, min() { @@ -901,6 +900,7 @@ export interface LazyDataFrameConstructor extends Deserialize { fromExternal(external: any): LazyDataFrame; } +/** @ignore */ export const LazyDataFrame: LazyDataFrameConstructor = Object.assign( _LazyDataFrame, { @@ -909,5 +909,5 @@ export const LazyDataFrame: LazyDataFrameConstructor = Object.assign( fromExternal(external) { return _LazyDataFrame(pli.JsLazyFrame.cloneExternal(external)); }, - } + }, ); diff --git a/polars/lazy/expr.ts b/polars/lazy/expr.ts deleted file mode 100644 index de6595365..000000000 --- a/polars/lazy/expr.ts +++ /dev/null @@ -1,1093 +0,0 @@ -import {DataType} from "../datatypes"; -import pli from "../internals/polars_internal"; -import { - ExprOrString, - FillNullStrategy, - RankMethod, - selectionToExprList, - INSPECT_SYMBOL -} from "../utils"; -import {Series} from "../series/series"; - -import * as expr from "./expr/"; -import {Arithmetic, Comparison, Cumulative, Deserialize, Rolling, Round, Sample, Serialize} from "../shared_traits"; -export type InterpolationMethod = "nearest" | "higher" | "lower" | "midpoint" | "linear"; - - -export interface Expr extends - Rolling, - Arithmetic, - Comparison, - Cumulative, - Sample, - Round, - Serialize { - /** @ignore */ - _expr: any; - get date(): expr.Datetime; - get str(): expr.String; - get lst(): expr.List; - get struct(): expr.Struct; - [Symbol.toStringTag](): string; - [INSPECT_SYMBOL](): string; - toString(): string; - /** compat with `JSON.stringify` */ - toJSON(): string; - /** Take absolute values */ - abs(): Expr - aggGroups(): Expr - /** - * Rename the output of an expression. - * @param name new name - * @see {@link Expr.as} - * @example - * ``` - * >>> df = pl.DataFrame({ - * ... "a": [1, 2, 3], - * ... "b": ["a", "b", None], - * ... }) - * >>> df - * shape: (3, 2) - * ╭─────┬──────╮ - * │ a ┆ b │ - * │ --- ┆ --- │ - * │ i64 ┆ str │ - * ╞═════╪══════╡ - * │ 1 ┆ "a" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 2 ┆ "b" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 3 ┆ null │ - * ╰─────┴──────╯ - * >>> df.select([ - * ... pl.col("a").alias("bar"), - * ... pl.col("b").alias("foo"), - * ... ]) - * shape: (3, 2) - * ╭─────┬──────╮ - * │ bar ┆ foo │ - * │ --- ┆ --- │ - * │ i64 ┆ str │ - * ╞═════╪══════╡ - * │ 1 ┆ "a" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 2 ┆ "b" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 3 ┆ null │ - * ╰─────┴──────╯ - *``` - */ - alias(name: string): Expr - and(other: any): Expr - /** Get the index of the maximal value. */ - argMax(): Expr - /** Get the index of the minimal value. */ - argMin(): Expr - /** - * Get the index values that would sort this column. - * @param reverse - * - false -> order from small to large. - * - true -> order from large to small. - * @returns UInt32 Series - */ - argSort(reverse?: boolean): Expr - argSort({reverse}: {reverse: boolean}): Expr - /** Get index of first unique value. */ - argUnique(): Expr - /** @see {@link Expr.alias} */ - as(name: string): Expr - /** Fill missing values with the next to be seen values */ - backwardFill(): Expr - /** Cast between data types. */ - cast(dtype: DataType, strict?: boolean): Expr - /** Count the number of values in this expression */ - count(): Expr - /** Calculate the n-th discrete difference. - * - * @param n number of slots to shift - * @param nullBehavior ignore or drop - */ - diff(n: number, nullBehavior: "ignore" | "drop"): Expr - diff(o: {n: number, nullBehavior: "ignore" | "drop"}): Expr - /** - * Compute the dot/inner product between two Expressions - * @param other Expression to compute dot product with - */ - dot(other: any): Expr - /** - * Exclude certain columns from a wildcard/regex selection. - * - * You may also use regexes in the exclude list. They must start with `^` and end with `$`. - * - * @param columns Column(s) to exclude from selection - * @example - * ``` - * >>> df = pl.DataFrame({ - * ... "a": [1, 2, 3], - * ... "b": ["a", "b", None], - * ... "c": [None, 2, 1], - * ...}) - * >>> df - * shape: (3, 3) - * ╭─────┬──────┬──────╮ - * │ a ┆ b ┆ c │ - * │ --- ┆ --- ┆ --- │ - * │ i64 ┆ str ┆ i64 │ - * ╞═════╪══════╪══════╡ - * │ 1 ┆ "a" ┆ null │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 2 ┆ "b" ┆ 2 │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 3 ┆ null ┆ 1 │ - * ╰─────┴──────┴──────╯ - * >>> df.select( - * ... pl.col("*").exclude("b"), - * ... ) - * shape: (3, 2) - * ╭─────┬──────╮ - * │ a ┆ c │ - * │ --- ┆ --- │ - * │ i64 ┆ i64 │ - * ╞═════╪══════╡ - * │ 1 ┆ null │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 2 ┆ 2 │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ - * │ 3 ┆ 1 │ - * ╰─────┴──────╯ - * ``` - */ - exclude(column: string, ...columns: string[]): Expr - /** - * Explode a list or utf8 Series. - * - * This means that every item is expanded to a new row. - */ - explode(): Expr - /** - * Extend the Series with given number of values. - * @param value The value to extend the Series with. This value may be null to fill with nulls. - * @param n The number of values to extend. - * @deprecated - * @see {@link extendConstant} - */ - extend(value: any, n: number): Expr - extend(opt: {value: any, n: number}): Expr - /** - * Extend the Series with given number of values. - * @param value The value to extend the Series with. This value may be null to fill with nulls. - * @param n The number of values to extend. - */ - extendConstant(value: any, n: number): Expr - extendConstant(opt: {value: any, n: number}): Expr - /** Fill nan value with a fill value */ - fillNan(other: any): Expr - /** Fill null value with a fill value or strategy */ - fillNull(other: any | FillNullStrategy): Expr - /** - * Filter a single column. - * - * Mostly useful in in aggregation context. - * If you want to filter on a DataFrame level, use `LazyFrame.filter`. - * @param predicate Boolean expression. - */ - filter(predicate: Expr): Expr - /** Get the first value. */ - first(): Expr - /** @see {@link Expr.explode} */ - flatten(): Expr - /** Fill missing values with the latest seen values */ - forwardFill(): Expr - /** Hash the Series. */ - hash(k0?: number, k1?: number, k2?: number, k3?: number): Expr - hash({k0, k1, k2, k3}: {k0?: number, k1?: number, k2?: number, k3?: number}): Expr - /** Take the first n values. */ - head(length?: number): Expr - head({length}: {length: number}): Expr - inner(): any - /** Interpolate intermediate values. The interpolation method is linear. */ - interpolate(): Expr - /** Get mask of duplicated values. */ - isDuplicated(): Expr - /** Create a boolean expression returning `true` where the expression values are finite. */ - isFinite(): Expr - /** Get a mask of the first unique value. */ - isFirst(): Expr - /** - * Check if elements of this Series are in the right Series, or List values of the right Series. - * - * @param other Series of primitive type or List type. - * @returns Expr that evaluates to a Boolean Series. - * @example - * ``` - * >>> df = pl.DataFrame({ - * ... "sets": [[1, 2, 3], [1, 2], [9, 10]], - * ... "optional_members": [1, 2, 3] - * ... }) - * >>> df.select( - * ... pl.col("optional_members").isIn("sets").alias("contains") - * ... ) - * shape: (3, 1) - * ┌──────────┐ - * │ contains │ - * │ --- │ - * │ bool │ - * ╞══════════╡ - * │ true │ - * ├╌╌╌╌╌╌╌╌╌╌┤ - * │ true │ - * ├╌╌╌╌╌╌╌╌╌╌┤ - * │ false │ - * └──────────┘ - * ``` - */ - isIn(other): Expr - /** Create a boolean expression returning `true` where the expression values are infinite. */ - isInfinite(): Expr - /** Create a boolean expression returning `true` where the expression values are NaN (Not A Number). */ - isNan(): Expr - /** Create a boolean expression returning `true` where the expression values are not NaN (Not A Number). */ - isNotNan(): Expr - /** Create a boolean expression returning `true` where the expression does not contain null values. */ - isNotNull(): Expr - /** Create a boolean expression returning `True` where the expression contains null values. */ - isNull(): Expr - /** Get mask of unique values. */ - isUnique(): Expr - /** - * Keep the original root name of the expression. - * - * A groupby aggregation often changes the name of a column. - * With `keepName` we can keep the original name of the column - * @example - * ``` - * >>> df = pl.DataFrame({ - * ... "a": [1, 2, 3], - * ... "b": ["a", "b", None], - * ... }) - * - * >>> df - * ... .groupBy("a") - * ... .agg(pl.col("b").list()) - * ... .sort({by:"a"}) - * - * shape: (3, 2) - * ╭─────┬────────────╮ - * │ a ┆ b_agg_list │ - * │ --- ┆ --- │ - * │ i64 ┆ list [str] │ - * ╞═════╪════════════╡ - * │ 1 ┆ [a] │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ [b] │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ [null] │ - * ╰─────┴────────────╯ - * - * Keep the original column name: - * - * >>> df - * ... .groupby("a") - * ... .agg(col("b").list().keepName()) - * ... .sort({by:"a"}) - * - * shape: (3, 2) - * ╭─────┬────────────╮ - * │ a ┆ b │ - * │ --- ┆ --- │ - * │ i64 ┆ list [str] │ - * ╞═════╪════════════╡ - * │ 1 ┆ [a] │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ [b] │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ [null] │ - * ╰─────┴────────────╯ - * ``` - */ - keepName(): Expr - kurtosis(): Expr - kurtosis(fisher: boolean, bias?: boolean): Expr - kurtosis({fisher, bias}: {fisher?: boolean, bias?: boolean}): Expr - /** Get the last value. */ - last(): Expr - /** Aggregate to list. */ - list(): Expr - /** Returns a unit Series with the lowest value possible for the dtype of this expression. */ - lowerBound(): Expr - /** Compute the max value of the arrays in the list */ - max(): Expr - /** Compute the mean value of the arrays in the list */ - mean(): Expr - /** Get median value. */ - median(): Expr - /** Get minimum value. */ - min(): Expr - /** Compute the most occurring value(s). Can return multiple Values */ - mode(): Expr - /** Negate a boolean expression. */ - not(): Expr - /** Count unique values. */ - nUnique(): Expr - or(other: any): Expr - /** - * Apply window function over a subgroup. - * - * This is similar to a groupby + aggregation + self join. - * Or similar to [window functions in Postgres](https://www.postgresql.org/docs/9.1/tutorial-window.html) - * @param partitionBy Column(s) to partition by. - * - * @example - * ``` - * >>> df = pl.DataFrame({ - * ... "groups": [1, 1, 2, 2, 1, 2, 3, 3, 1], - * ... "values": [1, 2, 3, 4, 5, 6, 7, 8, 8], - * ... }) - * >>> df.select( - * ... pl.col("groups").sum().over("groups") - * ... ) - * ╭────────┬────────╮ - * │ groups ┆ values │ - * │ --- ┆ --- │ - * │ i32 ┆ i32 │ - * ╞════════╪════════╡ - * │ 1 ┆ 16 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 1 ┆ 16 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ 13 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ 13 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ ... ┆ ... │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 1 ┆ 16 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ 13 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ 15 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ 15 │ - * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 1 ┆ 16 │ - * ╰────────┴────────╯ - * ``` - */ - over(by: ExprOrString, ...partitionBy: ExprOrString[]): Expr - /** Raise expression to the power of exponent. */ - pow(exponent: number): Expr - pow({exponent}: {exponent: number}): Expr - /** - * Add a prefix the to root column name of the expression. - * @example - * ``` - * >>> df = pl.DataFrame({ - * ... "A": [1, 2, 3, 4, 5], - * ... "fruits": ["banana", "banana", "apple", "apple", "banana"], - * ... "B": [5, 4, 3, 2, 1], - * ... "cars": ["beetle", "audi", "beetle", "beetle", "beetle"], - * ... }) - * shape: (5, 4) - * ╭─────┬──────────┬─────┬──────────╮ - * │ A ┆ fruits ┆ B ┆ cars │ - * │ --- ┆ --- ┆ --- ┆ --- │ - * │ i64 ┆ str ┆ i64 ┆ str │ - * ╞═════╪══════════╪═════╪══════════╡ - * │ 1 ┆ "banana" ┆ 5 ┆ "beetle" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ "banana" ┆ 4 ┆ "audi" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ "apple" ┆ 3 ┆ "beetle" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ - * │ 4 ┆ "apple" ┆ 2 ┆ "beetle" │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ - * │ 5 ┆ "banana" ┆ 1 ┆ "beetle" │ - * ╰─────┴──────────┴─────┴──────────╯ - * >>> df.select( - * ... pl.all().reverse().prefix("reverse_"), - * ... ) - * shape: (5, 8) - * ╭───────────┬────────────────┬───────────┬──────────────╮ - * │ reverse_A ┆ reverse_fruits ┆ reverse_B ┆ reverse_cars │ - * │ --- ┆ --- ┆ --- ┆ --- │ - * │ i64 ┆ str ┆ i64 ┆ str │ - * ╞═══════════╪════════════════╪═══════════╪══════════════╡ - * │ 5 ┆ "banana" ┆ 1 ┆ "beetle" │ - * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 4 ┆ "apple" ┆ 2 ┆ "beetle" │ - * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ "apple" ┆ 3 ┆ "beetle" │ - * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 2 ┆ "banana" ┆ 4 ┆ "audi" │ - * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ - * │ 1 ┆ "banana" ┆ 5 ┆ "beetle" │ - * ╰───────────┴────────────────┴───────────┴──────────────╯ - * ``` - */ - prefix(prefix: string): Expr - /** Get quantile value. */ - quantile(quantile: number | Expr): Expr - /** Assign ranks to data, dealing with ties appropriately. */ - rank(method?: RankMethod): Expr - rank({method}: {method: string}): Expr - reinterpret(signed?: boolean): Expr - reinterpret({signed}: {signed: boolean}): Expr - /** - * Repeat the elements in this Series `n` times by dictated by the number given by `by`. - * The elements are expanded into a `List` - * @param by Numeric column that determines how often the values will be repeated. - * - * The column will be coerced to UInt32. Give this dtype to make the coercion a no-op. - */ - repeatBy(by: Expr | string): Expr - /** Reverse the arrays in the list */ - reverse(): 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). - */ - shift(periods?: number): Expr - shift({periods}: {periods: 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). - * @param fillValue Fill null values with the result of this expression. - */ - shiftAndFill(periods: number, fillValue: Expr): Expr - shiftAndFill({periods, fillValue}: {periods: number, fillValue: Expr}): Expr - /** - * Compute the sample skewness of a data set. - * For normally distributed data, the skewness should be about zero. For - * unimodal continuous distributions, a skewness value greater than zero means - * that there is more weight in the right tail of the distribution. - * ___ - * @param bias If False, then the calculations are corrected for statistical bias. - */ - skew(bias?: boolean): Expr - skew({bias}: {bias: boolean}): Expr - /** Slice the Series. */ - slice(offset: number | Expr, length: number | Expr): Expr - slice({offset, length}: {offset: number | Expr, length: number | Expr}): Expr - /** - * Sort this column. In projection/ selection context the whole column is sorted. - * @param reverse - * * false -> order from small to large. - * * true -> order from large to small. - * @param nullsLast If true nulls are considered to be larger than any valid value - */ - sort(reverse?: boolean, nullsLast?: boolean): Expr - sort({reverse, nullsLast}: {reverse?: boolean, nullsLast?: boolean}): Expr - /** - * Sort this column by the ordering of another column, or multiple other columns. - In projection/ selection context the whole column is sorted. - If used in a groupby context, the groups are sorted. - - Parameters - ---------- - @param by - The column(s) used for sorting. - @param reverse - false -> order from small to large. - true -> order from large to small. - */ - sortBy(by: ExprOrString[] | ExprOrString, reverse?: boolean | boolean[]): Expr - sortBy(options: {by: ExprOrString[] | ExprOrString, reverse?: boolean | boolean[]}): Expr - /** Get standard deviation. */ - std(): Expr - /** Add a suffix the to root column name of the expression. */ - suffix(suffix: string): Expr - /** - * Get sum value. - * @note - * Dtypes in {Int8, UInt8, Int16, UInt16} are cast to Int64 before summing to prevent overflow issues. - */ - sum(): Expr - /** Take the last n values. */ - tail(length?: number): Expr - tail({length}: {length: number}): Expr - /** - * Take values by index. - * @param index An expression that leads to a UInt32 dtyped Series. - */ - take(index: Expr | number[] | Series): Expr - take({index}: {index: Expr | number[] | Series}): Expr - /** Take every nth value in the Series and return as a new Series. */ - takeEvery(n: number): Expr - /** - * Get the unique values of this expression; - * @param maintainOrder Maintain order of data. This requires more work. - */ - unique(maintainOrder?: boolean | {maintainOrder: boolean}): Expr - /** Returns a unit Series with the highest value possible for the dtype of this expression. */ - upperBound(): Expr - /** Get variance. */ - var(): Expr - /** Alias for filter: @see {@link filter} */ - where(predicate: Expr): Expr -} - - -export const _Expr = (_expr: any): Expr => { - - const unwrap = (method: string, ...args: any[]) => { - return _expr[method as any](...args); - }; - const wrap = (method, ...args): Expr => { - return _Expr(unwrap(method, ...args)); - }; - - const wrapExprArg = (method: string, lit=false) => (other: any) => { - - const expr = exprToLitOrExpr(other, lit).inner(); - - return wrap(method, expr); - }; - - const rolling = (method: string) => (opts, weights?, minPeriods?, center?): Expr => { - const windowSize = opts?.["windowSize"] ?? (typeof opts === "number" ? opts : null); - if(windowSize === null) { - throw new Error("window size is required"); - } - const callOpts = { - windowSize: `${windowSize}i`, - weights: opts?.["weights"] ?? weights, - minPeriods: opts?.["minPeriods"] ?? minPeriods ?? windowSize, - center : opts?.["center"] ?? center ?? false, - }; - - return wrap(method, callOpts); - }; - - return { - _expr, - [Symbol.toStringTag]() { - return "Expr"; - }, - [INSPECT_SYMBOL]() { - return _expr.toString(); - }, - serialize(format) { - return _expr.serialize(format); - }, - toString() { - return _expr.toString(); - }, - toJSON(...args: any[]) { - // this is passed by `JSON.stringify` when calling `toJSON()` - if(args[0] === "") { - return _expr.toJs(); - } - - return _expr.serialize("json").toString(); - }, - get str() { - return expr.StringFunctions(_expr); - }, - get lst() { - return expr.ListFunctions(_expr); - }, - get date() { - return expr.DateTimeFunctions(_expr); - }, - get struct() { - return expr.StructFunctions(_expr); - }, - abs() { - return _Expr(_expr.abs()); - }, - aggGroups() { - return _Expr(_expr.aggGroups()); - }, - alias(name) { - return _Expr(_expr.alias(name)); - }, - inner() { - return _expr; - }, - and(other) { - const expr = (exprToLitOrExpr(other, false) as any).inner(); - - return _Expr(_expr.and(expr)); - }, - argMax() { - return _Expr(_expr.argMax()); - }, - argMin() { - return _Expr(_expr.argMin()); - }, - argSort(reverse: any = false) { - reverse = reverse?.reverse ?? reverse; - - return _Expr(_expr.argSort(reverse)); - }, - argUnique() { - return _Expr(_expr.argUnique()); - }, - as(name) { - return _Expr(_expr.alias(name)); - }, - backwardFill() { - return _Expr(_expr.backwardFill()); - }, - cast(dtype, strict=false) { - return _Expr(_expr.cast(dtype, strict)); - }, - ceil() { - return _Expr(_expr.ceil()); - }, - clip(arg, max?){ - if(typeof arg === "number") { - return _Expr(_expr.clip(arg, max)); - } else { - return _Expr(_expr.clip(arg.min, arg.max)); - } - }, - count() { - return _Expr(_expr.count()); - }, - cumCount(reverse: any = false) { - reverse = reverse?.reverse ?? reverse; - - return _Expr(_expr.cumcount(reverse?.reverse ?? reverse)); - }, - cumMax(reverse: any = false) { - reverse = reverse?.reverse ?? reverse; - - return _Expr(_expr.cummax(reverse)); - }, - cumMin(reverse: any = false) { - reverse = reverse?.reverse ?? reverse; - - return _Expr(_expr.cummin(reverse)); - }, - cumProd(reverse: any = false) { - reverse = reverse?.reverse ?? reverse; - - return _Expr(_expr.cumprod(reverse)); - }, - cumSum(reverse: any = false) { - reverse = reverse?.reverse ?? reverse; - - return _Expr(_expr.cumsum(reverse)); - }, - diff(n, nullBehavior = "ignore") { - - if(typeof n === "number") { - - return _Expr(_expr.diff(n, nullBehavior)); - } - else { - return _Expr(_expr.diff(n.n, n.nullBehavior)); - } - }, - dot(other) { - const expr = (exprToLitOrExpr(other, false) as any).inner(); - - return _Expr(_expr.dot(expr)); - }, - exclude(...columns) { - return _Expr(_expr.exclude(columns.flat(2))); - }, - explode() { - return _Expr(_expr.explode()); - }, - extend(o, n?) { - if(n !== null && typeof n === "number") { - return _Expr(_expr.extendConstant(o, n)); - } - - return _Expr(_expr.extendConstant(o.value, o.n)); - - }, - extendConstant(o, n?) { - if(n !== null && typeof n === "number") { - return _Expr(_expr.extendConstant(o, n)); - } - - return _Expr(_expr.extendConstant(o.value, o.n)); - }, - fillNan(other) { - const expr = (exprToLitOrExpr(other, true) as any).inner(); - - return _Expr(_expr.fillNan(expr)); - }, - fillNull(fillValue) { - if(["backward", "forward", "mean", "min", "max", "zero", "one"].includes(fillValue)) { - return _Expr(_expr.fillNullWithStrategy(fillValue)); - } - - const expr = exprToLitOrExpr(fillValue).inner(); - - return _Expr(_expr.fillNull(expr)); - }, - filter(predicate) { - const expr = exprToLitOrExpr(predicate).inner(); - - return _Expr(_expr.filter(expr)); - }, - first() { - return _Expr(_expr.first()); - }, - flatten() { - return _Expr(_expr.explode()); - }, - floor() { - return _Expr(_expr.floor()); - }, - forwardFill() { - return _Expr(_expr.forwardFill()); - }, - hash(obj: any=0, k1=1, k2=2, k3=3) { - if (typeof obj === "number" || typeof obj === "bigint") { - return wrap("hash", BigInt(obj), BigInt(k1), BigInt(k2), BigInt(k3)); - } - const o = { k0: obj, k1: k1, k2: k2, k3: k3, ...obj}; - - return wrap( - "hash", - BigInt(o.k0), - BigInt(o.k1), - BigInt(o.k2), - BigInt(o.k3) - ); - }, - head(length) { - if(typeof length === "number") { - return wrap("head", length); - } - - return wrap("head", length.length); - }, - interpolate(method: InterpolationMethod = "linear") { - return _Expr(_expr.interpolate(method)); - }, - isDuplicated() { - return _Expr(_expr.isDuplicated()); - }, - isFinite() { - return _Expr(_expr.isFinite()); - }, - isInfinite() { - return _Expr(_expr.isInfinite()); - }, - isFirst() { - return _Expr(_expr.isFirst()); - }, - isNan() { - return _Expr(_expr.isNan()); - }, - isNotNan() { - return _Expr(_expr.isNotNan()); - }, - isNotNull() { - return _Expr(_expr.isNotNull()); - }, - isNull() { - return _Expr(_expr.isNull()); - }, - isUnique() { - return _Expr(_expr.isUnique()); - }, - isIn(other) { - if(Array.isArray(other)) { - other = pli.lit(Series(other).inner()); - } else { - other = exprToLitOrExpr(other, false).inner(); - } - - return wrap("isIn", other); - }, - keepName() { - return _Expr(_expr.keepName()); - }, - kurtosis(obj?, bias=true) { - const fisher = obj?.["fisher"] ?? (typeof obj === "boolean" ? obj : true); - bias = obj?.["bias"] ?? bias; - - return _Expr(_expr.kurtosis(fisher, bias)); - - }, - last() { - return _Expr(_expr.last()); - }, - list() { - return _Expr(_expr.list()); - }, - lowerBound() { - return _Expr(_expr.lowerBound()); - }, - max() { - return _Expr(_expr.max()); - }, - mean() { - return _Expr(_expr.mean()); - }, - median() { - return _Expr(_expr.median()); - }, - min() { - return _Expr(_expr.min()); - }, - mode() { - return _Expr(_expr.mode()); - }, - not() { - return _Expr(_expr.not()); - }, - nUnique() { - return _Expr(_expr.nUnique()); - }, - or(other) { - const expr = exprToLitOrExpr(other).inner(); - - return _Expr(_expr.or(expr)); - }, - over(...exprs) { - const partitionBy = selectionToExprList(exprs, false); - - return wrap("over", partitionBy); - }, - pow(exponent) { - return _Expr(_expr.pow(exponent?.exponent ?? exponent)); - }, - prefix(prefix) { - - return _Expr(_expr.prefix(prefix)); - }, - quantile(quantile, interpolation: InterpolationMethod = "nearest") { - if (Expr.isExpr(quantile)) { - quantile = quantile._expr; - } else { - quantile = pli.lit(quantile); - } - - return _Expr(_expr.quantile(quantile, interpolation)); - }, - rank(method: any = "average", reverse=false) { - return _Expr(_expr.rank(method?.method ?? method, method?.reverse ?? reverse)); - }, - reinterpret(signed: any = true) { - signed = signed?.signed ?? signed; - - return _Expr(_expr.reinterpret(signed)); - }, - repeatBy(expr) { - const e = exprToLitOrExpr(expr, false)._expr; - - return _Expr(_expr.repeatBy(e)); - }, - reverse() { - return _Expr(_expr.reverse()); - }, - rollingMax: rolling("rollingMax"), - rollingMean: rolling("rollingMean"), - rollingMin: rolling("rollingMin"), - rollingSum: rolling("rollingSum"), - rollingStd: rolling("rollingStd"), - rollingVar: rolling("rollingVar"), - rollingMedian: rolling("rollingMedian"), - rollingQuantile(val, interpolation?, windowSize?, weights?, minPeriods?, center?) { - if(typeof val === "number") { - - return wrap("rollingQuantile", - val, - interpolation ?? "nearest", - { - windowSize: `${windowSize}i`, - weights, - minPeriods, - center - }); - } - windowSize = val?.["windowSize"] ?? (typeof val === "number" ? val : null); - if(windowSize === null) { - throw new Error("window size is required"); - } - const options = { - windowSize: `${windowSize}i`, - weights: val?.["weights"] ?? weights, - minPeriods: val?.["minPeriods"] ?? minPeriods ?? windowSize, - center : val?.["center"] ?? center ?? false, - }; - - return wrap("rollingQuantile", - val.quantile, - val.interpolation ?? "nearest", - options - ); - }, - rollingSkew(val, bias=true) { - if(typeof val === "number") { - return wrap("rollingSkew", val, bias); - } - - return wrap("rollingSkew", val.windowSize, val.bias ?? bias); - }, - round(decimals) { - return _Expr(_expr.round(decimals?.decimals ?? decimals)); - }, - sample(opts?, frac?, withReplacement = false, seed?) { - if(opts?.n !== undefined || opts?.frac !== undefined) { - - return this.sample(opts.n, opts.frac, opts.withReplacement, seed); - } - if (typeof opts === "number") { - throw new Error("sample_n is not yet supported for expr"); - } - if(typeof frac === "number") { - return wrap("sampleFrac", - frac, - withReplacement, - false, - seed - ); - } - else { - throw new TypeError("must specify either 'frac' or 'n'"); - } - }, - shift(periods) { - return _Expr(_expr.shift(periods)); - }, - shiftAndFill(optOrPeriods, fillValue?) { - if(typeof optOrPeriods === "number") { - fillValue = exprToLitOrExpr(fillValue).inner(); - - return wrap("shiftAndFill", optOrPeriods, fillValue); - - } - else { - fillValue = exprToLitOrExpr(optOrPeriods.fillValue).inner(); - const periods = optOrPeriods.periods; - - return wrap("shiftAndFill", periods, fillValue); - } - }, - skew(bias) { - return wrap("skew", bias?.bias ?? bias ?? true); - }, - slice(arg, len?) { - if(typeof arg === "number") { - return wrap("slice", pli.lit(arg), pli.lit(len)); - } - - return wrap("slice", pli.lit(arg.offset), pli.lit(arg.length)); - }, - sort(reverse: any = false, nullsLast=false) { - if(typeof reverse === "boolean") { - return wrap("sortWith", reverse, nullsLast); - } - - return wrap("sortWith", reverse?.reverse ?? false, reverse?.nullsLast ?? nullsLast); - }, - sortBy(arg, reverse=false) { - if(arg?.by !== undefined) { - return this.sortBy(arg.by, arg.reverse); - } - - reverse = Array.isArray(reverse) ? reverse.flat() : [reverse] as any; - const by = selectionToExprList(arg, false); - - return wrap("sortBy", by, reverse); - }, - std() { - return _Expr(_expr.std()); - }, - suffix(suffix) { - - return _Expr(_expr.suffix(suffix)); - }, - sum() { - return _Expr(_expr.sum()); - }, - tail(length) { - return _Expr(_expr.tail(length)); - }, - - take(indices) { - if(Array.isArray(indices)) { - indices = pli.lit(Series(indices).inner()); - } else { - indices = indices.inner(); - } - - return wrap("take", indices); - }, - takeEvery(n) { - return _Expr(_expr.takeEvery(n)); - }, - unique(opt?) { - if(opt) { - return wrap("unique_stable"); - } - - return wrap("unique"); - }, - upperBound() { - return _Expr(_expr.upperBound()); - }, - where(expr) { - - return this.filter(expr); - }, - var() { - return _Expr(_expr.var()); - }, - add: wrapExprArg("add"), - sub: wrapExprArg("sub"), - div: wrapExprArg("div"), - mul: wrapExprArg("mul"), - rem: wrapExprArg("rem"), - - plus: wrapExprArg("add"), - minus: wrapExprArg("sub"), - divideBy: wrapExprArg("div"), - multiplyBy: wrapExprArg("mul"), - modulo: wrapExprArg("rem"), - - eq: wrapExprArg("eq"), - equals: wrapExprArg("eq"), - gtEq: wrapExprArg("gtEq"), - greaterThanEquals: wrapExprArg("gtEq"), - gt: wrapExprArg("gt"), - greaterThan: wrapExprArg("gt"), - ltEq: wrapExprArg("ltEq"), - lessThanEquals: wrapExprArg("ltEq"), - lt: wrapExprArg("lt"), - lessThan: wrapExprArg("lt"), - neq: wrapExprArg("neq"), - notEquals: wrapExprArg("neq"), - }; -}; - -export interface ExprConstructor extends Deserialize { - isExpr(arg: any): arg is Expr; -} - -const isExpr = (anyVal: any): anyVal is Expr => { - try { - return anyVal?.[Symbol.toStringTag]?.() === "Expr"; - } catch (err) { - return false; - } -}; - - -const deserialize = (buf, format) => { - return _Expr(pli.JsExpr.deserialize(buf, format)); -}; - -export const Expr: ExprConstructor = Object.assign(_Expr, {isExpr, deserialize}); - -/** @ignore */ -export const exprToLitOrExpr = (expr: any, stringToLit = true): Expr => { - if(typeof expr === "string" && !stringToLit) { - return _Expr(pli.col(expr)); - } else if (Expr.isExpr(expr)) { - return expr; - } else if (Series.isSeries(expr)) { - return _Expr(pli.lit((expr as any)._s)); - } else { - return _Expr(pli.lit(expr)); - } -}; diff --git a/polars/lazy/expr/datetime.ts b/polars/lazy/expr/datetime.ts index d2c3f41fd..a58eb2675 100644 --- a/polars/lazy/expr/datetime.ts +++ b/polars/lazy/expr/datetime.ts @@ -1,102 +1,13 @@ -import {Expr, _Expr} from "../expr"; +import { DateFunctions } from "../../shared_traits"; +import { Expr, _Expr } from "../expr"; -export interface ExprDateTime { - /** - * Extract day from underlying Date representation. - * Can be performed on Date and Datetime. - * - * Returns the day of month starting from 1. - * The return value ranges from 1 to 31. (The last day of month differs by months.) - * @returns day as pl.UInt32 - */ - day(): Expr; - /** - * Extract hour from underlying DateTime representation. - * Can be performed on Datetime. - * - * Returns the hour number from 0 to 23. - * @returns Hour as UInt32 - */ - hour(): Expr; - /** - * Extract minutes from underlying DateTime representation. - * Can be performed on Datetime. - * - * Returns the minute number from 0 to 59. - * @returns minute as UInt32 - */ - minute(): Expr; - /** - * Extract month from underlying Date representation. - * Can be performed on Date and Datetime. - * - * Returns the month number starting from 1. - * The return value ranges from 1 to 12. - * @returns Month as UInt32 - */ - month(): Expr; - /** - * Extract seconds from underlying DateTime representation. - * Can be performed on Datetime. - * - * Returns the number of nanoseconds since the whole non-leap second. - * The range from 1,000,000,000 to 1,999,999,999 represents the leap second. - * @returns Nanosecond as UInt32 - */ - nanosecond(): Expr; - /** - * Extract ordinal day from underlying Date representation. - * Can be performed on Date and Datetime. - * - * Returns the day of year starting from 1. - * The return value ranges from 1 to 366. (The last day of year differs by years.) - * @returns Day as UInt32 - */ - ordinalDay(): Expr; - /** - * Extract seconds from underlying DateTime representation. - * Can be performed on Datetime. - * - * Returns the second number from 0 to 59. - * @returns Second as UInt32 - */ - second(): Expr; - /** - * Format Date/datetime with a formatting rule: See [chrono strftime/strptime](https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html). - */ - strftime(fmt: string): Expr; - /** Return timestamp in ms as Int64 type. */ - timestamp(): Expr; - /** - * Extract the week from the underlying Date representation. - * Can be performed on Date and Datetime - * - * Returns the ISO week number starting from 1. - * The return value ranges from 1 to 53. (The last week of year differs by years.) - * @returns Week number as UInt32 - */ - week(): Expr; - /** - * Extract the week day from the underlying Date representation. - * Can be performed on Date and Datetime. - * - * Returns the weekday number where monday = 0 and sunday = 6 - * @returns Week day as UInt32 - */ - weekday(): Expr; - /** - * Extract year from underlying Date representation. - * Can be performed on Date and Datetime. - * - * Returns the year number in the calendar date. - * @returns Year as Int32 - */ - year(): Expr; -} +/** + * DateTime functions + */ +export interface ExprDateTime extends DateFunctions {} export const ExprDateTimeFunctions = (_expr: any): ExprDateTime => { const wrap = (method, ...args: any[]): Expr => { - return _Expr(_expr[method](...args)); }; diff --git a/polars/lazy/expr/index.ts b/polars/lazy/expr/index.ts index a0289830c..58d32c2a1 100644 --- a/polars/lazy/expr/index.ts +++ b/polars/lazy/expr/index.ts @@ -2,19 +2,1140 @@ import * as dt from "./datetime"; import * as lst from "./list"; import * as str from "./string"; import * as struct from "./struct"; +export type { StringNamespace } from "./string"; +export type { ExprList as ListNamespace } from "./list"; +export type { ExprDateTime as DatetimeNamespace } from "./datetime"; +export type { ExprStruct as StructNamespace } from "./struct"; -namespace expr { +import { DataType } from "../../datatypes"; +import pli from "../../internals/polars_internal"; +import { ExprOrString, selectionToExprList, INSPECT_SYMBOL } from "../../utils"; +import { Series } from "../../series"; +import { + Arithmetic, + Comparison, + Cumulative, + Deserialize, + Rolling, + Round, + Sample, + Serialize, +} from "../../shared_traits"; +import { InterpolationMethod, FillNullStrategy, RankMethod } from "../../types"; +/** + * Expressions that can be used in various contexts. + */ +export interface Expr + extends Rolling, + Arithmetic, + Comparison, + Cumulative, + Sample, + Round, + Serialize { + /** @ignore */ + _expr: any; + /** + * Datetime namespace + */ + get date(): dt.ExprDateTime; + /** + * String namespace + */ + get str(): str.StringNamespace; + /** + * List namespace + */ + get lst(): lst.ExprList; + /** + * Struct namespace + */ + get struct(): struct.ExprStruct; + [Symbol.toStringTag](): string; + [INSPECT_SYMBOL](): string; + toString(): string; + /** compat with `JSON.stringify` */ + toJSON(): string; + /** Take absolute values */ + abs(): Expr; + aggGroups(): Expr; + /** + * Rename the output of an expression. + * @param name new name + * @see {@link Expr.as} + * @example + * ``` + * > df = pl.DataFrame({ + * ... "a": [1, 2, 3], + * ... "b": ["a", "b", None], + * ... }) + * > df + * shape: (3, 2) + * ╭─────┬──────╮ + * │ a ┆ b │ + * │ --- ┆ --- │ + * │ i64 ┆ str │ + * ╞═════╪══════╡ + * │ 1 ┆ "a" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2 ┆ "b" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 3 ┆ null │ + * ╰─────┴──────╯ + * > df.select([ + * ... pl.col("a").alias("bar"), + * ... pl.col("b").alias("foo"), + * ... ]) + * shape: (3, 2) + * ╭─────┬──────╮ + * │ bar ┆ foo │ + * │ --- ┆ --- │ + * │ i64 ┆ str │ + * ╞═════╪══════╡ + * │ 1 ┆ "a" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2 ┆ "b" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 3 ┆ null │ + * ╰─────┴──────╯ + *``` + */ + alias(name: string): Expr; + and(other: any): Expr; + /** Get the index of the maximal value. */ + argMax(): Expr; + /** Get the index of the minimal value. */ + argMin(): Expr; + /** + * Get the index values that would sort this column. + * @param reverse + * - false -> order from small to large. + * - true -> order from large to small. + * @returns UInt32 Series + */ + argSort(reverse?: boolean): Expr; + argSort({ reverse }: { reverse: boolean }): Expr; + /** Get index of first unique value. */ + argUnique(): Expr; + /** @see {@link Expr.alias} */ + as(name: string): Expr; + /** Fill missing values with the next to be seen values */ + backwardFill(): Expr; + /** Cast between data types. */ + cast(dtype: DataType, strict?: boolean): Expr; + /** Count the number of values in this expression */ + count(): Expr; + /** Calculate the n-th discrete difference. + * + * @param n number of slots to shift + * @param nullBehavior ignore or drop + */ + diff(n: number, nullBehavior: "ignore" | "drop"): Expr; + diff(o: { n: number; nullBehavior: "ignore" | "drop" }): Expr; + /** + * Compute the dot/inner product between two Expressions + * @param other Expression to compute dot product with + */ + dot(other: any): Expr; + /** + * Exclude certain columns from a wildcard/regex selection. + * + * You may also use regexes in the exclude list. They must start with `^` and end with `$`. + * + * @param columns Column(s) to exclude from selection + * @example + * ``` + * >df = pl.DataFrame({ + * ... "a": [1, 2, 3], + * ... "b": ["a", "b", None], + * ... "c": [None, 2, 1], + * ...}) + * >df + * shape: (3, 3) + * ╭─────┬──────┬──────╮ + * │ a ┆ b ┆ c │ + * │ --- ┆ --- ┆ --- │ + * │ i64 ┆ str ┆ i64 │ + * ╞═════╪══════╪══════╡ + * │ 1 ┆ "a" ┆ null │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2 ┆ "b" ┆ 2 │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 3 ┆ null ┆ 1 │ + * ╰─────┴──────┴──────╯ + * >df.select( + * ... pl.col("*").exclude("b"), + * ... ) + * shape: (3, 2) + * ╭─────┬──────╮ + * │ a ┆ c │ + * │ --- ┆ --- │ + * │ i64 ┆ i64 │ + * ╞═════╪══════╡ + * │ 1 ┆ null │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 2 ┆ 2 │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌┤ + * │ 3 ┆ 1 │ + * ╰─────┴──────╯ + * ``` + */ + exclude(column: string, ...columns: string[]): Expr; + /** + * Explode a list or utf8 Series. + * + * This means that every item is expanded to a new row. + */ + explode(): Expr; + /** + * Extend the Series with given number of values. + * @param value The value to extend the Series with. This value may be null to fill with nulls. + * @param n The number of values to extend. + * @deprecated + * @see {@link extendConstant} + */ + extend(value: any, n: number): Expr; + extend(opt: { value: any; n: number }): Expr; + /** + * Extend the Series with given number of values. + * @param value The value to extend the Series with. This value may be null to fill with nulls. + * @param n The number of values to extend. + */ + extendConstant(value: any, n: number): Expr; + extendConstant(opt: { value: any; n: number }): Expr; + /** Fill nan value with a fill value */ + fillNan(other: any): Expr; + /** Fill null value with a fill value or strategy */ + fillNull(other: any | FillNullStrategy): Expr; + /** + * Filter a single column. + * + * Mostly useful in in aggregation context. + * If you want to filter on a DataFrame level, use `LazyFrame.filter`. + * @param predicate Boolean expression. + */ + filter(predicate: Expr): Expr; + /** Get the first value. */ + first(): Expr; + /** @see {@link Expr.explode} */ + flatten(): Expr; + /** Fill missing values with the latest seen values */ + forwardFill(): Expr; + /** Hash the Series. */ + hash(k0?: number, k1?: number, k2?: number, k3?: number): Expr; + hash({ + k0, + k1, + k2, + k3, + }: { k0?: number; k1?: number; k2?: number; k3?: number }): Expr; + /** Take the first n values. */ + head(length?: number): Expr; + head({ length }: { length: number }): Expr; + inner(): any; + /** Interpolate intermediate values. The interpolation method is linear. */ + interpolate(): Expr; + /** Get mask of duplicated values. */ + isDuplicated(): Expr; + /** Create a boolean expression returning `true` where the expression values are finite. */ + isFinite(): Expr; + /** Get a mask of the first unique value. */ + isFirst(): Expr; + /** + * Check if elements of this Series are in the right Series, or List values of the right Series. + * + * @param other Series of primitive type or List type. + * @returns Expr that evaluates to a Boolean Series. + * @example + * ``` + * > df = pl.DataFrame({ + * ... "sets": [[1, 2, 3], [1, 2], [9, 10]], + * ... "optional_members": [1, 2, 3] + * ... }) + * > df.select( + * ... pl.col("optional_members").isIn("sets").alias("contains") + * ... ) + * shape: (3, 1) + * ┌──────────┐ + * │ contains │ + * │ --- │ + * │ bool │ + * ╞══════════╡ + * │ true │ + * ├╌╌╌╌╌╌╌╌╌╌┤ + * │ true │ + * ├╌╌╌╌╌╌╌╌╌╌┤ + * │ false │ + * └──────────┘ + * ``` + */ + isIn(other): Expr; + /** Create a boolean expression returning `true` where the expression values are infinite. */ + isInfinite(): Expr; + /** Create a boolean expression returning `true` where the expression values are NaN (Not A Number). */ + isNan(): Expr; + /** Create a boolean expression returning `true` where the expression values are not NaN (Not A Number). */ + isNotNan(): Expr; + /** Create a boolean expression returning `true` where the expression does not contain null values. */ + isNotNull(): Expr; + /** Create a boolean expression returning `True` where the expression contains null values. */ + isNull(): Expr; + /** Get mask of unique values. */ + isUnique(): Expr; + /** + * Keep the original root name of the expression. + * + * A groupby aggregation often changes the name of a column. + * With `keepName` we can keep the original name of the column + * @example + * ``` + * > df = pl.DataFrame({ + * ... "a": [1, 2, 3], + * ... "b": ["a", "b", None], + * ... }) + * + * > df + * ... .groupBy("a") + * ... .agg(pl.col("b").list()) + * ... .sort({by:"a"}) + * + * shape: (3, 2) + * ╭─────┬────────────╮ + * │ a ┆ b_agg_list │ + * │ --- ┆ --- │ + * │ i64 ┆ list [str] │ + * ╞═════╪════════════╡ + * │ 1 ┆ [a] │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ [b] │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ [null] │ + * ╰─────┴────────────╯ + * + * Keep the original column name: + * + * > df + * ... .groupby("a") + * ... .agg(col("b").list().keepName()) + * ... .sort({by:"a"}) + * + * shape: (3, 2) + * ╭─────┬────────────╮ + * │ a ┆ b │ + * │ --- ┆ --- │ + * │ i64 ┆ list [str] │ + * ╞═════╪════════════╡ + * │ 1 ┆ [a] │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ [b] │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ [null] │ + * ╰─────┴────────────╯ + * ``` + */ + keepName(): Expr; + kurtosis(): Expr; + kurtosis(fisher: boolean, bias?: boolean): Expr; + kurtosis({ fisher, bias }: { fisher?: boolean; bias?: boolean }): Expr; + /** Get the last value. */ + last(): Expr; + /** Aggregate to list. */ + list(): Expr; + /** Returns a unit Series with the lowest value possible for the dtype of this expression. */ + lowerBound(): Expr; + /** Compute the max value of the arrays in the list */ + max(): Expr; + /** Compute the mean value of the arrays in the list */ + mean(): Expr; + /** Get median value. */ + median(): Expr; + /** Get minimum value. */ + min(): Expr; + /** Compute the most occurring value(s). Can return multiple Values */ + mode(): Expr; + /** Negate a boolean expression. */ + not(): Expr; + /** Count unique values. */ + nUnique(): Expr; + or(other: any): Expr; + /** + * Apply window function over a subgroup. + * + * This is similar to a groupby + aggregation + self join. + * Or similar to [window functions in Postgres](https://www.postgresql.org/docs/9.1/tutorial-window.html) + * @param partitionBy Column(s) to partition by. + * + * @example + * ``` + * > df = pl.DataFrame({ + * ... "groups": [1, 1, 2, 2, 1, 2, 3, 3, 1], + * ... "values": [1, 2, 3, 4, 5, 6, 7, 8, 8], + * ... }) + * > df.select( + * ... pl.col("groups").sum().over("groups") + * ... ) + * ╭────────┬────────╮ + * │ groups ┆ values │ + * │ --- ┆ --- │ + * │ i32 ┆ i32 │ + * ╞════════╪════════╡ + * │ 1 ┆ 16 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 1 ┆ 16 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ 13 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ 13 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ ... ┆ ... │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 1 ┆ 16 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ 13 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ 15 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ 15 │ + * ├╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 1 ┆ 16 │ + * ╰────────┴────────╯ + * ``` + */ + over(by: ExprOrString, ...partitionBy: ExprOrString[]): Expr; + /** Raise expression to the power of exponent. */ + pow(exponent: number): Expr; + pow({ exponent }: { exponent: number }): Expr; + /** + * Add a prefix the to root column name of the expression. + * @example + * ``` + * > df = pl.DataFrame({ + * ... "A": [1, 2, 3, 4, 5], + * ... "fruits": ["banana", "banana", "apple", "apple", "banana"], + * ... "B": [5, 4, 3, 2, 1], + * ... "cars": ["beetle", "audi", "beetle", "beetle", "beetle"], + * ... }) + * shape: (5, 4) + * ╭─────┬──────────┬─────┬──────────╮ + * │ A ┆ fruits ┆ B ┆ cars │ + * │ --- ┆ --- ┆ --- ┆ --- │ + * │ i64 ┆ str ┆ i64 ┆ str │ + * ╞═════╪══════════╪═════╪══════════╡ + * │ 1 ┆ "banana" ┆ 5 ┆ "beetle" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ "banana" ┆ 4 ┆ "audi" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ "apple" ┆ 3 ┆ "beetle" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ + * │ 4 ┆ "apple" ┆ 2 ┆ "beetle" │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ + * │ 5 ┆ "banana" ┆ 1 ┆ "beetle" │ + * ╰─────┴──────────┴─────┴──────────╯ + * > df.select( + * ... pl.all().reverse().prefix("reverse_"), + * ... ) + * shape: (5, 8) + * ╭───────────┬────────────────┬───────────┬──────────────╮ + * │ reverse_A ┆ reverse_fruits ┆ reverse_B ┆ reverse_cars │ + * │ --- ┆ --- ┆ --- ┆ --- │ + * │ i64 ┆ str ┆ i64 ┆ str │ + * ╞═══════════╪════════════════╪═══════════╪══════════════╡ + * │ 5 ┆ "banana" ┆ 1 ┆ "beetle" │ + * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 4 ┆ "apple" ┆ 2 ┆ "beetle" │ + * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ "apple" ┆ 3 ┆ "beetle" │ + * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 2 ┆ "banana" ┆ 4 ┆ "audi" │ + * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 1 ┆ "banana" ┆ 5 ┆ "beetle" │ + * ╰───────────┴────────────────┴───────────┴──────────────╯ + * ``` + */ + prefix(prefix: string): Expr; + /** Get quantile value. */ + quantile(quantile: number | Expr): Expr; + /** Assign ranks to data, dealing with ties appropriately. */ + rank(method?: RankMethod): Expr; + rank({ method }: { method: string }): Expr; + reinterpret(signed?: boolean): Expr; + reinterpret({ signed }: { signed: boolean }): Expr; + /** + * Repeat the elements in this Series `n` times by dictated by the number given by `by`. + * The elements are expanded into a `List` + * @param by Numeric column that determines how often the values will be repeated. + * + * The column will be coerced to UInt32. Give this dtype to make the coercion a no-op. + */ + repeatBy(by: Expr | string): Expr; + /** Reverse the arrays in the list */ + reverse(): 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). + */ + shift(periods?: number): Expr; + shift({ periods }: { periods: 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). + * @param fillValue Fill null values with the result of this expression. + */ + shiftAndFill(periods: number, fillValue: Expr): Expr; + shiftAndFill({ + periods, + fillValue, + }: { periods: number; fillValue: Expr }): Expr; + /** + * Compute the sample skewness of a data set. + * For normally distributed data, the skewness should be about zero. For + * unimodal continuous distributions, a skewness value greater than zero means + * that there is more weight in the right tail of the distribution. + * ___ + * @param bias If False, then the calculations are corrected for statistical bias. + */ + skew(bias?: boolean): Expr; + skew({ bias }: { bias: boolean }): Expr; + /** Slice the Series. */ + slice(offset: number | Expr, length: number | Expr): Expr; + slice({ + offset, + length, + }: { offset: number | Expr; length: number | Expr }): Expr; + /** + * Sort this column. In projection/ selection context the whole column is sorted. + * @param reverse + * * false -> order from small to large. + * * true -> order from large to small. + * @param nullsLast If true nulls are considered to be larger than any valid value + */ + sort(reverse?: boolean, nullsLast?: boolean): Expr; + sort({ + reverse, + nullsLast, + }: { reverse?: boolean; nullsLast?: boolean }): Expr; + /** + * Sort this column by the ordering of another column, or multiple other columns. + In projection/ selection context the whole column is sorted. + If used in a groupby context, the groups are sorted. - export import DateTimeFunctions = dt.ExprDateTimeFunctions; - export import ListFunctions = lst.ExprListFunctions; - export import StringFunctions = str.ExprStringFunctions; - export import StructFunctions = struct.ExprStructFunctions; + Parameters + ---------- + @param by + The column(s) used for sorting. + @param reverse + false -> order from small to large. + true -> order from large to small. + */ + sortBy( + by: ExprOrString[] | ExprOrString, + reverse?: boolean | boolean[], + ): Expr; + sortBy(options: { + by: ExprOrString[] | ExprOrString; + reverse?: boolean | boolean[]; + }): Expr; + /** Get standard deviation. */ + std(): Expr; + /** Add a suffix the to root column name of the expression. */ + suffix(suffix: string): Expr; + /** + * Get sum value. + * @note + * Dtypes in {Int8, UInt8, Int16, UInt16} are cast to Int64 before summing to prevent overflow issues. + */ + sum(): Expr; + /** Take the last n values. */ + tail(length?: number): Expr; + tail({ length }: { length: number }): Expr; + /** + * Take values by index. + * @param index An expression that leads to a UInt32 dtyped Series. + */ + take(index: Expr | number[] | Series): Expr; + take({ index }: { index: Expr | number[] | Series }): Expr; + /** Take every nth value in the Series and return as a new Series. */ + takeEvery(n: number): Expr; + /** + * Get the unique values of this expression; + * @param maintainOrder Maintain order of data. This requires more work. + */ + unique(maintainOrder?: boolean | { maintainOrder: boolean }): Expr; + /** Returns a unit Series with the highest value possible for the dtype of this expression. */ + upperBound(): Expr; + /** Get variance. */ + var(): Expr; + /** Alias for filter: @see {@link filter} */ + where(predicate: Expr): Expr; +} + +/** @ignore */ +export const _Expr = (_expr: any): Expr => { + const unwrap = (method: string, ...args: any[]) => { + return _expr[method as any](...args); + }; + const wrap = (method, ...args): Expr => { + return _Expr(unwrap(method, ...args)); + }; + + const wrapExprArg = (method: string, lit = false) => (other: any) => { + const expr = exprToLitOrExpr(other, lit).inner(); + + return wrap(method, expr); + }; + + const rolling = + (method: string) => (opts, weights?, minPeriods?, center?): Expr => { + const windowSize = + opts?.["windowSize"] ?? (typeof opts === "number" ? opts : null); + if (windowSize === null) { + throw new Error("window size is required"); + } + const callOpts = { + windowSize: `${windowSize}i`, + weights: opts?.["weights"] ?? weights, + minPeriods: opts?.["minPeriods"] ?? minPeriods ?? windowSize, + center: opts?.["center"] ?? center ?? false, + }; + + return wrap(method, callOpts); + }; + + return { + _expr, + [Symbol.toStringTag]() { + return "Expr"; + }, + [INSPECT_SYMBOL]() { + return _expr.toString(); + }, + serialize(format): any { + return _expr.serialize(format); + }, + toString() { + return _expr.toString(); + }, + toJSON(...args: any[]) { + // this is passed by `JSON.stringify` when calling `toJSON()` + if (args[0] === "") { + return _expr.toJs(); + } + + return _expr.serialize("json").toString(); + }, + get str() { + return str.ExprStringFunctions(_expr); + }, + get lst() { + return lst.ExprListFunctions(_expr); + }, + get date() { + return dt.ExprDateTimeFunctions(_expr); + }, + get struct() { + return struct.ExprStructFunctions(_expr); + }, + abs() { + return _Expr(_expr.abs()); + }, + aggGroups() { + return _Expr(_expr.aggGroups()); + }, + alias(name) { + return _Expr(_expr.alias(name)); + }, + inner() { + return _expr; + }, + and(other) { + const expr = (exprToLitOrExpr(other, false) as any).inner(); + + return _Expr(_expr.and(expr)); + }, + argMax() { + return _Expr(_expr.argMax()); + }, + argMin() { + return _Expr(_expr.argMin()); + }, + argSort(reverse: any = false) { + reverse = reverse?.reverse ?? reverse; + + return _Expr(_expr.argSort(reverse)); + }, + argUnique() { + return _Expr(_expr.argUnique()); + }, + as(name) { + return _Expr(_expr.alias(name)); + }, + backwardFill() { + return _Expr(_expr.backwardFill()); + }, + cast(dtype, strict = false) { + return _Expr(_expr.cast(dtype, strict)); + }, + ceil() { + return _Expr(_expr.ceil()); + }, + clip(arg, max?) { + if (typeof arg === "number") { + return _Expr(_expr.clip(arg, max)); + } else { + return _Expr(_expr.clip(arg.min, arg.max)); + } + }, + count() { + return _Expr(_expr.count()); + }, + cumCount(reverse: any = false) { + reverse = reverse?.reverse ?? reverse; + + return _Expr(_expr.cumcount(reverse?.reverse ?? reverse)); + }, + cumMax(reverse: any = false) { + reverse = reverse?.reverse ?? reverse; + + return _Expr(_expr.cummax(reverse)); + }, + cumMin(reverse: any = false) { + reverse = reverse?.reverse ?? reverse; + + return _Expr(_expr.cummin(reverse)); + }, + cumProd(reverse: any = false) { + reverse = reverse?.reverse ?? reverse; + + return _Expr(_expr.cumprod(reverse)); + }, + cumSum(reverse: any = false) { + reverse = reverse?.reverse ?? reverse; + + return _Expr(_expr.cumsum(reverse)); + }, + diff(n, nullBehavior = "ignore") { + if (typeof n === "number") { + return _Expr(_expr.diff(n, nullBehavior)); + } else { + return _Expr(_expr.diff(n.n, n.nullBehavior)); + } + }, + dot(other) { + const expr = (exprToLitOrExpr(other, false) as any).inner(); + + return _Expr(_expr.dot(expr)); + }, + exclude(...columns) { + return _Expr(_expr.exclude(columns.flat(2))); + }, + explode() { + return _Expr(_expr.explode()); + }, + extend(o, n?) { + if (n !== null && typeof n === "number") { + return _Expr(_expr.extendConstant(o, n)); + } + + return _Expr(_expr.extendConstant(o.value, o.n)); + }, + extendConstant(o, n?) { + if (n !== null && typeof n === "number") { + return _Expr(_expr.extendConstant(o, n)); + } + + return _Expr(_expr.extendConstant(o.value, o.n)); + }, + fillNan(other) { + const expr = (exprToLitOrExpr(other, true) as any).inner(); + + return _Expr(_expr.fillNan(expr)); + }, + fillNull(fillValue) { + if ( + ["backward", "forward", "mean", "min", "max", "zero", "one"].includes( + fillValue, + ) + ) { + return _Expr(_expr.fillNullWithStrategy(fillValue)); + } + + const expr = exprToLitOrExpr(fillValue).inner(); + + return _Expr(_expr.fillNull(expr)); + }, + filter(predicate) { + const expr = exprToLitOrExpr(predicate).inner(); + + return _Expr(_expr.filter(expr)); + }, + first() { + return _Expr(_expr.first()); + }, + flatten() { + return _Expr(_expr.explode()); + }, + floor() { + return _Expr(_expr.floor()); + }, + forwardFill() { + return _Expr(_expr.forwardFill()); + }, + hash(obj: any = 0, k1 = 1, k2 = 2, k3 = 3) { + if (typeof obj === "number" || typeof obj === "bigint") { + return wrap("hash", BigInt(obj), BigInt(k1), BigInt(k2), BigInt(k3)); + } + const o = { k0: obj, k1: k1, k2: k2, k3: k3, ...obj }; + + return wrap( + "hash", + BigInt(o.k0), + BigInt(o.k1), + BigInt(o.k2), + BigInt(o.k3), + ); + }, + head(length) { + if (typeof length === "number") { + return wrap("head", length); + } + return wrap("head", length.length); + }, + interpolate(method: InterpolationMethod = "linear") { + return _Expr(_expr.interpolate(method)); + }, + isDuplicated() { + return _Expr(_expr.isDuplicated()); + }, + isFinite() { + return _Expr(_expr.isFinite()); + }, + isInfinite() { + return _Expr(_expr.isInfinite()); + }, + isFirst() { + return _Expr(_expr.isFirst()); + }, + isNan() { + return _Expr(_expr.isNan()); + }, + isNotNan() { + return _Expr(_expr.isNotNan()); + }, + isNotNull() { + return _Expr(_expr.isNotNull()); + }, + isNull() { + return _Expr(_expr.isNull()); + }, + isUnique() { + return _Expr(_expr.isUnique()); + }, + isIn(other) { + if (Array.isArray(other)) { + other = pli.lit(Series(other).inner()); + } else { + other = exprToLitOrExpr(other, false).inner(); + } - export import List = lst.ExprList - export import Datetime = dt.ExprDateTime - export import String = str.ExprString - export import Struct = struct.ExprStruct + return wrap("isIn", other); + }, + keepName() { + return _Expr(_expr.keepName()); + }, + kurtosis(obj?, bias = true) { + const fisher = obj?.["fisher"] ?? (typeof obj === "boolean" ? obj : true); + bias = obj?.["bias"] ?? bias; + + return _Expr(_expr.kurtosis(fisher, bias)); + }, + last() { + return _Expr(_expr.last()); + }, + list() { + return _Expr(_expr.list()); + }, + lowerBound() { + return _Expr(_expr.lowerBound()); + }, + max() { + return _Expr(_expr.max()); + }, + mean() { + return _Expr(_expr.mean()); + }, + median() { + return _Expr(_expr.median()); + }, + min() { + return _Expr(_expr.min()); + }, + mode() { + return _Expr(_expr.mode()); + }, + not() { + return _Expr(_expr.not()); + }, + nUnique() { + return _Expr(_expr.nUnique()); + }, + or(other) { + const expr = exprToLitOrExpr(other).inner(); + + return _Expr(_expr.or(expr)); + }, + over(...exprs) { + const partitionBy = selectionToExprList(exprs, false); + + return wrap("over", partitionBy); + }, + pow(exponent) { + return _Expr(_expr.pow(exponent?.exponent ?? exponent)); + }, + prefix(prefix) { + return _Expr(_expr.prefix(prefix)); + }, + quantile(quantile, interpolation: InterpolationMethod = "nearest") { + if (Expr.isExpr(quantile)) { + quantile = quantile._expr; + } else { + quantile = pli.lit(quantile); + } + + return _Expr(_expr.quantile(quantile, interpolation)); + }, + rank(method: any = "average", reverse = false) { + return _Expr( + _expr.rank(method?.method ?? method, method?.reverse ?? reverse), + ); + }, + reinterpret(signed: any = true) { + signed = signed?.signed ?? signed; + + return _Expr(_expr.reinterpret(signed)); + }, + repeatBy(expr) { + const e = exprToLitOrExpr(expr, false)._expr; + + return _Expr(_expr.repeatBy(e)); + }, + reverse() { + return _Expr(_expr.reverse()); + }, + rollingMax: rolling("rollingMax"), + rollingMean: rolling("rollingMean"), + rollingMin: rolling("rollingMin"), + rollingSum: rolling("rollingSum"), + rollingStd: rolling("rollingStd"), + rollingVar: rolling("rollingVar"), + rollingMedian: rolling("rollingMedian"), + rollingQuantile( + val, + interpolation?, + windowSize?, + weights?, + minPeriods?, + center?, + ) { + if (typeof val === "number") { + return wrap("rollingQuantile", val, interpolation ?? "nearest", { + windowSize: `${windowSize}i`, + weights, + minPeriods, + center, + }); + } + windowSize = + val?.["windowSize"] ?? (typeof val === "number" ? val : null); + if (windowSize === null) { + throw new Error("window size is required"); + } + const options = { + windowSize: `${windowSize}i`, + weights: val?.["weights"] ?? weights, + minPeriods: val?.["minPeriods"] ?? minPeriods ?? windowSize, + center: val?.["center"] ?? center ?? false, + }; + + return wrap( + "rollingQuantile", + val.quantile, + val.interpolation ?? "nearest", + options, + ); + }, + rollingSkew(val, bias = true) { + if (typeof val === "number") { + return wrap("rollingSkew", val, bias); + } + + return wrap("rollingSkew", val.windowSize, val.bias ?? bias); + }, + round(decimals) { + return _Expr(_expr.round(decimals?.decimals ?? decimals)); + }, + sample(opts?, frac?, withReplacement = false, seed?) { + if (opts?.n !== undefined || opts?.frac !== undefined) { + return (this as any).sample( + opts.n, + opts.frac, + opts.withReplacement, + seed, + ); + } + if (typeof opts === "number") { + throw new Error("sample_n is not yet supported for expr"); + } + if (typeof frac === "number") { + return wrap("sampleFrac", frac, withReplacement, false, seed); + } else { + throw new TypeError("must specify either 'frac' or 'n'"); + } + }, + shift(periods) { + return _Expr(_expr.shift(periods)); + }, + shiftAndFill(optOrPeriods, fillValue?) { + if (typeof optOrPeriods === "number") { + fillValue = exprToLitOrExpr(fillValue).inner(); + + return wrap("shiftAndFill", optOrPeriods, fillValue); + } else { + fillValue = exprToLitOrExpr(optOrPeriods.fillValue).inner(); + const periods = optOrPeriods.periods; + + return wrap("shiftAndFill", periods, fillValue); + } + }, + skew(bias) { + return wrap("skew", bias?.bias ?? bias ?? true); + }, + slice(arg, len?) { + if (typeof arg === "number") { + return wrap("slice", pli.lit(arg), pli.lit(len)); + } + + return wrap("slice", pli.lit(arg.offset), pli.lit(arg.length)); + }, + sort(reverse: any = false, nullsLast = false) { + if (typeof reverse === "boolean") { + return wrap("sortWith", reverse, nullsLast); + } + + return wrap( + "sortWith", + reverse?.reverse ?? false, + reverse?.nullsLast ?? nullsLast, + ); + }, + sortBy(arg, reverse = false) { + if (arg?.by !== undefined) { + return this.sortBy(arg.by, arg.reverse); + } + + reverse = Array.isArray(reverse) ? reverse.flat() : ([reverse] as any); + const by = selectionToExprList(arg, false); + + return wrap("sortBy", by, reverse); + }, + std() { + return _Expr(_expr.std()); + }, + suffix(suffix) { + return _Expr(_expr.suffix(suffix)); + }, + sum() { + return _Expr(_expr.sum()); + }, + tail(length) { + return _Expr(_expr.tail(length)); + }, + + take(indices) { + if (Array.isArray(indices)) { + indices = pli.lit(Series(indices).inner()); + } else { + indices = indices.inner(); + } + + return wrap("take", indices); + }, + takeEvery(n) { + return _Expr(_expr.takeEvery(n)); + }, + unique(opt?) { + if (opt) { + return wrap("unique_stable"); + } + + return wrap("unique"); + }, + upperBound() { + return _Expr(_expr.upperBound()); + }, + where(expr) { + return this.filter(expr); + }, + var() { + return _Expr(_expr.var()); + }, + add: wrapExprArg("add"), + sub: wrapExprArg("sub"), + div: wrapExprArg("div"), + mul: wrapExprArg("mul"), + rem: wrapExprArg("rem"), + + plus: wrapExprArg("add"), + minus: wrapExprArg("sub"), + divideBy: wrapExprArg("div"), + multiplyBy: wrapExprArg("mul"), + modulo: wrapExprArg("rem"), + + eq: wrapExprArg("eq"), + equals: wrapExprArg("eq"), + gtEq: wrapExprArg("gtEq"), + greaterThanEquals: wrapExprArg("gtEq"), + gt: wrapExprArg("gt"), + greaterThan: wrapExprArg("gt"), + ltEq: wrapExprArg("ltEq"), + lessThanEquals: wrapExprArg("ltEq"), + lt: wrapExprArg("lt"), + lessThan: wrapExprArg("lt"), + neq: wrapExprArg("neq"), + notEquals: wrapExprArg("neq"), + } as Expr; +}; + +export interface ExprConstructor extends Deserialize { + isExpr(arg: any): arg is Expr; } -export = expr +const isExpr = (anyVal: any): anyVal is Expr => { + try { + return anyVal?.[Symbol.toStringTag]?.() === "Expr"; + } catch (err) { + return false; + } +}; + +const deserialize = (buf, format) => { + return _Expr(pli.JsExpr.deserialize(buf, format)); +}; + +export const Expr: ExprConstructor = Object.assign(_Expr, { + isExpr, + deserialize, +}); + +/** @ignore */ +export const exprToLitOrExpr = (expr: any, stringToLit = true): Expr => { + if (typeof expr === "string" && !stringToLit) { + return _Expr(pli.col(expr)); + } else if (Expr.isExpr(expr)) { + return expr; + } else if (Series.isSeries(expr)) { + return _Expr(pli.lit((expr as any)._s)); + } else { + return _Expr(pli.lit(expr)); + } +}; diff --git a/polars/lazy/expr/list.ts b/polars/lazy/expr/list.ts index 75d941dbd..b594f1a89 100644 --- a/polars/lazy/expr/list.ts +++ b/polars/lazy/expr/list.ts @@ -1,13 +1,13 @@ import { Expr, _Expr } from "../expr"; import { ListFunctions } from "../../shared_traits"; -import { Series } from "../../series/series"; +import { Series } from "../../series"; import pli from "../../internals/polars_internal"; -import { concatList, lit } from "../functions"; +import { concatList } from "../functions"; /** * namespace containing expr list functions */ -export type ExprList = ListFunctions; +export interface ExprList extends ListFunctions {} export const ExprListFunctions = (_expr: any): ExprList => { const wrap = (method, ...args: any[]): Expr => { return _Expr(_expr[method](...args)); diff --git a/polars/lazy/expr/string.ts b/polars/lazy/expr/string.ts index 906dc8624..59d9448b2 100644 --- a/polars/lazy/expr/string.ts +++ b/polars/lazy/expr/string.ts @@ -1,11 +1,12 @@ -import {DataType} from "../../datatypes"; +import { StringFunctions } from "../../shared_traits"; +import { DataType } from "../../datatypes"; import { regexToString } from "../../utils"; -import {Expr, _Expr} from "../expr"; +import { Expr, _Expr } from "../expr"; /** * namespace containing expr string functions */ -export interface ExprString { +export interface StringNamespace extends StringFunctions { /** * Vertically concat the values in the Series to a single string value. * @example @@ -272,19 +273,19 @@ export interface ExprString { strptime(datatype: DataType.Datetime, fmt?: string): Expr; } -export const ExprStringFunctions = (_expr: any): ExprString => { +export const ExprStringFunctions = (_expr: any): StringNamespace => { const wrap = (method, ...args: any[]): Expr => { return _Expr(_expr[method](...args)); }; const handleDecode = (encoding, strict) => { switch (encoding) { - case "hex": - return wrap(`strHexDecode`, strict); - case "base64": - return wrap(`strBase64Decode`, strict); - default: - throw new RangeError("supported encodings are 'hex' and 'base64'"); + case "hex": + return wrap("strHexDecode", strict); + case "base64": + return wrap("strBase64Decode", strict); + default: + throw new RangeError("supported encodings are 'hex' and 'base64'"); } }; @@ -295,8 +296,8 @@ export const ExprStringFunctions = (_expr: any): ExprString => { contains(pat: string | RegExp) { return wrap("strContains", regexToString(pat)); }, - decode(arg, strict=false) { - if(typeof arg === "string") { + decode(arg, strict = false) { + if (typeof arg === "string") { return handleDecode(arg, strict); } @@ -304,12 +305,12 @@ export const ExprStringFunctions = (_expr: any): ExprString => { }, encode(encoding) { switch (encoding) { - case "hex": - return wrap(`strHexEncode`); - case "base64": - return wrap(`strBase64Encode`); - default: - throw new RangeError("supported encodings are 'hex' and 'base64'"); + case "hex": + return wrap("strHexEncode"); + case "base64": + return wrap("strBase64Encode"); + default: + throw new RangeError("supported encodings are 'hex' and 'base64'"); } }, extract(pat: string | RegExp, groupIndex: number) { @@ -333,7 +334,7 @@ export const ExprStringFunctions = (_expr: any): ExprString => { rstrip() { return wrap("strRstrip"); }, - padStart(length: number, fillChar: string){ + padStart(length: number, fillChar: string) { return wrap("strPadStart", length, fillChar); }, zFill(length: number) { @@ -346,7 +347,8 @@ export const ExprStringFunctions = (_expr: any): ExprString => { return wrap("strSlice", start, length); }, split(by: string, options?) { - const inclusive = typeof options === "boolean" ? options : options?.inclusive; + const inclusive = + typeof options === "boolean" ? options : options?.inclusive; return wrap("strSplit", by, inclusive); }, @@ -359,7 +361,9 @@ export const ExprStringFunctions = (_expr: any): ExprString => { } else if (dtype.equals(DataType.Datetime("ms"))) { return wrap("strParseDatetime", fmt, false, false); } else { - throw new Error(`only "DataType.Date" and "DataType.Datetime" are supported`); + throw new Error( + `only "DataType.Date" and "DataType.Datetime" are supported`, + ); } }, toLowerCase() { diff --git a/polars/lazy/expr/struct.ts b/polars/lazy/expr/struct.ts index 52f8f2871..ff8603888 100644 --- a/polars/lazy/expr/struct.ts +++ b/polars/lazy/expr/struct.ts @@ -1,12 +1,21 @@ -import pli from "../../internals/polars_internal"; -import {_Expr, Expr} from "../expr"; +import { _Expr, Expr } from "../expr"; +/** + * Struct functions + */ export interface ExprStruct { + /** + * Access a field by name + * @param name - name of the field + */ field(name: string): Expr; - renameFields(names: string[]): Expr + /** + * Rename the fields of a struct + * @param names - new names of the fields + */ + renameFields(names: string[]): Expr; } - export const ExprStructFunctions = (_expr: any): ExprStruct => { return { field(name) { @@ -14,6 +23,6 @@ export const ExprStructFunctions = (_expr: any): ExprStruct => { }, renameFields(names) { return _Expr(_expr.structRenameFields(names)); - } + }, }; }; diff --git a/polars/lazy/functions.ts b/polars/lazy/functions.ts index b0a8df80a..b7d6f8bdc 100644 --- a/polars/lazy/functions.ts +++ b/polars/lazy/functions.ts @@ -1,7 +1,7 @@ -import {Expr, _Expr, exprToLitOrExpr} from "./expr"; -import {Series} from "../series/series"; -import {DataFrame} from "../dataframe"; -import {ExprOrString, range, selectionToExprList} from "../utils"; +import { Expr, _Expr, exprToLitOrExpr } from "./expr"; +import { Series } from "../series"; +import { DataFrame } from "../dataframe"; +import { ExprOrString, range, selectionToExprList } from "../utils"; import pli from "../internals/polars_internal"; /** @@ -13,11 +13,11 @@ import pli from "../internals/polars_internal"; * @param col * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "ham": [1, 2, 3], - * >>> "hamburger": [11, 22, 33], - * >>> "foo": [3, 2, 1]}) - * >>> df.select(col("foo")) + * > df = pl.DataFrame({ + * > "ham": [1, 2, 3], + * > "hamburger": [11, 22, 33], + * > "foo": [3, 2, 1]}) + * > df.select(col("foo")) * shape: (3, 1) * ╭─────╮ * │ foo │ @@ -30,7 +30,7 @@ import pli from "../internals/polars_internal"; * ├╌╌╌╌╌┤ * │ 1 │ * ╰─────╯ - * >>> df.select(col("*")) + * > df.select(col("*")) * shape: (3, 3) * ╭─────┬───────────┬─────╮ * │ ham ┆ hamburger ┆ foo │ @@ -43,7 +43,7 @@ import pli from "../internals/polars_internal"; * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┤ * │ 3 ┆ 33 ┆ 1 │ * ╰─────┴───────────┴─────╯ - * >>> df.select(col("^ham.*$")) + * > df.select(col("^ham.*$")) * shape: (3, 2) * ╭─────┬───────────╮ * │ ham ┆ hamburger │ @@ -56,7 +56,7 @@ import pli from "../internals/polars_internal"; * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ * │ 3 ┆ 33 │ * ╰─────┴───────────╯ - * >>> df.select(col("*").exclude("ham")) + * > df.select(col("*").exclude("ham")) * shape: (3, 2) * ╭───────────┬─────╮ * │ hamburger ┆ foo │ @@ -69,7 +69,7 @@ import pli from "../internals/polars_internal"; * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┤ * │ 33 ┆ 1 │ * ╰───────────┴─────╯ - * >>> df.select(col(["hamburger", "foo"]) + * > df.select(col(["hamburger", "foo"]) * shape: (3, 2) * ╭───────────┬─────╮ * │ hamburger ┆ foo │ @@ -82,7 +82,7 @@ import pli from "../internals/polars_internal"; * ├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┤ * │ 33 ┆ 1 │ * ╰───────────┴─────╯ - * >>> df.select(col(pl.Series(["hamburger", "foo"])) + * > df.select(col(pl.Series(["hamburger", "foo"])) * shape: (3, 2) * ╭───────────┬─────╮ * │ hamburger ┆ foo │ @@ -108,10 +108,9 @@ export function col(col: string | string[] | Series): Expr { } } -export function cols(col: string | string[]): Expr -export function cols(col: string, ...cols: string[]): Expr +export function cols(col: string | string[]): Expr; +export function cols(col: string, ...cols: string[]): Expr; export function cols(...cols): Expr { - return col(cols.flat()); } @@ -120,7 +119,6 @@ export function lit(value: any): Expr { value = Series(value); } if (Series.isSeries(value)) { - return _Expr(pli.lit(value.inner())); } @@ -143,14 +141,29 @@ export function lit(value: any): Expr { * @param eager - If eager evaluation is `true`, a Series is returned instead of an Expr * @example * ``` - * >>> df.lazy() - * >>> .filter(pl.col("foo").lt(pl.arange(0, 100))) - * >>> .collect() + * > df.lazy() + * > .filter(pl.col("foo").lt(pl.arange(0, 100))) + * > .collect() * ``` */ -export function arange(opts: {low: any, high: any, step: number, eager: boolean}); -export function arange(low: any, high?: any, step?: number, eager?: true): Series; -export function arange(low: any, high?: any, step?: number, eager?: false): Expr; +export function arange(opts: { + low: any; + high: any; + step: number; + eager: boolean; +}); +export function arange( + low: any, + high?: any, + step?: number, + eager?: true, +): Series; +export function arange( + low: any, + high?: any, + step?: number, + eager?: false, +): Expr; export function arange(opts: any, high?, step?, eager?): Series | Expr { if (typeof opts?.low === "number") { return arange(opts.low, opts.high, opts.step, opts.eager); @@ -158,9 +171,11 @@ export function arange(opts: any, high?, step?, eager?): Series | Expr { const low = exprToLitOrExpr(opts, false); high = exprToLitOrExpr(high, false); if (eager) { - const df = DataFrame({"a": [1]}); + const df = DataFrame({ a: [1] }); - return df.select(arange(low, high, step).alias("arange") as any).getColumn("arange") as any; + return df + .select(arange(low, high, step).alias("arange") as any) + .getColumn("arange") as any; } return _Expr(pli.arange(low, high, step)); @@ -177,9 +192,12 @@ export function all(): Expr { * If there are duplicates in the first column, the second column will be used to determine the ordering * and so on. */ -export function argSortBy(exprs: Expr[] | string[], reverse: boolean | boolean[] = false): Expr { +export function argSortBy( + exprs: Expr[] | string[], + reverse: boolean | boolean[] = false, +): Expr { if (!Array.isArray(reverse)) { - reverse = Array.from({length: exprs.length}, () => reverse) as any; + reverse = Array.from({ length: exprs.length }, () => reverse) as any; } const by = selectionToExprList(exprs); @@ -196,9 +214,13 @@ export function avg(column: Series | string): number | Expr { * Concat the arrays in a Series dtype List in linear time. * @param exprs Columns to concat into a List Series */ -export function concatList(exprs: ExprOrString[]): Expr -export function concatList(expr: ExprOrString, ...exprs: ExprOrString[]): Expr -export function concatList(expr: ExprOrString, expr2: ExprOrString, ...exprs: ExprOrString[]): Expr +export function concatList(exprs: ExprOrString[]): Expr; +export function concatList(expr: ExprOrString, ...exprs: ExprOrString[]): Expr; +export function concatList( + expr: ExprOrString, + expr2: ExprOrString, + ...exprs: ExprOrString[] +): Expr; export function concatList(...exprs): Expr { const items = selectionToExprList(exprs as any, false); @@ -206,7 +228,7 @@ export function concatList(...exprs): Expr { } /** Concat Utf8 Series in linear time. Non utf8 columns are cast to utf8. */ -export function concatString(opts: {exprs: ExprOrString[], sep: string}); +export function concatString(opts: { exprs: ExprOrString[]; sep: string }); export function concatString(exprs: ExprOrString[], sep?: string); export function concatString(opts, sep = ",") { if (opts?.exprs) { @@ -215,12 +237,11 @@ export function concatString(opts, sep = ",") { const items = selectionToExprList(opts as any, false); return (Expr as any)(pli.concatStr(items, sep)); - } /** Count the number of values in this column. */ -export function count(column: string): Expr -export function count(column: Series): number +export function count(column: string): Expr; +export function count(column: Series): number; export function count(column) { if (Series.isSeries(column)) { return column.len(); @@ -241,28 +262,30 @@ export function cov(a: ExprOrString, b: ExprOrString): Expr { * * Syntactic sugar for: * ``` - * >>> pl.col("*").exclude(columns) + * > pl.col("*").exclude(columns) * ``` */ -export function exclude(columns: string[] | string): Expr -export function exclude(col: string, ...cols: string[]): Expr +export function exclude(columns: string[] | string): Expr; +export function exclude(col: string, ...cols: string[]): Expr; export function exclude(...columns): Expr { return col("*").exclude(columns as any); } /** Get the first value. */ -export function first(): Expr -export function first(column: string): Expr -export function first(column: Series): T +export function first(): Expr; +export function first(column: string): Expr; +export function first(column: Series): T; export function first(column?: string | Series): Expr | T { - if(!column) { + if (!column) { return _Expr(pli.first()); } if (Series.isSeries(column)) { if (column.length) { return column.get(0); } else { - throw new RangeError("The series is empty, so no first value can be returned."); + throw new RangeError( + "The series is empty, so no first value can be returned.", + ); } } else { return col(column).first(); @@ -274,11 +297,11 @@ export function first(column?: string | Series): Expr | T { * Note: strings will be interpolated as `col()`. if you want a literal string, use `lit()` * @example * ``` - * >>> df = pl.DataFrame({ + * > df = pl.DataFrame({ * ... "a": ["a", "b", "c"], * ... "b": [1, 2, 3], * ... }) - * >>> df.select( + * > df.select( * ... pl.format("foo_{}_bar_{}", pl.col("a"), "b").alias("fmt"), * ... ) * shape: (3, 1) @@ -295,15 +318,20 @@ export function first(column?: string | Series): Expr | T { * └─────────────┘ * * // You can use format as tag function as well - * >>> pl.format("foo_{}_bar_{}", pl.col("a"), "b") // is the same as - * >>> pl.format`foo_${pl.col("a")}_bar_${"b"}` + * > pl.format("foo_{}_bar_{}", pl.col("a"), "b") // is the same as + * > pl.format`foo_${pl.col("a")}_bar_${"b"}` * ``` */ -export function format(strings: string | TemplateStringsArray, ...expr: ExprOrString[]): Expr { +export function format( + strings: string | TemplateStringsArray, + ...expr: ExprOrString[] +): Expr { if (typeof strings === "string") { const s = strings.split("{}"); if (s.length - 1 !== expr.length) { - throw new RangeError("number of placeholders should equal the number of arguments"); + throw new RangeError( + "number of placeholders should equal the number of arguments", + ); } return format(s as any, ...expr); @@ -333,7 +361,6 @@ export function head(column: Series | ExprOrString, n?): Series | Expr { return column.head(n); } else { return exprToLitOrExpr(column, false).head(n); - } } @@ -343,7 +370,9 @@ export function last(column: ExprOrString | Series): any { if (column.length) { return column.get(-1); } else { - throw new RangeError("The series is empty, so no last value can be returned."); + throw new RangeError( + "The series is empty, so no last value can be returned.", + ); } } else { return exprToLitOrExpr(column, false).last(); @@ -383,7 +412,6 @@ export function nUnique(column: Series | ExprOrString): number | Expr { return exprToLitOrExpr(column, false).nUnique(); } - /** Compute the pearson's correlation between two columns. */ export function pearsonCorr(a: ExprOrString, b: ExprOrString): Expr { a = exprToLitOrExpr(a, false); @@ -420,7 +448,6 @@ export function spearmanRankCorr(a: ExprOrString, b: ExprOrString): Expr { return _Expr(pli.spearmanRankCorr(a, b, null, false)); } - /** Get the last n rows of an Expression. */ export function tail(column: ExprOrString, n?: number): Expr; export function tail(column: Series, n?: number): Series; @@ -437,7 +464,6 @@ export function list(column: ExprOrString): Expr { return exprToLitOrExpr(column, false).list(); } - /** Collect several columns into a Series of dtype Struct Parameters @@ -450,7 +476,7 @@ export function list(column: ExprOrString): Expr { Examples -------- ``` - >>> pl.DataFrame( + >pl.DataFrame( ... { ... "int": [1, 2], ... "str": ["a", "b"], @@ -470,12 +496,12 @@ export function list(column: ExprOrString): Expr { └───────────────────────┘ // Only collect specific columns as a struct: - >>> df = pl.DataFrame({ + >df = pl.DataFrame({ ... "a": [1, 2, 3, 4], ... "b": ["one", "two", "three", "four"], ... "c": [9, 8, 7, 6] ... }) - >>> df.withColumn(pl.struct(pl.col(["a", "b"])).alias("a_and_b")) + >df.withColumn(pl.struct(pl.col(["a", "b"])).alias("a_and_b")) shape: (4, 4) ┌─────┬───────┬─────┬───────────────────────────────┐ │ a ┆ b ┆ c ┆ a_and_b │ @@ -492,20 +518,23 @@ export function list(column: ExprOrString): Expr { └─────┴───────┴─────┴───────────────────────────────┘ ``` */ -export function struct(exprs: Series[]): Series -export function struct(exprs: ExprOrString | ExprOrString[]): Expr -export function struct(exprs: ExprOrString | ExprOrString[] | Series[]): Expr | Series { +export function struct(exprs: Series[]): Series; +export function struct(exprs: ExprOrString | ExprOrString[]): Expr; +export function struct( + exprs: ExprOrString | ExprOrString[] | Series[], +): Expr | Series { exprs = Array.isArray(exprs) ? exprs : [exprs]; if (Series.isSeries(exprs[0])) { - return select(_Expr(pli.asStruct(exprs.map(e => pli.lit(e.inner()))))).toSeries(); + return select( + _Expr(pli.asStruct(exprs.map((e) => pli.lit(e.inner())))), + ).toSeries(); } exprs = selectionToExprList(exprs); return _Expr(pli.asStruct(exprs)); } - /** * Alias for an element in evaluated in an `eval` expression. @@ -513,8 +542,8 @@ export function struct(exprs: ExprOrString | ExprOrString[] | Series[]): Expr | * * A horizontal rank computation by taking the elements of a list * - * >>> df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]}) - * >>> df.withColumn( + * >df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]}) + * >df.withColumn( * ... pl.concatList(["a", "b"]).arr.eval(pl.element().rank()).alias("rank") * ... ) * shape: (3, 3) @@ -532,8 +561,8 @@ export function struct(exprs: ExprOrString | ExprOrString[] | Series[]): Expr | * * A mathematical operation on array elements * - * >>> df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]}) - * >>> df.withColumn( + * >df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]}) + * >df.withColumn( * ... pl.concatList(["a", "b"]).arr.eval(pl.element().multiplyBy(2)).alias("a_b_doubled") * ... ) * shape: (3, 3) diff --git a/polars/lazy/groupby.ts b/polars/lazy/groupby.ts index 1f62b2dd8..f32a6d46d 100644 --- a/polars/lazy/groupby.ts +++ b/polars/lazy/groupby.ts @@ -1,29 +1,42 @@ -import {Expr} from "./expr"; -import {selectionToExprList} from "../utils"; -import {_LazyDataFrame, LazyDataFrame} from "./dataframe"; - -export interface LazyGroupBy { - agg(...aggs: Expr[]): LazyDataFrame - head(n?: number): LazyDataFrame - tail(n?: number): LazyDataFrame -} - - -export const LazyGroupBy = (_lgb: any): LazyGroupBy => { +import { Expr } from "./expr"; +import { selectionToExprList } from "../utils"; +import { _LazyDataFrame, LazyDataFrame } from "./dataframe"; +/** @ignore */ +export const _LazyGroupBy = (_lgb: any): LazyGroupBy => { return { - agg(...aggs: Expr[]) { - aggs = selectionToExprList(aggs, false); + agg(...aggs: Expr[]) { + aggs = selectionToExprList(aggs, false); const ret = _lgb.agg(aggs.flat()); return _LazyDataFrame(ret); }, - head(n=5) { + head(n = 5) { return _LazyDataFrame(_lgb.head(n)); }, - tail(n=5) { + tail(n = 5) { return _LazyDataFrame(_lgb.tail(n)); - - } + }, }; }; + +/** + * LazyGroupBy + * @category lazy + */ +export interface LazyGroupBy { + /** + * Aggregate the groupby. + */ + agg(...aggs: Expr[]): LazyDataFrame; + /** + * Return the first n rows of the groupby. + * @param n number of rows to return + */ + head(n?: number): LazyDataFrame; + /** + * Return the last n rows of the groupby. + * @param n number of rows to return + */ + tail(n?: number): LazyDataFrame; +} diff --git a/polars/lazy/index.ts b/polars/lazy/index.ts index 7b637d549..00b70946c 100644 --- a/polars/lazy/index.ts +++ b/polars/lazy/index.ts @@ -1,13 +1,4 @@ - -import * as func from "./functions"; -import * as gb from "./groupby"; -import * as expr from "./expr"; -import * as whenthen from "./whenthen"; -namespace lazy { - export import GroupBy = gb.LazyGroupBy; - export import Expr = expr; - export import funcs = func; - export import when = whenthen; -} - -export = lazy; +export * from "./functions"; +export * from "./groupby"; +export * from "./expr"; +export * from "./whenthen"; diff --git a/polars/lazy/whenthen.ts b/polars/lazy/whenthen.ts index d210fda83..b31b991f5 100644 --- a/polars/lazy/whenthen.ts +++ b/polars/lazy/whenthen.ts @@ -1,63 +1,65 @@ -import {Expr} from "./expr"; +import { Expr } from "./expr"; import pli from "../internals/polars_internal"; - export interface When { /** Values to return in case of the predicate being `true`.*/ - then(expr: Expr): WhenThen + then(expr: Expr): WhenThen; } export interface WhenThen { /** Start another when, then, otherwise layer. */ - when(predicate: Expr): WhenThenThen + when(predicate: Expr): WhenThenThen; /** Values to return in case of the predicate being `false`. */ - otherwise(expr: Expr): Expr + otherwise(expr: Expr): Expr; } export interface WhenThenThen { /** Start another when, then, otherwise layer. */ - when(predicate: Expr): WhenThenThen + when(predicate: Expr): WhenThenThen; /** Values to return in case of the predicate being `true`. */ - then(expr: Expr): WhenThenThen + then(expr: Expr): WhenThenThen; /** Values to return in case of the predicate being `false`. */ - otherwise(expr: Expr): Expr + otherwise(expr: Expr): Expr; } function WhenThenThen(_whenthenthen: any): WhenThenThen { return { - when: ({_expr}: Expr): WhenThenThen => WhenThenThen(_whenthenthen.when(_expr)), - then: ({_expr}: Expr): WhenThenThen => WhenThenThen(_whenthenthen.then(_expr)), - otherwise: ({_expr}: Expr): Expr => (Expr as any)(_whenthenthen.otherwise(_expr)) + when: ({ _expr }: Expr): WhenThenThen => + WhenThenThen(_whenthenthen.when(_expr)), + then: ({ _expr }: Expr): WhenThenThen => + WhenThenThen(_whenthenthen.then(_expr)), + otherwise: ({ _expr }: Expr): Expr => + (Expr as any)(_whenthenthen.otherwise(_expr)), }; } function WhenThen(_whenthen: any): WhenThen { return { - when: ({_expr}: Expr): WhenThenThen => WhenThenThen(_whenthen.when(_expr)), - otherwise: ({_expr}: Expr): Expr => (Expr as any)(_whenthen.otherwise(_expr)) + when: ({ _expr }: Expr): WhenThenThen => + WhenThenThen(_whenthen.when(_expr)), + otherwise: ({ _expr }: Expr): Expr => + (Expr as any)(_whenthen.otherwise(_expr)), }; } - /** * Utility function. * @see {@link when} */ function When(_when: any): When { return { - then: ({_expr}: Expr): WhenThen => WhenThen(_when.then(_expr)) + then: ({ _expr }: Expr): WhenThen => WhenThen(_when.then(_expr)), }; } - /** * Start a when, then, otherwise expression. * * @example * ``` * // Below we add a column with the value 1, where column "foo" > 2 and the value -1 where it isn't. - * >>> df = pl.DataFrame({"foo": [1, 3, 4], "bar": [3, 4, 0]}) - * >>> df.withColumn(pl.when(pl.col("foo").gt(2)).then(pl.lit(1)).otherwise(pl.lit(-1))) + * > df = pl.DataFrame({"foo": [1, 3, 4], "bar": [3, 4, 0]}) + * > df.withColumn(pl.when(pl.col("foo").gt(2)).then(pl.lit(1)).otherwise(pl.lit(-1))) * shape: (3, 3) * ┌─────┬─────┬─────────┐ * │ foo ┆ bar ┆ literal │ @@ -72,7 +74,7 @@ function When(_when: any): When { * └─────┴─────┴─────────┘ * * // Or with multiple `when, thens` chained: - * >>> df.with_column( + * > df.with_column( * ... pl.when(pl.col("foo").gt(2)) * ... .then(1) * ... .when(pl.col("bar").gt(2)) @@ -93,7 +95,6 @@ function When(_when: any): When { * └─────┴─────┴─────────┘ * ``` */ -export function when(expr: Expr): When { - +export function when(expr: Expr): When { return When(pli.when(expr._expr)); } diff --git a/polars/series/datetime.ts b/polars/series/datetime.ts index 3234ac3e1..9077743ed 100644 --- a/polars/series/datetime.ts +++ b/polars/series/datetime.ts @@ -1,11 +1,10 @@ -import {Series, _Series} from "./series"; -import {DateFunctions} from "../shared_traits"; +import { Series, _Series } from "."; +import { DateFunctions } from "../shared_traits"; export type SeriesDateFunctions = DateFunctions; export const SeriesDateFunctions = (_s): SeriesDateFunctions => { const wrap = (method, ...args: any[]): Series => { - return _Series(_s[method](...args)) as any; }; diff --git a/polars/series/series.ts b/polars/series/index.ts similarity index 76% rename from polars/series/series.ts rename to polars/series/index.ts index 3af45867a..b5422c5b1 100644 --- a/polars/series/series.ts +++ b/polars/series/index.ts @@ -1,44 +1,55 @@ import pli from "../internals/polars_internal"; -import {arrayToJsSeries} from "../internals/construction"; -import {DataType, DTYPE_TO_FFINAME, Optional} from "../datatypes"; -import {DataFrame, _DataFrame} from "../dataframe"; -import {StringFunctions} from "./string"; -import {SeriesListFunctions} from "./list"; -import {SeriesDateFunctions} from "./datetime"; -import {SeriesStructFunctions} from "./struct"; -import {InvalidOperationError} from "../error"; -import {RankMethod} from "../utils"; -import {Arithmetic, Comparison, Cumulative, Deserialize, Rolling, Round, Sample, Serialize} from "../shared_traits"; -import {col} from "../lazy/functions"; -import {InterpolationMethod} from "../lazy/expr"; +import { arrayToJsSeries } from "../internals/construction"; +import { DataType, DTYPE_TO_FFINAME, Optional } from "../datatypes"; +import { DataFrame, _DataFrame } from "../dataframe"; +import { SeriesStringFunctions, StringNamespace } from "./string"; +import { ListNamespace, SeriesListFunctions } from "./list"; +import { SeriesDateFunctions } from "./datetime"; +import { SeriesStructFunctions } from "./struct"; +import { InvalidOperationError } from "../error"; +import { + Arithmetic, + Comparison, + Cumulative, + Deserialize, + Rolling, + Round, + Sample, + Serialize, +} from "../shared_traits"; +import { col } from "../lazy/functions"; +import { InterpolationMethod, RankMethod } from "../types"; const inspect = Symbol.for("nodejs.util.inspect.custom"); -export interface Series extends - ArrayLike, - Rolling, - Arithmetic, - Comparison, - Cumulative, - Round, - Sample, - Serialize { - inner(): any - name: string - dtype: DataType - str: StringFunctions - lst: SeriesListFunctions, - struct: SeriesStructFunctions, - date: SeriesDateFunctions +/** + * A Series represents a single column in a polars DataFrame. + */ +export interface Series + extends ArrayLike, + Rolling, + Arithmetic, + Comparison, + Cumulative, + Round, + Sample, + Serialize { + inner(): any; + name: string; + dtype: DataType; + str: StringNamespace; + lst: ListNamespace; + struct: SeriesStructFunctions; + date: SeriesDateFunctions; [inspect](): string; [Symbol.iterator](): IterableIterator; // inner(): JsSeries - bitand(other: Series): Series - bitor(other: Series): Series - bitxor(other: Series): Series + bitand(other: Series): Series; + bitor(other: Series): Series; + bitxor(other: Series): Series; /** * Take absolute values */ - abs(): Series + abs(): Series; /** * __Rename this Series.__ * @@ -46,15 +57,15 @@ export interface Series extends * @see {@link rename} * */ - alias(name: string): Series + alias(name: string): Series; /** * __Append a Series to this one.__ * ___ * @param {Series} other - Series to append. * @example - * > const s = pl.Series("a", [1, 2, 3]) - * > const s2 = pl.Series("b", [4, 5, 6]) - * > s.append(s2) + * > const s = pl.Series("a", [1, 2, 3]) + * > const s2 = pl.Series("b", [4, 5, 6]) + * > s.append(s2) * shape: (6,) * Series: 'a' [i64] * [ @@ -66,7 +77,7 @@ export interface Series extends * 6 * ] */ - append(other: Series): void + append(other: Series): void; // TODO! // /** // * __Apply a function over elements in this Series and return a new Series.__ @@ -78,8 +89,8 @@ export interface Series extends // * @returns {SeriesType} `Series | Series` // * @example // * ``` - // * > const s = pl.Series("a", [1, 2, 3]) - // * > s.apply(x => x + 10) + // * > const s = pl.Series("a", [1, 2, 3]) + // * > s.apply(x => x + 10) // * shape: (3,) // * Series: 'a' [i64] // * [ @@ -93,29 +104,29 @@ export interface Series extends /** * Get the index of the maximal value. */ - argMax(): Optional + argMax(): Optional; /** - * Get the index of the minimal value. - */ - argMin(): Optional + * Get the index of the minimal value. + */ + argMin(): Optional; /** * Get index values where Boolean Series evaluate True. * */ - argTrue(): Series + argTrue(): Series; /** * Get unique index as Series. */ - argUnique(): Series + argUnique(): Series; /** * Index location of the sorted variant of this Series. * ___ * @param reverse * @return {SeriesType} indexes - Indexes that can be used to sort this array. */ - argSort(): Series - argSort(reverse: boolean): Series - argSort({reverse}: {reverse: boolean}): Series + argSort(): Series; + argSort(reverse: boolean): Series; + argSort({ reverse }: { reverse: boolean }): Series; /** * __Rename this Series.__ * @@ -123,20 +134,20 @@ export interface Series extends * @see {@link rename} {@link alias} * */ - as(name: string): Series + as(name: string): Series; /** * Cast between data types. */ - cast(dtype: DataType, strict?: boolean): Series + cast(dtype: DataType, strict?: boolean): Series; /** * Get the length of each individual chunk */ - chunkLengths(): Array + chunkLengths(): Array; /** * Cheap deep clones. */ - clone(): Series - concat(other: Series): Series + clone(): Series; + concat(other: Series): Series; /** * __Quick summary statistics of a series. __ @@ -145,8 +156,8 @@ export interface Series extends * ___ * @example * ``` - * > const seriesNum = pl.Series([1,2,3,4,5]) - * > series_num.describe() + * > const seriesNum = pl.Series([1,2,3,4,5]) + * > series_num.describe() * * shape: (6, 2) * ┌──────────────┬────────────────────┐ @@ -167,8 +178,8 @@ export interface Series extends * │ "count" ┆ 5 │ * └──────────────┴────────────────────┘ * - * > series_str = pl.Series(["a", "a", None, "b", "c"]) - * > series_str.describe() + * > series_str = pl.Series(["a", "a", None, "b", "c"]) + * > series_str.describe() * * shape: (3, 2) * ┌──────────────┬───────┐ @@ -184,30 +195,33 @@ export interface Series extends * └──────────────┴───────┘ * ``` */ - describe(): DataFrame + describe(): DataFrame; /** * Calculates the n-th discrete difference. * @param n - number of slots to shift * @param nullBehavior - `'ignore' | 'drop'` */ - diff(n: number, nullBehavior: "ignore" | "drop"): Series - diff({n, nullBehavior}: {n: number, nullBehavior: "ignore" | "drop"}): Series - /** - * Compute the dot/inner product between two Series - * ___ - * @example - * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > const s2 = pl.Series("b", [4.0, 5.0, 6.0]) - * > s.dot(s2) - * 32.0 - * ``` - */ - dot(other: Series): number | undefined | null + diff(n: number, nullBehavior: "ignore" | "drop"): Series; + diff({ + n, + nullBehavior, + }: { n: number; nullBehavior: "ignore" | "drop" }): Series; + /** + * Compute the dot/inner product between two Series + * ___ + * @example + * ``` + * > const s = pl.Series("a", [1, 2, 3]) + * > const s2 = pl.Series("b", [4.0, 5.0, 6.0]) + * > s.dot(s2) + * 32.0 + * ``` + */ + dot(other: Series): number | undefined | null; /** * Create a new Series that copies data from this Series without null values. */ - dropNulls(): Series + dropNulls(): Series; /** * __Explode a list or utf8 Series.__ * @@ -215,8 +229,8 @@ export interface Series extends * ___ * @example * ``` - * > const s = pl.Series('a', [[1, 2], [3, 4], [9, 10]]) - * > s.explode() + * > const s = pl.Series('a', [[1, 2], [3, 4], [9, 10]]) + * > s.explode() * shape: (6,) * Series: 'a' [i64] * [ @@ -229,7 +243,7 @@ export interface Series extends * ] * ``` */ - explode(): any + explode(): any; /** * Extend the Series with given number of values. * @param value The value to extend the Series with. This value may be null to fill with nulls. @@ -237,21 +251,21 @@ export interface Series extends * @deprecated * @see {@link extendConstant} */ - extend(value: any, n: number): Series + extend(value: any, n: number): Series; /** * Extend the Series with given number of values. * @param value The value to extend the Series with. This value may be null to fill with nulls. * @param n The number of values to extend. */ - extendConstant(value: any, n: number): Series + extendConstant(value: any, n: number): Series; /** * __Fill null values with a filling strategy.__ * ___ * @param strategy - Filling Strategy * @example * ``` - * > const s = pl.Series("a", [1, 2, 3, None]) - * > s.fill_null('forward')) + * > const s = pl.Series("a", [1, 2, 3, None]) + * > s.fill_null('forward')) * shape: (4,) * Series: '' [i64] * [ @@ -260,7 +274,7 @@ export interface Series extends * 3 * 3 * ] - * > s.fill_null('min')) + * > s.fill_null('min')) * shape: (4,) * Series: 'a' [i64] * [ @@ -271,22 +285,28 @@ export interface Series extends * ] * ``` */ - fillNull(strategy: "backward" | "forward" | "min" | "max" | "mean" | "one" | "zero"): Series - fillNull({strategy}: {strategy: "backward" | "forward" | "min" | "max" | "mean" | "one" | "zero"}): Series + fillNull( + strategy: "backward" | "forward" | "min" | "max" | "mean" | "one" | "zero", + ): Series; + fillNull({ + strategy, + }: { + strategy: "backward" | "forward" | "min" | "max" | "mean" | "one" | "zero"; + }): Series; /** * __Filter elements by a boolean mask.__ * @param {SeriesType} predicate - Boolean mask * */ - filter(predicate: Series): Series - filter({predicate}: {predicate: Series}): Series - get(index: number): any - getIndex(n: number): any + filter(predicate: Series): Series; + filter({ predicate }: { predicate: Series }): Series; + get(index: number): any; + getIndex(n: number): any; /** * Returns True if the Series has a validity bitmask. * If there is none, it means that there are no null values. */ - hasValidity(): boolean + hasValidity(): boolean; /** * Hash the Series * The hash value is of type `UInt64` @@ -297,8 +317,8 @@ export interface Series extends * @param k3 - seed parameter * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.hash(42) + * > const s = pl.Series("a", [1, 2, 3]) + * > s.hash(42) * shape: (3,) * Series: 'a' [u64] * [ @@ -308,16 +328,31 @@ export interface Series extends * ] * ``` */ - hash(k0?: number | bigint, k1?: number | bigint, k2?: number | bigint, k3?: number | bigint): Series - hash({k0, k1, k2, k3}: {k0?: number | bigint, k1?: number | bigint, k2?: number | bigint, k3?: number | bigint}): Series + hash( + k0?: number | bigint, + k1?: number | bigint, + k2?: number | bigint, + k3?: number | bigint, + ): Series; + hash({ + k0, + k1, + k2, + k3, + }: { + k0?: number | bigint; + k1?: number | bigint; + k2?: number | bigint; + k3?: number | bigint; + }): Series; /** * __Get first N elements as Series.__ * ___ * @param length Length of the head * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.head(2) + * > const s = pl.Series("a", [1, 2, 3]) + * > s.head(2) * shape: (2,) * Series: 'a' [i64] * [ @@ -326,7 +361,7 @@ export interface Series extends * ] * ``` */ - head(length?: number): Series + head(length?: number): Series; /** * __Interpolate intermediate values.__ * @@ -334,8 +369,8 @@ export interface Series extends * ___ * @example * ``` - * > const s = pl.Series("a", [1, 2, None, None, 5]) - * > s.interpolate() + * > const s = pl.Series("a", [1, 2, None, None, 5]) + * > s.interpolate() * shape: (5,) * Series: 'a' [i64] * [ @@ -347,22 +382,22 @@ export interface Series extends * ] * ``` */ - interpolate(method?: InterpolationMethod): Series + interpolate(method?: InterpolationMethod): Series; /** * Check if this Series is a Boolean. */ - isBoolean(): boolean + isBoolean(): boolean; /** * Check if this Series is a DataTime. */ - isDateTime(): boolean + isDateTime(): boolean; /** * __Get mask of all duplicated values.__ * * @example * ``` - * > const s = pl.Series("a", [1, 2, 2, 3]) - * > s.isDuplicated() + * > const s = pl.Series("a", [1, 2, 2, 3]) + * > s.isDuplicated() * * shape: (4,) * Series: 'a' [bool] @@ -374,29 +409,29 @@ export interface Series extends * ] * ``` */ - isDuplicated(): Series + isDuplicated(): Series; /** * Get mask of finite values if Series dtype is Float. */ - isFinite(): Series + isFinite(): Series; /** * Get a mask of the first unique value. */ - isFirst(): Series + isFirst(): Series; /** * Check if this Series is a Float. */ - isFloat(): boolean + isFloat(): boolean; /** * Check if elements of this Series are in the right Series, or List values of the right Series. */ - isIn(other: Series | U[]): Series + isIn(other: Series | U[]): Series; /** * __Get mask of infinite values if Series dtype is Float.__ * @example * ``` - * > const s = pl.Series("a", [1.0, 2.0, 3.0]) - * > s.isInfinite() + * > const s = pl.Series("a", [1.0, 2.0, 3.0]) + * > s.isInfinite() * * shape: (3,) * Series: 'a' [bool] @@ -407,7 +442,7 @@ export interface Series extends * ] * ``` */ - isInfinite(): Series + isInfinite(): Series; /** * __Get mask of non null values.__ * @@ -415,8 +450,8 @@ export interface Series extends * ___ * @example * ``` - * > const s = pl.Series("a", [1.0, undefined, 2.0, 3.0, null]) - * > s.isNotNull() + * > const s = pl.Series("a", [1.0, undefined, 2.0, 3.0, null]) + * > s.isNotNull() * shape: (5,) * Series: 'a' [bool] * [ @@ -428,7 +463,7 @@ export interface Series extends * ] * ``` */ - isNotNull(): Series + isNotNull(): Series; /** * __Get mask of null values.__ * @@ -436,8 +471,8 @@ export interface Series extends * ___ * @example * ``` - * > const s = pl.Series("a", [1.0, undefined, 2.0, 3.0, null]) - * > s.isNull() + * > const s = pl.Series("a", [1.0, undefined, 2.0, 3.0, null]) + * > s.isNull() * shape: (5,) * Series: 'a' [bool] * [ @@ -449,18 +484,18 @@ export interface Series extends * ] * ``` */ - isNull(): Series + isNull(): Series; /** * Check if this Series datatype is numeric. */ - isNumeric(): boolean + isNumeric(): boolean; /** * __Get mask of unique values.__ * ___ * @example * ``` - * > const s = pl.Series("a", [1, 2, 2, 3]) - * > s.isUnique() + * > const s = pl.Series("a", [1, 2, 2, 3]) + * > s.isUnique() * shape: (4,) * Series: 'a' [bool] * [ @@ -471,11 +506,11 @@ export interface Series extends * ] * ``` */ - isUnique(): Series + isUnique(): Series; /** * Checks if this Series datatype is a Utf8. */ - isUtf8(): boolean + isUtf8(): boolean; /** * __Compute the kurtosis (Fisher or Pearson) of a dataset.__ * @@ -489,20 +524,23 @@ export interface Series extends * - If True, Fisher's definition is used (normal ==> 0.0). * - If False, Pearson's definition is used (normal ==> 3.0) */ - kurtosis(): Optional - kurtosis(fisher: boolean, bias?: boolean): Optional - kurtosis({fisher, bias}: {fisher?: boolean, bias?: boolean}): Optional + kurtosis(): Optional; + kurtosis(fisher: boolean, bias?: boolean): Optional; + kurtosis({ + fisher, + bias, + }: { fisher?: boolean; bias?: boolean }): Optional; /** * __Length of this Series.__ * ___ * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.len() + * > const s = pl.Series("a", [1, 2, 3]) + * > s.len() * 3 * ``` */ - len(): number + len(): number; /** * __Take `n` elements from this Series.__ * ___ @@ -520,47 +558,47 @@ export interface Series extends * ] * ``` */ - limit(n?: number): Series + limit(n?: number): Series; /** * Get the maximum value in this Series. * @example * ``` - * >>> s = pl.Series("a", [1, 2, 3]) - * >>> s.max() + * > s = pl.Series("a", [1, 2, 3]) + * > s.max() * 3 * ``` */ - max(): number + max(): number; /** * Reduce this Series to the mean value. * @example * ``` - * >>> s = pl.Series("a", [1, 2, 3]) - * >>> s.mean() + * > s = pl.Series("a", [1, 2, 3]) + * > s.mean() * 2 * ``` */ - mean(): number + mean(): number; /** * Get the median of this Series * @example * ``` - * >>> s = pl.Series("a", [1, 2, 3]) - * >>> s.median() + * > s = pl.Series("a", [1, 2, 3]) + * > s.median() * 2 * ``` */ - median(): number + median(): number; /** * Get the minimal value in this Series. * @example * ``` - * >>> s = pl.Series("a", [1, 2, 3]) - * >>> s.min() + * > s = pl.Series("a", [1, 2, 3]) + * > s.min() * 1 * ``` */ - min(): number + min(): number; /** * __Compute the most occurring value(s). Can return multiple Values__ * ___ @@ -584,11 +622,11 @@ export interface Series extends * ] * ``` */ - mode(): Series + mode(): Series; /** * Get the number of chunks that this Series contains. */ - nChunks(): number + nChunks(): number; /** * __Count the number of unique values in this Series.__ * ___ @@ -599,13 +637,13 @@ export interface Series extends * 3 * ``` */ - nUnique(): number + nUnique(): number; /** * Count the null values in this Series. -- * _`undefined` values are treated as null_ * */ - nullCount(): number + nullCount(): number; /** * Get a boolean mask of the local maximum peaks. * ___ @@ -624,7 +662,7 @@ export interface Series extends * ] * ``` */ - peakMax(): Series + peakMax(): Series; /** * Get a boolean mask of the local minimum peaks. * ___ @@ -643,7 +681,7 @@ export interface Series extends * ] * ``` */ - peakMin(): Series + peakMin(): Series; /** * Get the quantile value of this Series. * ___ @@ -655,7 +693,7 @@ export interface Series extends * 2 * ``` */ - quantile(quantile: number, interpolation?: string): number + quantile(quantile: number, interpolation?: string): number; /** * Assign ranks to data, dealing with ties appropriately. * @param method @@ -677,10 +715,10 @@ export interface Series extends * * __'random'__: Like 'ordinal', but the rank for ties is not dependent * on the order that the values occur in `a`. */ - rank(method?: RankMethod): Series - rechunk(): Series - rechunk(inPlace: true): Series - rechunk(inPlace: false): void + rank(method?: RankMethod): Series; + rechunk(): Series; + rechunk(inPlace: true): Series; + rechunk(inPlace: false): void; /** * __Reinterpret the underlying bits as a signed/unsigned integer.__ * @@ -694,7 +732,7 @@ export interface Series extends * @see {@link cast} * */ - reinterpret(signed?: boolean): Series + reinterpret(signed?: boolean): Series; /** * __Rename this Series.__ * @@ -715,9 +753,9 @@ export interface Series extends * ``` */ rename(name: string): Series; - rename(name: string, inPlace: boolean): void - rename({name, inPlace}: {name: string, inPlace?: boolean}): void - rename({name, inPlace}: {name: string, inPlace: true}): void + rename(name: string, inPlace: boolean): void; + rename({ name, inPlace }: { name: string; inPlace?: boolean }): void; + rename({ name, inPlace }: { name: string; inPlace: true }): void; /** * __Check if series is equal with another Series.__ @@ -734,14 +772,14 @@ export interface Series extends * false * ``` */ - seriesEqual(other: Series, nullEqual?: boolean, strict?: boolean): boolean + seriesEqual(other: Series, nullEqual?: boolean, strict?: boolean): boolean; /** * __Set masked values__ * @param filter Boolean mask * @param value value to replace masked values with */ - set(filter: Series, value: any): Series - setAtIdx(indices: number[] | Series, value: any): void + set(filter: Series, value: any): Series; + setAtIdx(indices: number[] | Series, value: any): void; /** * __Shift the values by a given period__ * @@ -769,7 +807,7 @@ export interface Series extends * ] * ``` */ - shift(periods: number): Series + shift(periods: number): Series; /** * Shift the values by a given period * @@ -778,15 +816,15 @@ export interface Series extends * @param periods - Number of places to shift (may be negative). * @param fillValue - Fill null & undefined values with the result of this expression. */ - shiftAndFill(periods: number, fillValue: any): Series - shiftAndFill(args: {periods: number, fillValue: any}): Series + shiftAndFill(periods: number, fillValue: any): Series; + shiftAndFill(args: { periods: number; fillValue: any }): Series; /** * __Shrink memory usage of this Series to fit the exact capacity needed to hold the data.__ * @param inPlace - Modify the Series in-place. */ - shrinkToFit(): Series - shrinkToFit(inPlace: true): void + shrinkToFit(): Series; + shrinkToFit(inPlace: true): void; /** * __Compute the sample skewness of a data set.__ * @@ -798,14 +836,14 @@ export interface Series extends * ___ * @param bias - If false, then the calculations are corrected for statistical bias. */ - skew(bias?: boolean): number | undefined + skew(bias?: boolean): number | undefined; /** * Create subslices of the Series. * * @param offset - Start of the slice (negative indexing may be used). * @param length - length of the slice. */ - slice(start: number, length?: number): Series + slice(start: number, length?: number): Series; /** * __Sort this Series.__ * @param reverse - Reverse sort @@ -832,19 +870,19 @@ export interface Series extends * ] * ``` */ - sort(): Series - sort(reverse?: boolean): Series - sort(options: {reverse: boolean}): Series + sort(): Series; + sort(reverse?: boolean): Series; + sort(options: { reverse: boolean }): Series; /** * Reduce this Series to the sum value. * @example * ``` - * >>> s = pl.Series("a", [1, 2, 3]) - * >>> s.sum() + * > s = pl.Series("a", [1, 2, 3]) + * > s.sum() * 6 * ``` */ - sum(): number + sum(): number; /** * __Get last N elements as Series.__ * @@ -863,7 +901,7 @@ export interface Series extends * ] * ``` */ - tail(length?: number): Series + tail(length?: number): Series; /** * Take every nth value in the Series and return as new Series. * @param n - nth value to take @@ -879,7 +917,7 @@ export interface Series extends * ] * ``` */ - takeEvery(n: number): Series + takeEvery(n: number): Series; /** * Take values by index. * ___ @@ -896,7 +934,7 @@ export interface Series extends * ] * ``` */ - take(indices: Array): Series + take(indices: Array): Series; /** * __Get unique elements in series.__ @@ -915,29 +953,29 @@ export interface Series extends * ] * ``` */ - unique(maintainOrder?: boolean | {maintainOrder: boolean}): Series - /** - * __Count the unique values in a Series.__ - * ___ - * @example - * ``` - * s = pl.Series("a", [1, 2, 2, 3]) - * s.valueCounts() - * shape: (3, 2) - * ╭─────┬────────╮ - * │ a ┆ counts │ - * │ --- ┆ --- │ - * │ i64 ┆ u32 │ - * ╞═════╪════════╡ - * │ 2 ┆ 2 │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 1 ┆ 1 │ - * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ - * │ 3 ┆ 1 │ - * ╰─────┴────────╯ - * ``` - */ - valueCounts(): DataFrame + unique(maintainOrder?: boolean | { maintainOrder: boolean }): Series; + /** + * __Count the unique values in a Series.__ + * ___ + * @example + * ``` + * s = pl.Series("a", [1, 2, 2, 3]) + * s.valueCounts() + * shape: (3, 2) + * ╭─────┬────────╮ + * │ a ┆ counts │ + * │ --- ┆ --- │ + * │ i64 ┆ u32 │ + * ╞═════╪════════╡ + * │ 2 ┆ 2 │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 1 ┆ 1 │ + * ├╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ 1 │ + * ╰─────┴────────╯ + * ``` + */ + valueCounts(): DataFrame; /** * Where mask evaluates true, take values from self. * @@ -947,7 +985,7 @@ export interface Series extends * @param other - Series of same type * */ - zipWith(mask: Series, other: Series): Series + zipWith(mask: Series, other: Series): Series; /** * __Convert this Series to a Javascript Array.__ @@ -964,14 +1002,14 @@ export interface Series extends * true * ``` */ - toArray(): Array + toArray(): Array; /** * Converts series to a javascript typedArray. * * __Warning:__ * This will throw an error if you have nulls, or are using non numeric data types */ - toTypedArray(): any + toTypedArray(): any; /** * _Returns a Javascript object representation of Series_ @@ -988,20 +1026,14 @@ export interface Series extends * } * ``` */ - toObject(): {name: string, datatype: string, values: any[]} - toFrame(): DataFrame + toObject(): { name: string; datatype: string; values: any[] }; + toFrame(): DataFrame; /** compat with `JSON.stringify */ - toJSON(): string + toJSON(): string; /** Returns an iterator over the values */ - values(): IterableIterator + values(): IterableIterator; } -export interface RollingOptions { - windowSize: number - weights?: Array - minPeriods: number - center: boolean -} export function _Series(_s: any): Series { const unwrap = (method: keyof any, ...args: any[]) => { return _s[method as any](...args); @@ -1067,7 +1099,7 @@ export function _Series(_s: any): Series { return _s.len(); }, get str() { - return StringFunctions(_s); + return SeriesStringFunctions(_s); }, get lst() { return SeriesListFunctions(_s); @@ -1104,19 +1136,17 @@ export function _Series(_s: any): Series { return _Series(_s.argsort(reverse, nullsLast)); } - return _Series(_s.argsort( - reverse.reverse, - reverse.nullsLast ?? nullsLast - )); + return _Series( + _s.argsort(reverse.reverse, reverse.nullsLast ?? nullsLast), + ); }, argTrue() { - return _Series(this - .toFrame() - ._df - .lazy() - .select([pli.argWhere(pli.col(this.name))]) - .collectSync() - .column(this.name) + return _Series( + this.toFrame() + ._df.lazy() + .select([pli.argWhere(pli.col(this.name))]) + .collectSync() + .column(this.name), ); }, argUnique() { @@ -1173,37 +1203,37 @@ export function _Series(_s: any): Series { if (this.isNumeric()) { s = s.cast(DataType.Float64); stats = { - "min": s.min(), - "max": s.max(), - "null_count": s.nullCount(), - "mean": s.mean(), - "count": s.len(), + min: s.min(), + max: s.max(), + null_count: s.nullCount(), + mean: s.mean(), + count: s.len(), }; } else if (s.isBoolean()) { stats = { - "sum": s.sum(), - "null_count": s.nullCount(), - "count": s.len(), + sum: s.sum(), + null_count: s.nullCount(), + count: s.len(), }; } else if (s.isUtf8()) { stats = { - "unique": s.nUnique(), - "null_count": s.nullCount(), - "count": s.len(), + unique: s.nUnique(), + null_count: s.nullCount(), + count: s.len(), }; } else { throw new InvalidOperationError("describe", s.dtype); } return DataFrame({ - "statistic": Object.keys(stats), - "value": Object.values(stats) + statistic: Object.keys(stats), + value: Object.values(stats), }); }, diff(n: any = 1, nullBehavior = "ignore") { - return typeof n === "number" ? - _Series(_s.diff(n, nullBehavior)) : - _Series(_s.diff(n?.n ?? 1, n.nullBehavior ?? nullBehavior)); + return typeof n === "number" + ? _Series(_s.diff(n, nullBehavior)) + : _Series(_s.diff(n?.n ?? 1, n.nullBehavior ?? nullBehavior)); }, div(field: Series) { return dtypeWrap("Div", field); @@ -1233,14 +1263,14 @@ export function _Series(_s: any): Series { return wrap("extendConstant", value, n); }, fillNull(strategy) { - return typeof strategy === "string" ? - wrap("fillNull", strategy) : - wrap("fillNull", strategy.strategy); + return typeof strategy === "string" + ? wrap("fillNull", strategy) + : wrap("fillNull", strategy.strategy); }, filter(predicate) { - return Series.isSeries(predicate) ? - wrap("filter", (predicate as any)._s) : - wrap("filter", (SeriesConstructor("", predicate) as any)._s); + return Series.isSeries(predicate) + ? wrap("filter", (predicate as any)._s) + : wrap("filter", (SeriesConstructor("", predicate) as any)._s); }, get(field) { return dtypeUnwrap("Get", field); @@ -1264,14 +1294,14 @@ export function _Series(_s: any): Series { if (typeof obj === "number" || typeof obj === "bigint") { return wrap("hash", BigInt(obj), BigInt(k1), BigInt(k2), BigInt(k3)); } - const o = {k0: obj, k1: k1, k2: k2, k3: k3, ...obj}; + const o = { k0: obj, k1: k1, k2: k2, k3: k3, ...obj }; return wrap( "hash", BigInt(o.k0), BigInt(o.k1), BigInt(o.k2), - BigInt(o.k3) + BigInt(o.k3), ); }, hasValidity() { @@ -1302,7 +1332,11 @@ export function _Series(_s: any): Series { isFinite() { const dtype = this.dtype; - if (![DataType.Float32.variant, DataType.Float64.variant].includes(dtype.variant)) { + if ( + ![DataType.Float32.variant, DataType.Float64.variant].includes( + dtype.variant, + ) + ) { throw new InvalidOperationError("isFinite", dtype); } else { return wrap("isFinite"); @@ -1314,17 +1348,23 @@ export function _Series(_s: any): Series { isFloat() { const dtype = this.dtype; - return [DataType.Float32.variant, DataType.Float64.variant].includes(dtype.variant); + return [DataType.Float32.variant, DataType.Float64.variant].includes( + dtype.variant, + ); }, isIn(other) { - return Series.isSeries(other) ? - wrap("isIn", (other as any)._s) : - wrap("isIn", (Series("", other) as any)._s); + return Series.isSeries(other) + ? wrap("isIn", (other as any)._s) + : wrap("isIn", (Series("", other) as any)._s); }, isInfinite() { const dtype = this.dtype; - if (![DataType.Float32.variant, DataType.Float64.variant].includes(dtype.variant)) { + if ( + ![DataType.Float32.variant, DataType.Float64.variant].includes( + dtype.variant, + ) + ) { throw new InvalidOperationError("isFinite", dtype); } else { return wrap("isInfinite"); @@ -1355,7 +1395,7 @@ export function _Series(_s: any): Series { DataType.UInt32.variant, DataType.UInt64.variant, DataType.Float32.variant, - DataType.Float64.variant + DataType.Float64.variant, ]; return numericTypes.includes(dtype.variant); @@ -1373,7 +1413,7 @@ export function _Series(_s: any): Series { const d = { fisher: true, bias, - ...fisher + ...fisher, }; return _s.kurtosis(d.fisher, d.bias); @@ -1452,7 +1492,11 @@ export function _Series(_s: any): Series { }, reinterpret(signed = true) { const dtype = this.dtype; - if ([DataType.UInt64.variant, DataType.Int64.variant].includes(dtype.variant)) { + if ( + [DataType.UInt64.variant, DataType.Int64.variant].includes( + dtype.variant, + ) + ) { return wrap("reinterpret", signed); } else { throw new InvalidOperationError("reinterpret", dtype); @@ -1472,7 +1516,6 @@ export function _Series(_s: any): Series { } }, - rollingMax(...args) { return expr_op("rollingMax", ...args); }, @@ -1513,7 +1556,6 @@ export function _Series(_s: any): Series { } else { return wrap("round", opt.decimals); } - } else { throw new InvalidOperationError("round", this.dtype); } @@ -1522,7 +1564,9 @@ export function _Series(_s: any): Series { return expr_op("clip", ...args); }, setAtIdx(indices, value) { - indices = Series.isSeries(indices) ? indices.cast(DataType.UInt32) : Series(indices); + indices = Series.isSeries(indices) + ? indices.cast(DataType.UInt32) + : Series(indices); if (!Series.isSeries(value)) { if (!Array.isArray(value)) { value = [value]; @@ -1530,9 +1574,8 @@ export function _Series(_s: any): Series { value = Series(value); } - if(indices.length > 0) { + if (indices.length > 0) { value = value.extendConstant(value[0], indices.length - 1); - } _s.setAtIdx(indices._s, value._s); }, @@ -1543,37 +1586,20 @@ export function _Series(_s: any): Series { }, sample(opts?, frac?, withReplacement = false, seed?) { // rome-ignore lint/style/noArguments: - if (arguments.length === 0) { - return wrap("sampleN", - 1, - withReplacement, - false, - seed - ); + if (arguments.length === 0) { + return wrap("sampleN", 1, withReplacement, false, seed); } if (opts?.n !== undefined || opts?.frac !== undefined) { return this.sample(opts.n, opts.frac, opts.withReplacement, seed); } if (typeof opts === "number") { - return wrap("sampleN", - opts, - withReplacement, - false, - seed - ); + return wrap("sampleN", opts, withReplacement, false, seed); } if (typeof frac === "number") { - return wrap("sampleFrac", - frac, - withReplacement, - false, - seed - ); - } - else { + return wrap("sampleFrac", frac, withReplacement, false, seed); + } else { throw new TypeError("must specify either 'frac' or 'n'"); } - }, seriesEqual(other, nullEqual: any = true, strict = false) { return _s.seriesEqual(other._s, nullEqual, strict); @@ -1593,7 +1619,6 @@ export function _Series(_s: any): Series { return s as any; } - }, skew(bias: any = true) { if (typeof bias === "boolean") { @@ -1615,7 +1640,6 @@ export function _Series(_s: any): Series { } return wrap("sort", reverse?.reverse ?? false); - }, sub(field) { return dtypeWrap("Sub", field); @@ -1677,7 +1701,7 @@ export function _Series(_s: any): Series { }, zipWith(mask, other) { return wrap("zipWith", mask._s, other._s); - } + }, }; return new Proxy(series, { @@ -1694,34 +1718,73 @@ export function _Series(_s: any): Series { return true; } - } + }, }); } +/** + * @ignore + * @inheritDoc {Series} + */ export interface SeriesConstructor extends Deserialize { - (values: any): Series - (name: string, values: any[], dtype?): Series + /** + * Creates a new Series from a set of values. + * @param values — A set of values to include in the new Series object. + * @example + * ``` + * > pl.Series([1, 2, 3]) + * shape: (3,) + * Series: '' [f64] + * [ + * 1 + * 2 + * 3 + * ] + * ``` + */ + (values: any): Series; + /** + * Create a new named series + * @param name - The name of the series + * @param values - A set of values to include in the new Series object. + * @example + * ``` + * > pl.Series('foo', [1, 2, 3]) + * shape: (3,) + * Series: 'foo' [f64] + * [ + * 1 + * 2 + * 3 + * ] + * ``` + */ + (name: string, values: any[], dtype?): Series; /** * Creates an array from an array-like object. * @param arrayLike — An array-like object to convert to an array. */ - from(arrayLike: ArrayLike): Series - from(name: string, arrayLike: ArrayLike): Series + from(arrayLike: ArrayLike): Series; + from(name: string, arrayLike: ArrayLike): Series; /** - * Returns a new Series from a set of elements. - * @param items — A set of elements to include in the new Series object. - */ - of(...items: T[]): Series + * Returns a new Series from a set of elements. + * @param items — A set of elements to include in the new Series object. + */ + of(...items: T[]): Series; isSeries(arg: any): arg is Series; /** * @param binary used to serialize/deserialize series. This will only work with the output from series.toBinary(). - */ + */ // fromBinary(binary: Buffer): Series } - -let SeriesConstructor = function (arg0: any, arg1?: any, dtype?: any, strict?: any): Series { +let SeriesConstructor = function ( + arg0: any, + arg1?: any, + dtype?: any, + strict?: any, +): Series { if (typeof arg0 === "string") { const _s = arrayToJsSeries(arg0, arg1, dtype, strict); @@ -1741,7 +1804,6 @@ const isSeries = (anyVal: any): anyVal is Series => { const from = (name, values?: ArrayLike): Series => { if (Array.isArray(name)) { return SeriesConstructor("", values); - } else { return SeriesConstructor(name, values); } @@ -1750,10 +1812,9 @@ const of = (...values: any[]): Series => { return Series.from(values); }; - export const Series: SeriesConstructor = Object.assign(SeriesConstructor, { isSeries, from, of, - deserialize: (buf, fmt) => _Series(pli.JsSeries.deserialize(buf, fmt)) + deserialize: (buf, fmt) => _Series(pli.JsSeries.deserialize(buf, fmt)), }); diff --git a/polars/series/list.ts b/polars/series/list.ts index c4ccc36bf..160d05889 100644 --- a/polars/series/list.ts +++ b/polars/series/list.ts @@ -1,8 +1,9 @@ -import { Series, _Series } from "./series"; +import { Series, _Series } from "."; import { col } from "../lazy/functions"; import { ListFunctions } from "../shared_traits"; -export type SeriesListFunctions = ListFunctions; +export interface ListNamespace extends ListFunctions {} + export const SeriesListFunctions = (_s): ListFunctions => { const wrap = (method, ...args) => { const s = _Series(_s); @@ -12,7 +13,7 @@ export const SeriesListFunctions = (_s): ListFunctions => { .select( col(s.name) .lst[method](...args) - .as(s.name) + .as(s.name), ) .getColumn(s.name); }; diff --git a/polars/series/string.ts b/polars/series/string.ts index 47e37518c..950d3c91f 100644 --- a/polars/series/string.ts +++ b/polars/series/string.ts @@ -1,30 +1,28 @@ -import pli from "../internals/polars_internal"; -import {DataType} from "../datatypes"; -import {_Series, Series} from "./series"; -import {regexToString} from "../utils"; -import {todo} from "../error"; -import {col} from "../lazy/functions"; - +import { DataType } from "../datatypes"; +import { _Series, Series } from "."; +import { regexToString } from "../utils"; +import { col } from "../lazy/functions"; +import { StringFunctions } from "../shared_traits"; /** * namespace containing series string functions */ -export interface StringFunctions { +export interface StringNamespace extends StringFunctions { /** * Vertically concat the values in the Series to a single string value. * @example * ``` - * >>> pl.Series([1, null, 2]).str.concat("-")[0] + * > pl.Series([1, null, 2]).str.concat("-")[0] * '1-null-2' * ``` */ - concat(delimiter: string): Series + concat(delimiter: string): Series; /** * Check if strings in Series contain regex pattern. * @param pattern A valid regex pattern * @returns Boolean mask */ - contains(pattern: string | RegExp): Series + contains(pattern: string | RegExp): Series; /** * Decodes a value using the provided encoding * @param encoding - hex | base64 @@ -45,8 +43,8 @@ export interface StringFunctions { * ] * ``` */ - decode(encoding: "hex" | "base64", strict?: boolean): Series - decode(options: {encoding: "hex" | "base64", strict?: boolean}): Series + decode(encoding: "hex" | "base64", strict?: boolean): Series; + decode(options: { encoding: "hex" | "base64"; strict?: boolean }): Series; /** * Encodes a value using the provided encoding * @param encoding - hex | base64 @@ -63,7 +61,7 @@ export interface StringFunctions { * ] * ``` */ - encode(encoding: "hex" | "base64"): Series + encode(encoding: "hex" | "base64"): Series; /** * Extract the target capture group from provided patterns. * @param pattern A valid regex pattern @@ -73,13 +71,13 @@ export interface StringFunctions { * @returns Utf8 array. Contain null if original value is null or regex capture nothing. * @example * ``` - * > df = pl.DataFrame({ + * > df = pl.DataFrame({ * ... 'a': [ * ... 'http://vote.com/ballon_dor?candidate=messi&ref=polars', * ... 'http://vote.com/ballon_dor?candidat=jorginho&ref=polars', * ... 'http://vote.com/ballon_dor?candidate=ronaldo&ref=polars' * ... ]}) - * > df.getColumn("a").str.extract(/candidate=(\w+)/, 1) + * > df.getColumn("a").str.extract(/candidate=(\w+)/, 1) * shape: (3, 1) * ┌─────────┐ * │ a │ @@ -94,7 +92,7 @@ export interface StringFunctions { * └─────────┘ * ``` */ - extract(pattern: string | RegExp, groupIndex: number): Series + extract(pattern: string | RegExp, groupIndex: number): Series; /** * Extract the first match of json string with provided JSONPath expression. * Throw errors if encounter invalid json strings. @@ -104,14 +102,14 @@ export interface StringFunctions { * @returns Utf8 array. Contain null if original value is null or the `jsonPath` return nothing. * @example * ``` - * >>> s = pl.Series('json_val', [ + * > s = pl.Series('json_val', [ * ... '{"a":"1"}', * ... null, * ... '{"a":2}', * ... '{"a":2.1}', * ... '{"a":true}' * ... ]) - * >>> s.str.jsonPathMatch('$.a') + * > s.str.jsonPathMatch('$.a') * shape: (5,) * Series: 'json_val' [str] * [ @@ -123,12 +121,12 @@ export interface StringFunctions { * ] * ``` */ - jsonPathMatch(jsonPath: string): Series + jsonPathMatch(jsonPath: string): Series; /** Get length of the string values in the Series. */ - lengths(): Series + lengths(): Series; /** Remove leading whitespace. */ - lstrip(): Series - /** + lstrip(): Series; + /** * Add a leading fillChar to a string until string length is reached. * If string is longer or equal to given length no modifications will be done * @param {number} length - of the final string @@ -159,8 +157,8 @@ export interface StringFunctions { * │ cow │ * └──────────┘ * ``` - */ - padStart(length: number, fillChar: string): Series + */ + padStart(length: number, fillChar: string): Series; /** * Add a leading '0' to a string until string length is reached. * If string is longer or equal to given length no modifications will be done @@ -190,54 +188,54 @@ export interface StringFunctions { * │ cow │ * └──────────┘ * ``` - */ - zFill(length: number): Series + */ + zFill(length: number): Series; /** Add trailing zeros */ - padEnd(length: number, fillChar: string): Series + padEnd(length: number, fillChar: string): Series; /** * Replace first regex match with a string value. * @param pattern A valid regex pattern * @param value Substring to replace. */ - replace(pattern: string | RegExp, value: string): Series + replace(pattern: string | RegExp, value: string): Series; /** * Replace all regex matches with a string value. * @param pattern - A valid regex pattern * @param value Substring to replace. */ - replaceAll(pattern: string | RegExp, value: string): Series + replaceAll(pattern: string | RegExp, value: string): Series; /** Modify the strings to their lowercase equivalent. */ - toLowerCase(): Series + toLowerCase(): Series; /** Modify the strings to their uppercase equivalent. */ - toUpperCase(): Series + toUpperCase(): Series; /** Remove trailing whitespace. */ - rstrip(): Series + rstrip(): Series; /** Remove leading and trailing whitespace. */ - strip(): Series + strip(): Series; /** * Create subslices of the string values of a Utf8 Series. * @param start - Start of the slice (negative indexing may be used). * @param length - Optional length of the slice. */ - slice(start: number, length?: number): Series + slice(start: number, length?: number): Series; /** * Split a string into substrings using the specified separator. * The return type will by of type List * @param separator — A string that identifies character or characters to use in separating the string. * @param inclusive Include the split character/string in the results */ - split(separator: string, options?: {inclusive?: boolean} | boolean): Series + split(separator: string, options?: { inclusive?: boolean } | boolean): Series; /** * Parse a Series of dtype Utf8 to a Date/Datetime Series. * @param datatype Date or Datetime. * @param fmt formatting syntax. [Read more](https://docs.rs/chrono/0.4.19/chrono/format/strptime/index.html) */ - strptime(datatype: DataType.Date, fmt?: string): Series - strptime(datatype: DataType.Datetime, fmt?: string): Series + strptime(datatype: DataType.Date, fmt?: string): Series; + strptime(datatype: DataType.Datetime, fmt?: string): Series; } -export const StringFunctions = (_s: any): StringFunctions => { +export const SeriesStringFunctions = (_s: any): StringNamespace => { const wrap = (method, ...args): any => { const ret = _s[method](...args); @@ -246,34 +244,27 @@ export const StringFunctions = (_s: any): StringFunctions => { const handleDecode = (encoding, strict) => { switch (encoding) { - case "hex": - return wrap("strHexDecode", strict); - case "base64": - - return wrap("strBase64Decode", strict); - default: - throw new RangeError("supported encodings are 'hex' and 'base64'"); + case "hex": + return wrap("strHexDecode", strict); + case "base64": + return wrap("strBase64Decode", strict); + default: + throw new RangeError("supported encodings are 'hex' and 'base64'"); } }; return { concat(delimiter: string) { - return _Series(_s) .toFrame() - .select( - col(_s.name) - .str - .concat(delimiter) - .as(_s.name) - ) + .select(col(_s.name).str.concat(delimiter).as(_s.name)) .getColumn(_s.name); }, contains(pat: string | RegExp) { return wrap("strContains", regexToString(pat)); }, - decode(arg, strict=false) { - if(typeof arg === "string") { + decode(arg, strict = false) { + if (typeof arg === "string") { return handleDecode(arg, strict); } @@ -281,19 +272,18 @@ export const StringFunctions = (_s: any): StringFunctions => { }, encode(encoding) { switch (encoding) { - case "hex": - return wrap(`strHexEncode`); - case "base64": - return wrap(`strBase64Encode`); - default: - throw new RangeError("supported encodings are 'hex' and 'base64'"); + case "hex": + return wrap("strHexEncode"); + case "base64": + return wrap("strBase64Encode"); + default: + throw new RangeError("supported encodings are 'hex' and 'base64'"); } }, extract(pat: string | RegExp, groupIndex: number) { return wrap("strExtract", regexToString(pat), groupIndex); }, jsonPathMatch(pat: string) { - return wrap("strJsonPathMatch", pat); }, lengths() { @@ -324,47 +314,30 @@ export const StringFunctions = (_s: any): StringFunctions => { return wrap("strSlice", start, length); }, split(by: string, options?) { - const inclusive = typeof options === "boolean" ? options : options?.inclusive; + const inclusive = + typeof options === "boolean" ? options : options?.inclusive; const s = _Series(_s); return s .toFrame() - .select( - col(s.name) - .str - .split(by, inclusive) - .as(s.name) - ) + .select(col(s.name).str.split(by, inclusive).as(s.name)) .getColumn(s.name); - }, strip() { const s = _Series(_s); return s .toFrame() - .select( - col(s.name) - .str - .strip() - .as(s.name) - ) + .select(col(s.name).str.strip().as(s.name)) .getColumn(s.name); - }, strptime(dtype, fmt?) { const s = _Series(_s); return s .toFrame() - .select( - col(s.name) - .str - .strptime(dtype, fmt) - .as(s.name) - ) + .select(col(s.name).str.strptime(dtype, fmt).as(s.name)) .getColumn(s.name); - }, toLowerCase() { return wrap("strToLowercase"); diff --git a/polars/series/struct.ts b/polars/series/struct.ts index 52d06f751..64a8c2858 100644 --- a/polars/series/struct.ts +++ b/polars/series/struct.ts @@ -1,16 +1,15 @@ import pli from "../internals/polars_internal"; -import {_DataFrame, DataFrame} from "../dataframe"; -import {_Series, Series} from "./series"; -import {_Expr} from "../lazy/expr"; +import { _DataFrame, DataFrame } from "../dataframe"; +import { _Series, Series } from "."; +import { _Expr } from "../lazy/expr"; export interface SeriesStructFunctions { - fields: string[] + fields: string[]; toFrame(): DataFrame; field(name: string): Series; - renameFields(names: string[]): Series + renameFields(names: string[]): Series; } - export const SeriesStructFunctions = (_s: any): SeriesStructFunctions => { return { get fields() { @@ -20,12 +19,14 @@ export const SeriesStructFunctions = (_s: any): SeriesStructFunctions => { return _DataFrame(_s.structToFrame()); }, field(name) { - return DataFrame({}).select(_Expr(pli.lit(_s).structFieldByName(name))) + return DataFrame({}) + .select(_Expr(pli.lit(_s).structFieldByName(name))) .toSeries(); }, renameFields(names) { - return DataFrame({}).select(_Expr(pli.lit(_s).structRenameFields(names))) + return DataFrame({}) + .select(_Expr(pli.lit(_s).structRenameFields(names))) .toSeries(); - } + }, }; }; diff --git a/polars/shared_traits.ts b/polars/shared_traits.ts index a37f920a7..f23aefe5c 100644 --- a/polars/shared_traits.ts +++ b/polars/shared_traits.ts @@ -1,54 +1,141 @@ import { ColumnsOrExpr } from "./utils"; import { Expr, _Expr } from "./lazy/expr"; -import { Series } from "./series/series"; -import { concatList } from "./lazy/functions"; -import pli from "./internals/polars_internal"; - -export type RollingOptions = { - windowSize: number; - weights?: Array; - minPeriods?: number; - center?: boolean; -}; - -export type Interpolation = - | "nearest" - | "higher" - | "lower" - | "midpoint" - | "linear"; +import { + InterpolationMethod, + RollingOptions, + RollingQuantileOptions, + RollingSkewOptions, +} from "./types"; +import { DataType } from "./datatypes"; +/** + * Arithmetic operations + */ export interface Arithmetic { - add(rhs: any): T; - sub(rhs: any): T; - div(rhs: any): T; - mul(rhs: any): T; - rem(rhs: any): T; - plus(rhs: any): T; - minus(rhs: any): T; - divideBy(rhs: any): T; - multiplyBy(rhs: any): T; - modulo(rhs: any): T; + /** + * Add self to other + * @category Arithmetic + */ + add(other: any): T; + /** + * Subtract other from self + * @category Arithmetic + */ + sub(other: any): T; + /** + * Divide self by other + * @category Arithmetic + */ + div(other: any): T; + /** + * Multiply self by other + * @category Arithmetic + */ + mul(other: any): T; + /** + * Get the remainder of self divided by other + * @category Arithmetic + */ + rem(other: any): T; + /** + * Add self to other + * @category Arithmetic + */ + plus(other: any): T; + /** + * Subtract other from self + * @category Arithmetic + */ + minus(other: any): T; + /** + * Divide self by other + * @category Arithmetic + */ + divideBy(other: any): T; + /** + * Multiply self by other + * @category Arithmetic + */ + multiplyBy(other: any): T; + /** + * Get the remainder of self divided by other + * @category Arithmetic + */ + modulo(other: any): T; } export interface Comparison { - eq(rhs: any): T; - equals(rhs: any): T; - gtEq(rhs: any): T; - greaterThanEquals(rhs: any): T; - gt(rhs: any): T; - greaterThan(rhs: any): T; - ltEq(rhs: any): T; - lessThanEquals(rhs: any): T; - lt(rhs: any): T; - lessThan(rhs: any): T; - neq(rhs: any): T; - notEquals(rhs: any): T; + /** + * Compare self to other: `self == other` + * @category Comparison + */ + eq(other: any): T; + /** + * Compare self to other: `self == other` + * @category Comparison + */ + equals(other: any): T; + /** + * Compare self to other: `self >= other` + * @category Comparison + */ + gtEq(other: any): T; + /** + * Compare self to other: `self >= other` + * @category Comparison + */ + greaterThanEquals(other: any): T; + /** + * Compare self to other: `self > other` + * @category Comparison + */ + gt(other: any): T; + /** + * Compare self to other: `self > other` + * @category Comparison + */ + greaterThan(other: any): T; + /** + * Compare self to other: `self <= other` + * @category Comparison + */ + ltEq(other: any): T; + /** + * Compare self to other: `self =< other` + * @category Comparison + */ + lessThanEquals(other: any): T; + /** + * Compare self to other: `self < other` + * @category Comparison + */ + lt(other: any): T; + /** + * Compare self to other: `self < other` + * @category Comparison + */ + lessThan(other: any): T; + /** + * Compare self to other: `self !== other` + * @category Comparison + */ + neq(other: any): T; + /** + * Compare self to other: `self !== other` + * @category Comparison + */ + notEquals(other: any): T; } +/** + * A trait for cumulative operations. + */ export interface Cumulative { - /** Get an array with the cumulative count computed at every element. */ + /** + * Get an array with the cumulative count computed at every element. + * @category Cumulative + */ cumCount(reverse?: boolean): T; cumCount({ reverse }: { reverse: boolean }): T; /** @@ -57,8 +144,8 @@ export interface Cumulative { * @param reverse - reverse the operation * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.cumMax() + * > const s = pl.Series("a", [1, 2, 3]) + * > s.cumMax() * shape: (3,) * Series: 'b' [i64] * [ @@ -67,6 +154,7 @@ export interface Cumulative { * 3 * ] * ``` + * @category Cumulative */ cumMax(reverse?: boolean): T; cumMax({ reverse }: { reverse: boolean }): T; @@ -76,8 +164,8 @@ export interface Cumulative { * @param reverse - reverse the operation * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.cumMin() + * > const s = pl.Series("a", [1, 2, 3]) + * > s.cumMin() * shape: (3,) * Series: 'b' [i64] * [ @@ -86,6 +174,7 @@ export interface Cumulative { * 1 * ] * ``` + * @category Cumulative */ cumMin(reverse?: boolean): T; cumMin({ reverse }: { reverse: boolean }): T; @@ -95,8 +184,8 @@ export interface Cumulative { * @param reverse - reverse the operation * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.cumProd() + * > const s = pl.Series("a", [1, 2, 3]) + * > s.cumProd() * shape: (3,) * Series: 'b' [i64] * [ @@ -105,6 +194,7 @@ export interface Cumulative { * 6 * ] * ``` + * @category Cumulative */ cumProd(reverse?: boolean): T; cumProd({ reverse }: { reverse: boolean }): T; @@ -114,8 +204,8 @@ export interface Cumulative { * @param reverse - reverse the operation * @example * ``` - * > const s = pl.Series("a", [1, 2, 3]) - * > s.cumSum() + * > const s = pl.Series("a", [1, 2, 3]) + * > s.cumSum() * shape: (3,) * Series: 'b' [i64] * [ @@ -124,11 +214,15 @@ export interface Cumulative { * 6 * ] * ``` + * @category Cumulative */ cumSum(reverse?: boolean): T; cumSum({ reverse }: { reverse: boolean }): T; } +/** + * __A trait for DataFrame and Series that allows for the application of a rolling window.__ + */ export interface Rolling { /** * __Apply a rolling max (moving max) over the values in this Series.__ @@ -144,6 +238,7 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ rollingMax(options: RollingOptions): T; rollingMax( @@ -166,6 +261,7 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ rollingMean(options: RollingOptions): T; rollingMean( @@ -188,6 +284,7 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ rollingMin(options: RollingOptions): T; rollingMin( @@ -209,6 +306,7 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ rollingStd(options: RollingOptions): T; rollingStd( @@ -231,6 +329,7 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ rollingSum(options: RollingOptions): T; rollingSum( @@ -253,6 +352,7 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ rollingVar(options: RollingOptions): T; rollingVar( @@ -261,7 +361,10 @@ export interface Rolling { minPeriods?: Array, center?: boolean, ): T; - /** Compute a rolling median */ + /** + * Compute a rolling median + * @category Rolling + */ rollingMedian(options: RollingOptions): T; rollingMedian( windowSize: number, @@ -279,16 +382,12 @@ export interface Rolling { * @param minPeriods The number of values in the window that should be non-null before computing a result. * If undefined, it will be set equal to window size. * @param center - Set the labels at the center of the window + * @category Rolling */ - rollingQuantile( - options: RollingOptions & { - quantile: number; - interpolation?: Interpolation; - }, - ): T; + rollingQuantile(options: RollingQuantileOptions): T; rollingQuantile( quantile: number, - interpolation?: Interpolation, + interpolation?: InterpolationMethod, windowSize?: number, weights?: Array, minPeriods?: Array, @@ -298,9 +397,17 @@ export interface Rolling { * Compute a rolling skew * @param windowSize Size of the rolling window * @param bias If false, then the calculations are corrected for statistical bias. + * @category Rolling */ rollingSkew(windowSize: number, bias?: boolean): T; - rollingSkew({ windowSize, bias }: { windowSize: number; bias?: boolean }): T; + /** + * Compute a rolling skew + * @param options + * @param options.windowSize Size of the rolling window + * @param options.bias If false, then the calculations are corrected for statistical bias. + * @category Rolling + */ + rollingSkew(options: RollingSkewOptions): T; } export interface Round { @@ -309,17 +416,20 @@ export interface Round { * * Similar functionality to javascript `toFixed` * @param decimals number of decimals to round by. + * @category Math */ 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 + * @category Math */ floor(): T; /** * Ceil underlying floating point array to the highest integers smaller or equal to the float value. * Only works on floating point Series + * @category Math */ ceil(): T; @@ -329,6 +439,7 @@ export interface Round { * If you want to clip other dtypes, consider writing a when -> then -> otherwise expression * @param min Minimum value * @param max Maximum value + * @category Math */ clip(min: number, max: number): T; clip(options: { min: number; max: number }); @@ -343,12 +454,12 @@ export interface Sample { * @param seed - Seed initialization. If not provided, a random seed will be used * @example * ``` - * >>> df = pl.DataFrame({ - * >>> "foo": [1, 2, 3], - * >>> "bar": [6, 7, 8], - * >>> "ham": ['a', 'b', 'c'] - * >>> }) - * >>> df.sample({n: 2}) + * > df = pl.DataFrame({ + * > "foo": [1, 2, 3], + * > "bar": [6, 7, 8], + * > "ham": ['a', 'b', 'c'] + * > }) + * > df.sample({n: 2}) * shape: (2, 3) * ╭─────┬─────┬─────╮ * │ foo ┆ bar ┆ ham │ @@ -360,6 +471,7 @@ export interface Sample { * │ 3 ┆ 8 ┆ "c" │ * ╰─────┴─────┴─────╯ * ``` + * @category Math */ sample(opts?: { @@ -385,6 +497,9 @@ export interface Bincode { getState(T): Uint8Array; } +/** + * Functions that can be applied to dtype List + */ export interface ListFunctions { argMin(): T; argMax(): T; @@ -410,6 +525,7 @@ export interface ListFunctions { * │ ["x", "y", "z"] │ * └─────────────────┘ * ``` + * @category List */ concat(other: (string | T)[] | string | T): T; /** @@ -433,6 +549,7 @@ export interface ListFunctions { * │ true │ * └───────┘ * ``` + * @category List */ contains(item: any): T; /** @@ -450,6 +567,7 @@ export interface ListFunctions { * [null, -8, -1] * ] * ``` + * @category List */ diff(n?: number, nullBehavior?: "ignore" | "drop"): T; /** @@ -457,45 +575,49 @@ export interface ListFunctions { * So index `0` would return the first item of every sublist * and index `-1` would return the last item of every sublist * if an index is out of bounds, it will return a `null`. + * @category List */ get(index: number | Expr): T; /** - Run any polars expression against the lists' elements - Parameters - ---------- - @param expr - Expression to run. Note that you can select an element with `pl.first()`, or `pl.col()` - @param parallel - Run all expression parallel. Don't activate this blindly. - Parallelism is worth it if there is enough work to do per thread. - This likely should not be use in the groupby context, because we already parallel execution per group - @example - -------- - >>> df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]}) - >>> df.withColumn( - ... pl.concatList(["a", "b"]).lst.eval(pl.first().rank()).alias("rank") - ... ) - shape: (3, 3) - ┌─────┬─────┬────────────┐ - │ a ┆ b ┆ rank │ - │ --- ┆ --- ┆ --- │ - │ i64 ┆ i64 ┆ list [f32] │ - ╞═════╪═════╪════════════╡ - │ 1 ┆ 4 ┆ [1.0, 2.0] │ - ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ - │ 8 ┆ 5 ┆ [2.0, 1.0] │ - ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ - │ 3 ┆ 2 ┆ [2.0, 1.0] │ - └─────┴─────┴────────────┘ + * Run any polars expression against the lists' elements + * Parameters + * ---------- + * @param expr + * Expression to run. Note that you can select an element with `pl.first()`, or `pl.col()` + * @param parallel + * Run all expression parallel. Don't activate this blindly. + * Parallelism is worth it if there is enough work to do per thread. + * This likely should not be use in the groupby context, because we already parallel execution per group + * @example + * >df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]}) + * >df.withColumn( + * ... pl.concatList(["a", "b"]).lst.eval(pl.first().rank()).alias("rank") + * ... ) + * shape: (3, 3) + * ┌─────┬─────┬────────────┐ + * │ a ┆ b ┆ rank │ + * │ --- ┆ --- ┆ --- │ + * │ i64 ┆ i64 ┆ list [f32] │ + * ╞═════╪═════╪════════════╡ + * │ 1 ┆ 4 ┆ [1.0, 2.0] │ + * ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 8 ┆ 5 ┆ [2.0, 1.0] │ + * ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ + * │ 3 ┆ 2 ┆ [2.0, 1.0] │ + * └─────┴─────┴────────────┘ + * @category List */ eval(expr: Expr, parallel?: boolean): T; - /** Get the first value of the sublists. */ + /** + * Get the first value of the sublists. + * @category List + */ first(): T; /** * Slice the head of every sublist - * @param n How many values to take in the slice. + * @param n - How many values to take in the slice. * @example - * -------- + * ``` * s = pl.Series("a", [[1, 2, 3, 4], [10, 2, 1]]) * s.lst.head(2) * shape: (2,) @@ -504,13 +626,15 @@ export interface ListFunctions { * [1, 2] * [10, 2] * ] + * ``` + * @category List */ head(n: number): T; /** * Slice the tail of every sublist - * @param n How many values to take in the slice. + * @param n - How many values to take in the slice. * @example - * -------- + * ``` * s = pl.Series("a", [[1, 2, 3, 4], [10, 2, 1]]) * s.lst.tail(2) * shape: (2,) @@ -519,6 +643,8 @@ export interface ListFunctions { * [3, 4] * [2, q] * ] + * ``` + * @category List */ tail(n: number): T; /** @@ -526,23 +652,74 @@ export interface ListFunctions { * This errors if inner type of list `!= Utf8`. * @param separator A string used to separate one element of the list from the next in the resulting string. * If omitted, the list elements are separated with a comma. + * @category List */ join(separator?: string): T; - /** Get the last value of the sublists. */ + /** + * Get the last value of the sublists. + * @category List + */ last(): T; + /** + * Get the length of the sublists. + * @category List + */ lengths(): T; + /** + * Get the maximum value of the sublists. + * @category List + */ max(): T; + /** + * Get the mean value of the sublists. + * @category List + */ mean(): T; + /** + * Get the median value of the sublists. + * @category List + */ min(): T; + /** + * Reverse the sublists. + * @category List + */ reverse(): T; + /** + * Shift the sublists. + * @param periods - Number of periods to shift. Can be positive or negative. + * @category List + */ shift(periods: number): T; + /** + * Slice the sublists. + * @param offset - The offset of the slice. + * @param length - The length of the slice. + * @category List + */ slice(offset: number, length: number): T; + /** + * Sort the sublists. + * @param reverse - Sort in reverse order. + * @category List + */ sort(reverse?: boolean): T; sort(opt: { reverse: boolean }): T; + /** + * Sum all elements of the sublists. + * @category List + */ sum(): T; + /** + * Get the unique values of the sublists. + * @category List + */ unique(): T; } +/** + * Functions that can be applied to a Date or Datetime column. + */ export interface DateFunctions { /** * Extract day from underlying Date representation. @@ -637,6 +814,174 @@ export interface DateFunctions { year(): T; } +export interface StringFunctions { + /** + * Vertically concat the values in the Series to a single string value. + * @example + * ``` + * > df = pl.DataFrame({"foo": [1, null, 2]}) + * > df = df.select(pl.col("foo").str.concat("-")) + * > df + * shape: (1, 1) + * ┌──────────┐ + * │ foo │ + * │ --- │ + * │ str │ + * ╞══════════╡ + * │ 1-null-2 │ + * └──────────┘ + * ``` + */ + concat(delimiter: string): T; + /** Check if strings in Series contain regex pattern. */ + contains(pat: string | RegExp): T; + /** + * Decodes a value using the provided encoding + * @param encoding - hex | base64 + * @param strict - how to handle invalid inputs + * + * - true: method will throw error if unable to decode a value + * - false: unhandled values will be replaced with `null` + * @example + * ``` + * > df = pl.DataFrame({"strings": ["666f6f", "626172", null]}) + * > df.select(col("strings").str.decode("hex")) + * shape: (3, 1) + * ┌─────────┐ + * │ strings │ + * │ --- │ + * │ str │ + * ╞═════════╡ + * │ foo │ + * ├╌╌╌╌╌╌╌╌╌┤ + * │ bar │ + * ├╌╌╌╌╌╌╌╌╌┤ + * │ null │ + * └─────────┘ + * ``` + */ + decode(encoding: "hex" | "base64", strict?: boolean): T; + decode(options: { encoding: "hex" | "base64"; strict?: boolean }): T; + /** + * Encodes a value using the provided encoding + * @param encoding - hex | base64 + * @example + * ``` + * > df = pl.DataFrame({"strings", ["foo", "bar", null]}) + * > df.select(col("strings").str.encode("hex")) + * shape: (3, 1) + * ┌─────────┐ + * │ strings │ + * │ --- │ + * │ str │ + * ╞═════════╡ + * │ 666f6f │ + * ├╌╌╌╌╌╌╌╌╌┤ + * │ 626172 │ + * ├╌╌╌╌╌╌╌╌╌┤ + * │ null │ + * └─────────┘ + * ``` + */ + encode(encoding: "hex" | "base64"): T; + /** + * Extract the target capture group from provided patterns. + * @param pattern A valid regex pattern + * @param groupIndex Index of the targeted capture group. + * Group 0 mean the whole pattern, first group begin at index 1 + * Default to the first capture group + * @returns Utf8 array. Contain null if original value is null or regex capture nothing. + * @example + * ``` + * > df = pl.DataFrame({ + * ... 'a': [ + * ... 'http://vote.com/ballon_dor?candidate=messi&ref=polars', + * ... 'http://vote.com/ballon_dor?candidat=jorginho&ref=polars', + * ... 'http://vote.com/ballon_dor?candidate=ronaldo&ref=polars' + * ... ]}) + * > df.select(pl.col('a').str.extract(/candidate=(\w+)/, 1)) + * shape: (3, 1) + * ┌─────────┐ + * │ a │ + * │ --- │ + * │ str │ + * ╞═════════╡ + * │ messi │ + * ├╌╌╌╌╌╌╌╌╌┤ + * │ null │ + * ├╌╌╌╌╌╌╌╌╌┤ + * │ ronaldo │ + * └─────────┘ + * ``` + */ + extract(pat: string | RegExp, groupIndex: number): T; + /** + * Extract the first match of json string with provided JSONPath expression. + * Throw errors if encounter invalid json strings. + * All return value will be casted to Utf8 regardless of the original value. + * @see https://goessner.net/articles/JsonPath/ + * @param jsonPath - A valid JSON path query string + * @returns Utf8 array. Contain null if original value is null or the `jsonPath` return nothing. + * @example + * ``` + * > df = pl.DataFrame({ + * ... 'json_val': [ + * ... '{"a":"1"}', + * ... null, + * ... '{"a":2}', + * ... '{"a":2.1}', + * ... '{"a":true}' + * ... ] + * ... }) + * > df.select(pl.col('json_val').str.jsonPathMatch('$.a') + * shape: (5,) + * Series: 'json_val' [str] + * [ + * "1" + * null + * "2" + * "2.1" + * "true" + * ] + * ``` + */ + jsonPathMatch(pat: string): T; + /** Get length of the string values in the Series. */ + lengths(): T; + /** Remove leading whitespace. */ + lstrip(): T; + /** Replace first regex match with a string value. */ + replace(pat: string | RegExp, val: string): T; + /** Replace all regex matches with a string value. */ + replaceAll(pat: string | RegExp, val: string): T; + /** Modify the strings to their lowercase equivalent. */ + toLowerCase(): T; + /** Modify the strings to their uppercase equivalent. */ + toUpperCase(): T; + /** Remove trailing whitespace. */ + rstrip(): T; + /** + * Create subslices of the string values of a Utf8 Series. + * @param start - Start of the slice (negative indexing may be used). + * @param length - Optional length of the slice. + */ + slice(start: number, length?: number): T; + /** + * Split a string into substrings using the specified separator and return them as a Series. + * @param separator — A string that identifies character or characters to use in separating the string. + * @param inclusive Include the split character/string in the results + */ + split(by: string, options?: { inclusive?: boolean } | boolean): T; + /** Remove leading and trailing whitespace. */ + strip(): T; + /** + * Parse a Series of dtype Utf8 to a Date/Datetime Series. + * @param datatype Date or Datetime. + * @param fmt formatting syntax. [Read more](https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html) + */ + strptime(datatype: DataType.Date | DataType.Datetime, fmt?: string): T; +} + export interface Serialize { /** * Serializes object to desired format via [serde](https://serde.rs/) @@ -656,6 +1001,9 @@ export interface Deserialize { deserialize(buf: Buffer, format: "json" | "bincode"): T; } +/** + * GroupBy operations that can be applied to a DataFrame or LazyFrame. + */ export interface GroupByOps { /** Create rolling groups based on a time column (or index value of type Int32, Int64). @@ -703,7 +1051,7 @@ export interface GroupByOps { @example ``` - >>> dates = [ + >dates = [ ... "2020-01-01 13:45:48", ... "2020-01-01 16:42:13", ... "2020-01-01 16:45:09", @@ -711,20 +1059,20 @@ export interface GroupByOps { ... "2020-01-03 19:45:32", ... "2020-01-08 23:16:43", ... ] - >>> df = pl.DataFrame({"dt": dates, "a": [3, 7, 5, 9, 2, 1]}).withColumn( + >df = pl.DataFrame({"dt": dates, "a": [3, 7, 5, 9, 2, 1]}).withColumn( ... pl.col("dt").str.strptime(pl.Datetime) ... ) - >>> out = df.groupbyRolling({indexColumn:"dt", period:"2d"}).agg( + >out = df.groupbyRolling({indexColumn:"dt", period:"2d"}).agg( ... [ ... pl.sum("a").alias("sum_a"), ... pl.min("a").alias("min_a"), ... pl.max("a").alias("max_a"), ... ] ... ) - >>> assert(out["sum_a"].toArray() === [3, 10, 15, 24, 11, 1]) - >>> assert(out["max_a"].toArray() === [3, 7, 7, 9, 9, 1]) - >>> assert(out["min_a"].toArray() === [3, 3, 3, 3, 2, 1]) - >>> out + >assert(out["sum_a"].toArray() === [3, 10, 15, 24, 11, 1]) + >assert(out["max_a"].toArray() === [3, 7, 7, 9, 9, 1]) + >assert(out["min_a"].toArray() === [3, 3, 3, 3, 2, 1]) + >out shape: (6, 4) ┌─────────────────────┬───────┬───────┬───────┐ │ dt ┆ a_sum ┆ a_max ┆ a_min │ diff --git a/polars/types.ts b/polars/types.ts new file mode 100644 index 000000000..1f25d341d --- /dev/null +++ b/polars/types.ts @@ -0,0 +1,215 @@ +/** + * Downsample rules + */ +export type DownsampleRule = + | "month" + | "week" + | "day" + | "hour" + | "minute" + | "second"; +/** + * Fill null strategies + */ +export type FillNullStrategy = + | "backward" + | "forward" + | "mean" + | "min" + | "max" + | "zero" + | "one"; + +/** + * Rank methods + */ +export type RankMethod = + | "average" + | "min" + | "max" + | "dense" + | "ordinal" + | "random"; + +/** + * Options for {@link concat} + */ +export interface ConcatOptions { + rechunk?: boolean; + how?: "vertical" | "horizontal"; +} +/** + * Options for {@link DataFrame.writeCSV} + * @category Options + */ +export interface WriteCsvOptions { + hasHeader?: boolean; + sep?: string; +} + +/** + * Options for {@link DataFrame.writeJSON} + * @category Options + */ +export interface WriteJsonOptions { + orient?: "row" | "col" | "dataframe"; + multiline?: boolean; +} + +/** + * Options for {@link scanJson} + */ +export interface JsonScanOptions { + inferSchemaLength?: number; + nThreads?: number; + batchSize?: number; + lowMemory?: boolean; + numRows?: number; + skipRows?: number; + rowCount?: RowCount; +} + +/** + * Options for {@link DataFrame.writeParquet} + * @category Options + */ +export interface WriteParquetOptions { + compression?: + | "uncompressed" + | "snappy" + | "gzip" + | "lzo" + | "brotli" + | "lz4" + | "zstd"; +} +/** + * Options for {@link readParquet} + */ +export interface ReadParquetOptions { + columns?: string[] | number[]; + numRows?: number; + parallel?: "auto" | "columns" | "row_groups" | "none"; + rowCount?: RowCount; +} +/** + * Options for {@link scanParquet} + */ +export interface ScanParquetOptions { + columns?: string[] | number[]; + numRows?: number; + parallel?: "auto" | "columns" | "row_groups" | "none"; + rowCount?: RowCount; + cache?: boolean; + rechunk?: boolean; +} + +/** + * Add row count as column + */ +export interface RowCount { + /** name of column */ + name: string; + /** offset */ + offset: string; +} + +/** + * Options for {@link DataFrame.writeIPC} + * @category Options + */ +export interface WriteIPCOptions { + compression?: "uncompressed" | "lz4" | "zstd"; +} + +/** + * Options for writing Avro files + * @category Options + */ +export interface WriteAvroOptions { + compression?: "uncompressed" | "snappy" | "deflate"; +} + +/** + * Interpolation types + */ +export type InterpolationMethod = + | "nearest" + | "higher" + | "lower" + | "midpoint" + | "linear"; + +/** + * Join types + */ +export type JoinType = "left" | "inner" | "outer" | "semi" | "anti" | "cross"; + +/** @ignore */ +export type JoinBaseOptions = { + how?: JoinType; + suffix?: string; +}; +/** + * options for join operations @see {@link DataFrame.join} + */ +export interface JoinOptions { + /** left join column */ + leftOn?: string | Array; + /** right join column */ + rightOn?: string | Array; + /** left and right join column */ + on?: string | Array; + /** join type */ + how?: JoinType; + suffix?: string; +} + +/** + * options for lazy join operations @see {@link LazyDataFrame.join} + */ +export interface LazyJoinOptions extends JoinOptions { + allowParallel?: boolean; + forceParallel?: boolean; +} + +/** + * options for lazy operations @see {@link LazyDataFrame.collect} + */ +export type LazyOptions = { + typeCoercion?: boolean; + predicatePushdown?: boolean; + projectionPushdown?: boolean; + simplifyExpression?: boolean; + stringCache?: boolean; + noOptimization?: boolean; +}; + +/** + * options for rolling window operations + * @category Options + */ +export interface RollingOptions { + windowSize: number; + weights?: Array; + minPeriods?: number; + center?: boolean; +} + +/** + * options for rolling quantile operations + * @category Options + */ +export interface RollingQuantileOptions extends RollingOptions { + quantile: number; + interpolation?: InterpolationMethod; +} + +/** + * options for rolling mean operations + * @category Options + */ +export interface RollingSkewOptions { + windowSize: number; + bias?: boolean; +} diff --git a/polars/utils.ts b/polars/utils.ts index a2285be5e..e33c9f659 100644 --- a/polars/utils.ts +++ b/polars/utils.ts @@ -1,39 +1,45 @@ -import {Expr, exprToLitOrExpr} from "./lazy/expr"; -import {Series} from "./series/series"; -import {DataFrame} from "./dataframe"; +import { Expr, exprToLitOrExpr } from "./lazy/expr"; +import { Series } from "./series"; +import { DataFrame } from "./dataframe"; import path from "path"; -import {isExternal, isRegExp} from "util/types"; +import { isExternal, isRegExp } from "util/types"; +/** @ignore */ export type ValueOrArray = T | Array>; -export type ColumnSelection = ValueOrArray -export type ExpressionSelection = ValueOrArray -export type ColumnsOrExpr = ColumnSelection | ExpressionSelection -export type ExprOrString = Expr | string -export type DownsampleRule = "month" | "week" | "day" | "hour" | "minute" | "second" -export type FillNullStrategy = "backward" | "forward" | "mean" | "min" | "max" | "zero" | "one" -export type RankMethod = "average" | "min" | "max" | "dense" | "ordinal" | "random"; -export type RollingOptions = { - windowSize: number, - weights?: Array, - minPeriods?: number, - center?: boolean -}; +/** @ignore */ +export type ColumnSelection = ValueOrArray; +/** @ignore */ +export type ExpressionSelection = ValueOrArray; +/** @ignore */ +export type ColumnsOrExpr = ColumnSelection | ExpressionSelection; +/** @ignore */ +export type ExprOrString = Expr | string; -export function columnOrColumns(columns: ColumnSelection | string | Array | undefined): Array | undefined { +/** @ignore */ +export function columnOrColumns( + columns: ColumnSelection | string | Array | undefined, +): Array | undefined { if (columns) { return columnOrColumnsStrict(columns); } } -export function columnOrColumnsStrict(...columns: string[] | ValueOrArray[]): Array { +/** @ignore */ +export function columnOrColumnsStrict( + ...columns: string[] | ValueOrArray[] +): Array { return columns.flat(3) as any; } +/** @ignore */ export function selectionToExprList(columns: any[], stringToLit?) { - return [columns].flat(3).map(expr => exprToLitOrExpr(expr, stringToLit)._expr); + return [columns] + .flat(3) + .map((expr) => exprToLitOrExpr(expr, stringToLit)._expr); } +/** @ignore */ export function isPath(s: string, expectedExtensions?: string[]): boolean { - const {base, ext, name} = path.parse(s); + const { base, ext, name } = path.parse(s); - return Boolean(base && ext && name) && !!(expectedExtensions?.includes(ext)); + return Boolean(base && ext && name) && !!expectedExtensions?.includes(ext); } export const range = (start: number, end: number) => { @@ -42,13 +48,16 @@ export const range = (start: number, end: number) => { return Array.from({ length }, (_, i) => start + i); }; - -export const isDataFrameArray = (ty: any): ty is DataFrame[] => Array.isArray(ty) && DataFrame.isDataFrame(ty[0]); -export const isSeriesArray = (ty: any): ty is Series[] => Array.isArray(ty) && Series.isSeries(ty[0]); -export const isExprArray = (ty: any): ty is Expr[] => Array.isArray(ty) && Expr.isExpr(ty[0]); -export const isIterator = (ty: any): ty is Iterable => ty !== null && typeof ty[Symbol.iterator] === "function"; +export const isDataFrameArray = (ty: any): ty is DataFrame[] => + Array.isArray(ty) && DataFrame.isDataFrame(ty[0]); +export const isSeriesArray = (ty: any): ty is Series[] => + Array.isArray(ty) && Series.isSeries(ty[0]); +export const isExprArray = (ty: any): ty is Expr[] => + Array.isArray(ty) && Expr.isExpr(ty[0]); +export const isIterator = (ty: any): ty is Iterable => + ty !== null && typeof ty[Symbol.iterator] === "function"; export const regexToString = (r: string | RegExp): string => { - if(isRegExp(r)) { + if (isRegExp(r)) { return r.source; } diff --git a/rome.json b/rome.json new file mode 100644 index 000000000..e701a4932 --- /dev/null +++ b/rome.json @@ -0,0 +1,30 @@ +{ + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off" + } + }, + "ignore": [ + "polars/native-polars.js", + "./docs/*", + "./bin/*" + ] + }, + "formatter": { + "indentSize": 2, + "indentStyle": "space", + "ignore": [ + "polars/native-polars.js", + "./docs/*", + "./bin/*" + ] + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} \ No newline at end of file diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index c9595a36f..000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2022-11-24 \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..270b314ac --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2022-11-24" \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 6b656330d..e4b5e893c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ } }, "exclude": [ + "./target/*", "./docs/*", "./coverage/*", "./examples/*", @@ -31,22 +32,15 @@ "./jest.config.ts" ], "typedocOptions": { + "entryPointStrategy": "expand", "excludePrivate": true, + "excludeProtected": true, + "excludeExternals": true, + "categorizeByGroup": true, "entryPoints": [ - "polars/series/series.ts", - "polars/dataframe.ts", - "polars/groupby.ts", - "polars/io.ts", - "polars/cfg.ts", - "polars/lazy/expr.ts", - "polars/lazy/dataframe.ts", - "polars/lazy/functions.ts", - "polars/lazy/groupby.ts", - "polars/lazy/whenthen.ts", - "polars/series/datetime.ts", - "polars/series/string.ts", - "polars/series/list.ts" + "polars/index.ts" ], - "out": "docs" + "out": "docs", + "sort": ["alphabetical"] } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index adffcd0ff..7c2b31988 100644 --- a/yarn.lock +++ b/yarn.lock @@ -406,23 +406,6 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.4.0": - version: 1.4.0 - resolution: "@eslint/eslintrc@npm:1.4.0" - dependencies: - ajv: ^6.12.4 - debug: ^4.3.2 - espree: ^9.4.0 - globals: ^13.19.0 - ignore: ^5.2.0 - import-fresh: ^3.2.1 - js-yaml: ^4.1.0 - minimatch: ^3.1.2 - strip-json-comments: ^3.1.1 - checksum: 73e39c833deafde8d8706e6fa9b52b6d99927c094ead8e405ea4174e8197ec24aac9ba88ae38cc8ad32eaccf07b9c7fc5dc70761d1fba6da41a928691447305f - languageName: node - linkType: hard - "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -430,31 +413,6 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.8": - version: 0.11.8 - resolution: "@humanwhocodes/config-array@npm:0.11.8" - dependencies: - "@humanwhocodes/object-schema": ^1.2.1 - debug: ^4.1.1 - minimatch: ^3.0.5 - checksum: 0fd6b3c54f1674ce0a224df09b9c2f9846d20b9e54fabae1281ecfc04f2e6ad69bf19e1d6af6a28f88e8aa3990168b6cb9e1ef755868c3256a630605ec2cb1d3 - languageName: node - linkType: hard - -"@humanwhocodes/module-importer@npm:^1.0.1": - version: 1.0.1 - resolution: "@humanwhocodes/module-importer@npm:1.0.1" - checksum: 0fd22007db8034a2cdf2c764b140d37d9020bbfce8a49d3ec5c05290e77d4b0263b1b972b752df8c89e5eaa94073408f2b7d977aed131faf6cf396ebb5d7fb61 - languageName: node - linkType: hard - -"@humanwhocodes/object-schema@npm:^1.2.1": - version: 1.2.1 - resolution: "@humanwhocodes/object-schema@npm:1.2.1" - checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 - languageName: node - linkType: hard - "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.1.0 resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" @@ -747,33 +705,6 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" - dependencies: - "@nodelib/fs.stat": 2.0.5 - run-parallel: ^1.1.9 - checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59 - languageName: node - linkType: hard - -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 - languageName: node - linkType: hard - -"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" - dependencies: - "@nodelib/fs.scandir": 2.1.5 - fastq: ^1.6.0 - checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53 - languageName: node - linkType: hard - "@npmcli/fs@npm:^2.1.0": version: 2.1.2 resolution: "@npmcli/fs@npm:2.1.2" @@ -794,6 +725,48 @@ __metadata: languageName: node linkType: hard +"@rometools/cli-darwin-arm64@npm:11.0.0": + version: 11.0.0 + resolution: "@rometools/cli-darwin-arm64@npm:11.0.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rometools/cli-darwin-x64@npm:11.0.0": + version: 11.0.0 + resolution: "@rometools/cli-darwin-x64@npm:11.0.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rometools/cli-linux-arm64@npm:11.0.0": + version: 11.0.0 + resolution: "@rometools/cli-linux-arm64@npm:11.0.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@rometools/cli-linux-x64@npm:11.0.0": + version: 11.0.0 + resolution: "@rometools/cli-linux-x64@npm:11.0.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@rometools/cli-win32-arm64@npm:11.0.0": + version: 11.0.0 + resolution: "@rometools/cli-win32-arm64@npm:11.0.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rometools/cli-win32-x64@npm:11.0.0": + version: 11.0.0 + resolution: "@rometools/cli-win32-x64@npm:11.0.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@sinonjs/commons@npm:^1.7.0": version: 1.8.6 resolution: "@sinonjs/commons@npm:1.8.6" @@ -946,20 +919,6 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.9": - version: 7.0.11 - resolution: "@types/json-schema@npm:7.0.11" - checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d - languageName: node - linkType: hard - -"@types/json5@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/json5@npm:0.0.29" - checksum: e60b153664572116dfea673c5bda7778dbff150498f44f998e34b5886d8afc47f16799280e4b6e241c0472aef1bc36add771c569c68fc5125fc2ae519a3eb9ac - languageName: node - linkType: hard - "@types/node@npm:*": version: 18.11.17 resolution: "@types/node@npm:18.11.17" @@ -974,13 +933,6 @@ __metadata: languageName: node linkType: hard -"@types/parse-json@npm:^4.0.0": - version: 4.0.0 - resolution: "@types/parse-json@npm:4.0.0" - checksum: fd6bce2b674b6efc3db4c7c3d336bd70c90838e8439de639b909ce22f3720d21344f52427f1d9e57b265fcb7f6c018699b99e5e0c208a1a4823014269a6bf35b - languageName: node - linkType: hard - "@types/prettier@npm:^2.1.5": version: 2.7.2 resolution: "@types/prettier@npm:2.7.2" @@ -988,13 +940,6 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12": - version: 7.3.13 - resolution: "@types/semver@npm:7.3.13" - checksum: 00c0724d54757c2f4bc60b5032fe91cda6410e48689633d5f35ece8a0a66445e3e57fa1d6e07eb780f792e82ac542948ec4d0b76eb3484297b79bd18b8cf1cb0 - languageName: node - linkType: hard - "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -1018,137 +963,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.4.0": - version: 5.47.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.47.0" - dependencies: - "@typescript-eslint/scope-manager": 5.47.0 - "@typescript-eslint/type-utils": 5.47.0 - "@typescript-eslint/utils": 5.47.0 - debug: ^4.3.4 - ignore: ^5.2.0 - natural-compare-lite: ^1.4.0 - regexpp: ^3.2.0 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependencies: - "@typescript-eslint/parser": ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: fd867eb2b668d1f476fd28d38c2df2a680bf510a265a6e714b28d8f77e7a37e74e32294b70262a6fd1aec99ddb2fddef0212c862b4465ca4f83bb1172476f6e7 - languageName: node - linkType: hard - -"@typescript-eslint/experimental-utils@npm:^5.0.0": - version: 5.47.0 - resolution: "@typescript-eslint/experimental-utils@npm:5.47.0" - dependencies: - "@typescript-eslint/utils": 5.47.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 75b8a47d3881f0a1ca5fd52ba757b5de499f2a898ddf4b6c2def5a9bd6176770e1995198ddd49370023f5fe088ad26d544b1ddfc5883e6dce06ae9fc8297c8c3 - languageName: node - linkType: hard - -"@typescript-eslint/parser@npm:^5.4.0": - version: 5.47.0 - resolution: "@typescript-eslint/parser@npm:5.47.0" - dependencies: - "@typescript-eslint/scope-manager": 5.47.0 - "@typescript-eslint/types": 5.47.0 - "@typescript-eslint/typescript-estree": 5.47.0 - debug: ^4.3.4 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 5c864ca74b86ca740c73e5b87d90d43bb832b20ba6be0a39089175435771527722a7bf0a8ef7ddbd64b85235fbb7f6dbe8ae55a8bc73c6242f5559d580a8a80c - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:5.47.0": - version: 5.47.0 - resolution: "@typescript-eslint/scope-manager@npm:5.47.0" - dependencies: - "@typescript-eslint/types": 5.47.0 - "@typescript-eslint/visitor-keys": 5.47.0 - checksum: f637268a4cb065a89bb53d72620cc553f8c0d9f00805d6e6aac558cc4d3c08f3329208b0b4d5566d21eb636b080d453e5890221baef0e4bc4d67251f07cccd0d - languageName: node - linkType: hard - -"@typescript-eslint/type-utils@npm:5.47.0": - version: 5.47.0 - resolution: "@typescript-eslint/type-utils@npm:5.47.0" - dependencies: - "@typescript-eslint/typescript-estree": 5.47.0 - "@typescript-eslint/utils": 5.47.0 - debug: ^4.3.4 - tsutils: ^3.21.0 - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: 504b3e883ac02cb8e69957b706e76cb79fa2192aa62702c2a658119f28f8f50f1e668efb62318e85aeda6522e1d948b59382cae4ef3300a3f4eea809a87dec26 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.47.0": - version: 5.47.0 - resolution: "@typescript-eslint/types@npm:5.47.0" - checksum: 5a856e190cc2103427dbe15ccbbf87238261b5ed0859390a9e55f93afc2057f79dcbb4ac0db4d35787466f5e73f271111d19b2e725cf444af41d30e09678bf7a - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:5.47.0": - version: 5.47.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.47.0" - dependencies: - "@typescript-eslint/types": 5.47.0 - "@typescript-eslint/visitor-keys": 5.47.0 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: a9adfe8955b7dc9dfa9f43d450b782b83f506eaadae2a13f4e1bbe6c733be446d3edb26910954aec1bdc60d94ecc55c4e200d5b19bb24e6742f02329a4fb3e8c - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:5.47.0": - version: 5.47.0 - resolution: "@typescript-eslint/utils@npm:5.47.0" - dependencies: - "@types/json-schema": ^7.0.9 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.47.0 - "@typescript-eslint/types": 5.47.0 - "@typescript-eslint/typescript-estree": 5.47.0 - eslint-scope: ^5.1.1 - eslint-utils: ^3.0.0 - semver: ^7.3.7 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: f168920eec6f77651107f190b4ecadd82951fe4e3c0321ff660ac7380f4315d5ae30a1b63b4d2818f5e6f007a3f34c5df202619c24ec3a7e2ef25b215ec7b813 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.47.0": - version: 5.47.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.47.0" - dependencies: - "@typescript-eslint/types": 5.47.0 - eslint-visitor-keys: ^3.3.0 - checksum: 2191c079154bdfd1b85b8cd24baa6c0f55c73527c6c8460789483555b4eb5c72e3dc6d1aa4bbac2cf7b86b474588b45682a8deb151e9d903cf72c8f336141f1f - languageName: node - linkType: hard - "abab@npm:^2.0.3, abab@npm:^2.0.5": version: 2.0.6 resolution: "abab@npm:2.0.6" @@ -1173,15 +987,6 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.3.2": - version: 5.3.2 - resolution: "acorn-jsx@npm:5.3.2" - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: c3d3b2a89c9a056b205b69530a37b972b404ee46ec8e5b341666f9513d3163e2a4f214a71f4dfc7370f5a9c07472d2fd1c11c91c3f03d093e37637d95da98950 - languageName: node - linkType: hard - "acorn-walk@npm:^7.1.1": version: 7.2.0 resolution: "acorn-walk@npm:7.2.0" @@ -1205,7 +1010,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.0": +"acorn@npm:^8.2.4, acorn@npm:^8.4.1": version: 8.8.1 resolution: "acorn@npm:8.8.1" bin: @@ -1244,26 +1049,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.4": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" - dependencies: - fast-deep-equal: ^3.1.1 - fast-json-stable-stringify: ^2.0.0 - json-schema-traverse: ^0.4.1 - uri-js: ^4.2.2 - checksum: 874972efe5c4202ab0a68379481fbd3d1b5d0a7bd6d3cc21d40d3536ebff3352a2a1fabb632d4fd2cc7fe4cbdcd5ed6782084c9bbf7f32a1536d18f9da5007d4 - languageName: node - linkType: hard - -"ansi-colors@npm:^4.1.1": - version: 4.1.3 - resolution: "ansi-colors@npm:4.1.3" - checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e - languageName: node - linkType: hard - -"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": +"ansi-escapes@npm:^4.2.1": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -1347,52 +1133,6 @@ __metadata: languageName: node linkType: hard -"argparse@npm:^2.0.1": - version: 2.0.1 - resolution: "argparse@npm:2.0.1" - checksum: 83644b56493e89a254bae05702abf3a1101b4fa4d0ca31df1c9985275a5a5bd47b3c27b7fa0b71098d41114d8ca000e6ed90cad764b306f8a503665e4d517ced - languageName: node - linkType: hard - -"array-includes@npm:^3.1.4": - version: 3.1.6 - resolution: "array-includes@npm:3.1.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - get-intrinsic: ^1.1.3 - is-string: ^1.0.7 - checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 - languageName: node - linkType: hard - -"array-union@npm:^2.1.0": - version: 2.1.0 - resolution: "array-union@npm:2.1.0" - checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d - languageName: node - linkType: hard - -"array.prototype.flat@npm:^1.2.5": - version: 1.3.1 - resolution: "array.prototype.flat@npm:1.3.1" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - es-shim-unscopables: ^1.0.0 - checksum: 5a8415949df79bf6e01afd7e8839bbde5a3581300e8ad5d8449dea52639e9e59b26a467665622783697917b43bf39940a6e621877c7dd9b3d1c1f97484b9b88b - languageName: node - linkType: hard - -"astral-regex@npm:^2.0.0": - version: 2.0.0 - resolution: "astral-regex@npm:2.0.0" - checksum: 876231688c66400473ba505731df37ea436e574dd524520294cc3bbc54ea40334865e01fa0d074d74d036ee874ee7e62f486ea38bc421ee8e6a871c06f011766 - languageName: node - linkType: hard - "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -1593,16 +1333,6 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" - dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 - languageName: node - linkType: hard - "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -1694,25 +1424,6 @@ __metadata: languageName: node linkType: hard -"cli-cursor@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-cursor@npm:3.1.0" - dependencies: - restore-cursor: ^3.1.0 - checksum: 2692784c6cd2fd85cfdbd11f53aea73a463a6d64a77c3e098b2b4697a20443f430c220629e1ca3b195ea5ac4a97a74c2ee411f3807abf6df2b66211fec0c0a29 - languageName: node - linkType: hard - -"cli-truncate@npm:2.1.0, cli-truncate@npm:^2.1.0": - version: 2.1.0 - resolution: "cli-truncate@npm:2.1.0" - dependencies: - slice-ansi: ^3.0.0 - string-width: ^4.2.0 - checksum: bf1e4e6195392dc718bf9cd71f317b6300dc4a9191d052f31046b8773230ece4fa09458813bf0e3455a5e68c0690d2ea2c197d14a8b85a7b5e01c97f4b5feb5d - languageName: node - linkType: hard - "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -1779,20 +1490,6 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^1.4.0": - version: 1.4.0 - resolution: "colorette@npm:1.4.0" - checksum: 01c3c16058b182a4ab4c126a65a75faa4d38a20fa7c845090b25453acec6c371bb2c5dceb0a2338511f17902b9d1a9af0cadd8509c9403894b79311032c256c3 - languageName: node - linkType: hard - -"colorette@npm:^2.0.16": - version: 2.0.19 - resolution: "colorette@npm:2.0.19" - checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 - languageName: node - linkType: hard - "combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" @@ -1802,13 +1499,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^8.2.0": - version: 8.3.0 - resolution: "commander@npm:8.3.0" - checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 - languageName: node - linkType: hard - "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -1830,19 +1520,6 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^7.0.1": - version: 7.1.0 - resolution: "cosmiconfig@npm:7.1.0" - dependencies: - "@types/parse-json": ^4.0.0 - import-fresh: ^3.2.1 - parse-json: ^5.0.0 - path-type: ^4.0.0 - yaml: ^1.10.0 - checksum: c53bf7befc1591b2651a22414a5e786cd5f2eeaa87f3678a3d49d6069835a9d8d1aef223728e98aa8fec9a95bf831120d245096db12abe019fecb51f5696c96f - languageName: node - linkType: hard - "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -1850,7 +1527,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -1895,7 +1572,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.3": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -1907,24 +1584,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:^2.6.9": - version: 2.6.9 - resolution: "debug@npm:2.6.9" - dependencies: - ms: 2.0.0 - checksum: d2f51589ca66df60bf36e1fa6e4386b318c3f1e06772280eea5b1ae9fd3d05e9c2b7fd8a7d862457d00853c75b00451aa2d7459b924629ee385287a650f58fe6 - languageName: node - linkType: hard - -"debug@npm:^3.2.7": - version: 3.2.7 - resolution: "debug@npm:3.2.7" - dependencies: - ms: ^2.1.1 - checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c - languageName: node - linkType: hard - "decimal.js@npm:^10.2.1": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -1939,7 +1598,7 @@ __metadata: languageName: node linkType: hard -"deep-is@npm:^0.1.3, deep-is@npm:~0.1.3": +"deep-is@npm:~0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" checksum: edb65dd0d7d1b9c40b2f50219aef30e116cedd6fc79290e740972c132c09106d2e80aa0bc8826673dd5a00222d4179c84b36a790eef63a4c4bca75a37ef90804 @@ -1953,16 +1612,6 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4": - version: 1.1.4 - resolution: "define-properties@npm:1.1.4" - dependencies: - has-property-descriptors: ^1.0.0 - object-keys: ^1.1.1 - checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b - languageName: node - linkType: hard - "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -2005,33 +1654,6 @@ __metadata: languageName: node linkType: hard -"dir-glob@npm:^3.0.1": - version: 3.0.1 - resolution: "dir-glob@npm:3.0.1" - dependencies: - path-type: ^4.0.0 - checksum: fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 - languageName: node - linkType: hard - -"doctrine@npm:^2.1.0": - version: 2.1.0 - resolution: "doctrine@npm:2.1.0" - dependencies: - esutils: ^2.0.2 - checksum: a45e277f7feaed309fe658ace1ff286c6e2002ac515af0aaf37145b8baa96e49899638c7cd47dccf84c3d32abfc113246625b3ac8f552d1046072adee13b0dc8 - languageName: node - linkType: hard - -"doctrine@npm:^3.0.0": - version: 3.0.0 - resolution: "doctrine@npm:3.0.0" - dependencies: - esutils: ^2.0.2 - checksum: fd7673ca77fe26cd5cba38d816bc72d641f500f1f9b25b83e8ce28827fe2da7ad583a8da26ab6af85f834138cf8dae9f69b0cd6ab925f52ddab1754db44d99ce - languageName: node - linkType: hard - "domexception@npm:^2.0.1": version: 2.0.1 resolution: "domexception@npm:2.0.1" @@ -2071,15 +1693,6 @@ __metadata: languageName: node linkType: hard -"enquirer@npm:^2.3.6": - version: 2.3.6 - resolution: "enquirer@npm:2.3.6" - dependencies: - ansi-colors: ^4.1.1 - checksum: 1c0911e14a6f8d26721c91e01db06092a5f7675159f0261d69c403396a385afd13dd76825e7678f66daffa930cfaa8d45f506fb35f818a2788463d022af1b884 - languageName: node - linkType: hard - "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -2103,59 +1716,6 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4": - version: 1.20.5 - resolution: "es-abstract@npm:1.20.5" - dependencies: - call-bind: ^1.0.2 - es-to-primitive: ^1.2.1 - function-bind: ^1.1.1 - function.prototype.name: ^1.1.5 - get-intrinsic: ^1.1.3 - get-symbol-description: ^1.0.0 - gopd: ^1.0.1 - has: ^1.0.3 - has-property-descriptors: ^1.0.0 - has-symbols: ^1.0.3 - internal-slot: ^1.0.3 - is-callable: ^1.2.7 - is-negative-zero: ^2.0.2 - is-regex: ^1.1.4 - is-shared-array-buffer: ^1.0.2 - is-string: ^1.0.7 - is-weakref: ^1.0.2 - object-inspect: ^1.12.2 - object-keys: ^1.1.1 - object.assign: ^4.1.4 - regexp.prototype.flags: ^1.4.3 - safe-regex-test: ^1.0.0 - string.prototype.trimend: ^1.0.6 - string.prototype.trimstart: ^1.0.6 - unbox-primitive: ^1.0.2 - checksum: 00564779ddaf7fb977ab5aa2b8ea2cbd4fa2335ad5368f788bd0bb094c86bc1790335dd9c3e30374bb0af2fa54c724fb4e0c73659dcfe8e427355a56f2b65946 - languageName: node - linkType: hard - -"es-shim-unscopables@npm:^1.0.0": - version: 1.0.0 - resolution: "es-shim-unscopables@npm:1.0.0" - dependencies: - has: ^1.0.3 - checksum: 83e95cadbb6ee44d3644dfad60dcad7929edbc42c85e66c3e99aefd68a3a5c5665f2686885cddb47dfeabfd77bd5ea5a7060f2092a955a729bbd8834f0d86fa1 - languageName: node - linkType: hard - -"es-to-primitive@npm:^1.2.1": - version: 1.2.1 - resolution: "es-to-primitive@npm:1.2.1" - dependencies: - is-callable: ^1.1.4 - is-date-object: ^1.0.1 - is-symbol: ^1.0.2 - checksum: 4ead6671a2c1402619bdd77f3503991232ca15e17e46222b0a41a5d81aebc8740a77822f5b3c965008e631153e9ef0580540007744521e72de8e33599fca2eed - languageName: node - linkType: hard - "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -2177,13 +1737,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^4.0.0": - version: 4.0.0 - resolution: "escape-string-regexp@npm:4.0.0" - checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 - languageName: node - linkType: hard - "escodegen@npm:^2.0.0": version: 2.0.0 resolution: "escodegen@npm:2.0.0" @@ -2203,295 +1756,33 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:^8.3.0": - version: 8.5.0 - resolution: "eslint-config-prettier@npm:8.5.0" - peerDependencies: - eslint: ">=7.0.0" +"esprima@npm:^4.0.0, esprima@npm:^4.0.1": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" bin: - eslint-config-prettier: bin/cli.js - checksum: 0d0f5c32e7a0ad91249467ce71ca92394ccd343178277d318baf32063b79ea90216f4c81d1065d60f96366fdc60f151d4d68ae7811a58bd37228b84c2083f893 - languageName: node - linkType: hard - -"eslint-import-resolver-node@npm:^0.3.6": - version: 0.3.6 - resolution: "eslint-import-resolver-node@npm:0.3.6" - dependencies: - debug: ^3.2.7 - resolve: ^1.20.0 - checksum: 6266733af1e112970e855a5bcc2d2058fb5ae16ad2a6d400705a86b29552b36131ffc5581b744c23d550de844206fb55e9193691619ee4dbf225c4bde526b1c8 + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: b45bc805a613dbea2835278c306b91aff6173c8d034223fa81498c77dcbce3b2931bf6006db816f62eacd9fd4ea975dfd85a5b7f3c6402cfd050d4ca3c13a628 languageName: node linkType: hard -"eslint-module-utils@npm:^2.7.3": - version: 2.7.4 - resolution: "eslint-module-utils@npm:2.7.4" - dependencies: - debug: ^3.2.7 - peerDependenciesMeta: - eslint: - optional: true - checksum: 5da13645daff145a5c922896b258f8bba560722c3767254e458d894ff5fbb505d6dfd945bffa932a5b0ae06714da2379bd41011c4c20d2d59cc83e23895360f7 +"estraverse@npm:^5.2.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 072780882dc8416ad144f8fe199628d2b3e7bbc9989d9ed43795d2c90309a2047e6bc5979d7e2322a341163d22cfad9e21f4110597fe487519697389497e4e2b languageName: node linkType: hard -"eslint-plugin-es@npm:^3.0.0": - version: 3.0.1 - resolution: "eslint-plugin-es@npm:3.0.1" - dependencies: - eslint-utils: ^2.0.0 - regexpp: ^3.0.0 - peerDependencies: - eslint: ">=4.19.1" - checksum: e57592c52301ee8ddc296ae44216df007f3a870bcb3be8d1fbdb909a1d3a3efe3fa3785de02066f9eba1d6466b722d3eb3cc3f8b75b3cf6a1cbded31ac6298e4 +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 languageName: node linkType: hard -"eslint-plugin-import@npm:^2.25.3": - version: 2.26.0 - resolution: "eslint-plugin-import@npm:2.26.0" - dependencies: - array-includes: ^3.1.4 - array.prototype.flat: ^1.2.5 - debug: ^2.6.9 - doctrine: ^2.1.0 - eslint-import-resolver-node: ^0.3.6 - eslint-module-utils: ^2.7.3 - has: ^1.0.3 - is-core-module: ^2.8.1 - is-glob: ^4.0.3 - minimatch: ^3.1.2 - object.values: ^1.1.5 - resolve: ^1.22.0 - tsconfig-paths: ^3.14.1 - peerDependencies: - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: 0bf77ad80339554481eafa2b1967449e1f816b94c7a6f9614ce33fb4083c4e6c050f10d241dd50b4975d47922880a34de1e42ea9d8e6fd663ebb768baa67e655 - languageName: node - linkType: hard - -"eslint-plugin-jest@npm:^25.2.4": - version: 25.7.0 - resolution: "eslint-plugin-jest@npm:25.7.0" - dependencies: - "@typescript-eslint/experimental-utils": ^5.0.0 - peerDependencies: - "@typescript-eslint/eslint-plugin": ^4.0.0 || ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - "@typescript-eslint/eslint-plugin": - optional: true - jest: - optional: true - checksum: fc6da96131f4cbf33d15ef911ec8e600ccd71deb97d73c0ca340427cef7b01ff41a797e2e7d1e351abf97321a46ed0c0acff5ee8eeedac94961dd6dad1f718a9 - languageName: node - linkType: hard - -"eslint-plugin-node@npm:^11.1.0": - version: 11.1.0 - resolution: "eslint-plugin-node@npm:11.1.0" - dependencies: - eslint-plugin-es: ^3.0.0 - eslint-utils: ^2.0.0 - ignore: ^5.1.1 - minimatch: ^3.0.4 - resolve: ^1.10.1 - semver: ^6.1.0 - peerDependencies: - eslint: ">=5.16.0" - checksum: 5804c4f8a6e721f183ef31d46fbe3b4e1265832f352810060e0502aeac7de034df83352fc88643b19641bb2163f2587f1bd4119aff0fd21e8d98c57c450e013b - languageName: node - linkType: hard - -"eslint-plugin-prettier@npm:^4.0.0": - version: 4.2.1 - resolution: "eslint-plugin-prettier@npm:4.2.1" - dependencies: - prettier-linter-helpers: ^1.0.0 - peerDependencies: - eslint: ">=7.28.0" - prettier: ">=2.0.0" - peerDependenciesMeta: - eslint-config-prettier: - optional: true - checksum: b9e839d2334ad8ec7a5589c5cb0f219bded260839a857d7a486997f9870e95106aa59b8756ff3f37202085ebab658de382b0267cae44c3a7f0eb0bcc03a4f6d6 - languageName: node - linkType: hard - -"eslint-scope@npm:^5.1.1": - version: 5.1.1 - resolution: "eslint-scope@npm:5.1.1" - dependencies: - esrecurse: ^4.3.0 - estraverse: ^4.1.1 - checksum: 47e4b6a3f0cc29c7feedee6c67b225a2da7e155802c6ea13bbef4ac6b9e10c66cd2dcb987867ef176292bf4e64eccc680a49e35e9e9c669f4a02bac17e86abdb - languageName: node - linkType: hard - -"eslint-scope@npm:^7.1.1": - version: 7.1.1 - resolution: "eslint-scope@npm:7.1.1" - dependencies: - esrecurse: ^4.3.0 - estraverse: ^5.2.0 - checksum: 9f6e974ab2db641ca8ab13508c405b7b859e72afe9f254e8131ff154d2f40c99ad4545ce326fd9fde3212ff29707102562a4834f1c48617b35d98c71a97fbf3e - languageName: node - linkType: hard - -"eslint-utils@npm:^2.0.0": - version: 2.1.0 - resolution: "eslint-utils@npm:2.1.0" - dependencies: - eslint-visitor-keys: ^1.1.0 - checksum: 27500938f348da42100d9e6ad03ae29b3de19ba757ae1a7f4a087bdcf83ac60949bbb54286492ca61fac1f5f3ac8692dd21537ce6214240bf95ad0122f24d71d - languageName: node - linkType: hard - -"eslint-utils@npm:^3.0.0": - version: 3.0.0 - resolution: "eslint-utils@npm:3.0.0" - dependencies: - eslint-visitor-keys: ^2.0.0 - peerDependencies: - eslint: ">=5" - checksum: 0668fe02f5adab2e5a367eee5089f4c39033af20499df88fe4e6aba2015c20720404d8c3d6349b6f716b08fdf91b9da4e5d5481f265049278099c4c836ccb619 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^1.1.0": - version: 1.3.0 - resolution: "eslint-visitor-keys@npm:1.3.0" - checksum: 37a19b712f42f4c9027e8ba98c2b06031c17e0c0a4c696cd429bd9ee04eb43889c446f2cd545e1ff51bef9593fcec94ecd2c2ef89129fcbbf3adadbef520376a - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^2.0.0": - version: 2.1.0 - resolution: "eslint-visitor-keys@npm:2.1.0" - checksum: e3081d7dd2611a35f0388bbdc2f5da60b3a3c5b8b6e928daffff7391146b434d691577aa95064c8b7faad0b8a680266bcda0a42439c18c717b80e6718d7e267d - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.3.0": - version: 3.3.0 - resolution: "eslint-visitor-keys@npm:3.3.0" - checksum: d59e68a7c5a6d0146526b0eec16ce87fbf97fe46b8281e0d41384224375c4e52f5ffb9e16d48f4ea50785cde93f766b0c898e31ab89978d88b0e1720fbfb7808 - languageName: node - linkType: hard - -"eslint@npm:^8.1.0": - version: 8.30.0 - resolution: "eslint@npm:8.30.0" - dependencies: - "@eslint/eslintrc": ^1.4.0 - "@humanwhocodes/config-array": ^0.11.8 - "@humanwhocodes/module-importer": ^1.0.1 - "@nodelib/fs.walk": ^1.2.8 - ajv: ^6.10.0 - chalk: ^4.0.0 - cross-spawn: ^7.0.2 - debug: ^4.3.2 - doctrine: ^3.0.0 - escape-string-regexp: ^4.0.0 - eslint-scope: ^7.1.1 - eslint-utils: ^3.0.0 - eslint-visitor-keys: ^3.3.0 - espree: ^9.4.0 - esquery: ^1.4.0 - esutils: ^2.0.2 - fast-deep-equal: ^3.1.3 - file-entry-cache: ^6.0.1 - find-up: ^5.0.0 - glob-parent: ^6.0.2 - globals: ^13.19.0 - grapheme-splitter: ^1.0.4 - ignore: ^5.2.0 - import-fresh: ^3.0.0 - imurmurhash: ^0.1.4 - is-glob: ^4.0.0 - is-path-inside: ^3.0.3 - js-sdsl: ^4.1.4 - js-yaml: ^4.1.0 - json-stable-stringify-without-jsonify: ^1.0.1 - levn: ^0.4.1 - lodash.merge: ^4.6.2 - minimatch: ^3.1.2 - natural-compare: ^1.4.0 - optionator: ^0.9.1 - regexpp: ^3.2.0 - strip-ansi: ^6.0.1 - strip-json-comments: ^3.1.0 - text-table: ^0.2.0 - bin: - eslint: bin/eslint.js - checksum: b7525bb465b342665c3b8bab7e114d514ef1bc4e79f211c919863f9c71767e7412ec82383a22614a92d159783f91101018817000f7c61ce69a5e7015280cafaf - languageName: node - linkType: hard - -"espree@npm:^9.4.0": - version: 9.4.1 - resolution: "espree@npm:9.4.1" - dependencies: - acorn: ^8.8.0 - acorn-jsx: ^5.3.2 - eslint-visitor-keys: ^3.3.0 - checksum: 4d266b0cf81c7dfe69e542c7df0f246e78d29f5b04dda36e514eb4c7af117ee6cfbd3280e560571ed82ff6c9c3f0003c05b82583fc7a94006db7497c4fe4270e - languageName: node - linkType: hard - -"esprima@npm:^4.0.0, esprima@npm:^4.0.1": - version: 4.0.1 - resolution: "esprima@npm:4.0.1" - bin: - esparse: ./bin/esparse.js - esvalidate: ./bin/esvalidate.js - checksum: b45bc805a613dbea2835278c306b91aff6173c8d034223fa81498c77dcbce3b2931bf6006db816f62eacd9fd4ea975dfd85a5b7f3c6402cfd050d4ca3c13a628 - languageName: node - linkType: hard - -"esquery@npm:^1.4.0": - version: 1.4.0 - resolution: "esquery@npm:1.4.0" - dependencies: - estraverse: ^5.1.0 - checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210 - languageName: node - linkType: hard - -"esrecurse@npm:^4.3.0": - version: 4.3.0 - resolution: "esrecurse@npm:4.3.0" - dependencies: - estraverse: ^5.2.0 - checksum: ebc17b1a33c51cef46fdc28b958994b1dc43cd2e86237515cbc3b4e5d2be6a811b2315d0a1a4d9d340b6d2308b15322f5c8291059521cc5f4802f65e7ec32837 - languageName: node - linkType: hard - -"estraverse@npm:^4.1.1": - version: 4.3.0 - resolution: "estraverse@npm:4.3.0" - checksum: a6299491f9940bb246124a8d44b7b7a413a8336f5436f9837aaa9330209bd9ee8af7e91a654a3545aee9c54b3308e78ee360cef1d777d37cfef77d2fa33b5827 - languageName: node - linkType: hard - -"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": - version: 5.3.0 - resolution: "estraverse@npm:5.3.0" - checksum: 072780882dc8416ad144f8fe199628d2b3e7bbc9989d9ed43795d2c90309a2047e6bc5979d7e2322a341163d22cfad9e21f4110597fe487519697389497e4e2b - languageName: node - linkType: hard - -"esutils@npm:^2.0.2": - version: 2.0.3 - resolution: "esutils@npm:2.0.3" - checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 - languageName: node - linkType: hard - -"execa@npm:^5.0.0, execa@npm:^5.1.1": - version: 5.1.1 - resolution: "execa@npm:5.1.1" +"execa@npm:^5.0.0": + version: 5.1.1 + resolution: "execa@npm:5.1.1" dependencies: cross-spawn: ^7.0.3 get-stream: ^6.0.0 @@ -2525,33 +1816,6 @@ __metadata: languageName: node linkType: hard -"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": - version: 3.1.3 - resolution: "fast-deep-equal@npm:3.1.3" - checksum: e21a9d8d84f53493b6aa15efc9cfd53dd5b714a1f23f67fb5dc8f574af80df889b3bce25dc081887c6d25457cce704e636395333abad896ccdec03abaf1f3f9d - languageName: node - linkType: hard - -"fast-diff@npm:^1.1.2": - version: 1.2.0 - resolution: "fast-diff@npm:1.2.0" - checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae - languageName: node - linkType: hard - -"fast-glob@npm:^3.2.9": - version: 3.2.12 - resolution: "fast-glob@npm:3.2.12" - dependencies: - "@nodelib/fs.stat": ^2.0.2 - "@nodelib/fs.walk": ^1.2.3 - glob-parent: ^5.1.2 - merge2: ^1.3.0 - micromatch: ^4.0.4 - checksum: 0b1990f6ce831c7e28c4d505edcdaad8e27e88ab9fa65eedadb730438cfc7cde4910d6c975d6b7b8dc8a73da4773702ebcfcd6e3518e73938bb1383badfe01c2 - languageName: node - linkType: hard - "fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -2559,22 +1823,13 @@ __metadata: languageName: node linkType: hard -"fast-levenshtein@npm:^2.0.6, fast-levenshtein@npm:~2.0.6": +"fast-levenshtein@npm:~2.0.6": version: 2.0.6 resolution: "fast-levenshtein@npm:2.0.6" checksum: 92cfec0a8dfafd9c7a15fba8f2cc29cd0b62b85f056d99ce448bbcd9f708e18ab2764bda4dd5158364f4145a7c72788538994f0d1787b956ef0d1062b0f7c24c languageName: node linkType: hard -"fastq@npm:^1.6.0": - version: 1.14.0 - resolution: "fastq@npm:1.14.0" - dependencies: - reusify: ^1.0.4 - checksum: da2c05ec1446ef77b8ba2b76619c90d483404f5087e71e77469fbee797280a1f4ef26a63be15b2377198bc20d09fdf25c7d6e1e492a1e568a29dfdd9bcb7538c - languageName: node - linkType: hard - "fb-watchman@npm:^2.0.0": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" @@ -2584,15 +1839,6 @@ __metadata: languageName: node linkType: hard -"file-entry-cache@npm:^6.0.1": - version: 6.0.1 - resolution: "file-entry-cache@npm:6.0.1" - dependencies: - flat-cache: ^3.0.4 - checksum: f49701feaa6314c8127c3c2f6173cfefff17612f5ed2daaafc6da13b5c91fd43e3b2a58fd0d63f9f94478a501b167615931e7200e31485e320f74a33885a9c74 - languageName: node - linkType: hard - "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -2612,33 +1858,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^5.0.0": - version: 5.0.0 - resolution: "find-up@npm:5.0.0" - dependencies: - locate-path: ^6.0.0 - path-exists: ^4.0.0 - checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 - languageName: node - linkType: hard - -"flat-cache@npm:^3.0.4": - version: 3.0.4 - resolution: "flat-cache@npm:3.0.4" - dependencies: - flatted: ^3.1.0 - rimraf: ^3.0.2 - checksum: 4fdd10ecbcbf7d520f9040dd1340eb5dfe951e6f0ecf2252edeec03ee68d989ec8b9a20f4434270e71bcfd57800dc09b3344fca3966b2eb8f613072c7d9a2365 - languageName: node - linkType: hard - -"flatted@npm:^3.1.0": - version: 3.2.7 - resolution: "flatted@npm:3.2.7" - checksum: 427633049d55bdb80201c68f7eb1cbd533e03eac541f97d3aecab8c5526f12a20ccecaeede08b57503e772c769e7f8680b37e8d482d1e5f8d7e2194687f9ea35 - languageName: node - linkType: hard - "form-data@npm:^3.0.0": version: 3.0.1 resolution: "form-data@npm:3.0.1" @@ -2692,25 +1911,6 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.5": - version: 1.1.5 - resolution: "function.prototype.name@npm:1.1.5" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.0 - functions-have-names: ^1.2.2 - checksum: acd21d733a9b649c2c442f067567743214af5fa248dbeee69d8278ce7df3329ea5abac572be9f7470b4ec1cd4d8f1040e3c5caccf98ebf2bf861a0deab735c27 - languageName: node - linkType: hard - -"functions-have-names@npm:^1.2.2": - version: 1.2.3 - resolution: "functions-have-names@npm:1.2.3" - checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 - languageName: node - linkType: hard - "gauge@npm:^4.0.3": version: 4.0.4 resolution: "gauge@npm:4.0.4" @@ -2741,24 +1941,6 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3": - version: 1.1.3 - resolution: "get-intrinsic@npm:1.1.3" - dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 - has-symbols: ^1.0.3 - checksum: 152d79e87251d536cf880ba75cfc3d6c6c50e12b3a64e1ea960e73a3752b47c69f46034456eae1b0894359ce3bc64c55c186f2811f8a788b75b638b06fab228a - languageName: node - linkType: hard - -"get-own-enumerable-property-symbols@npm:^3.0.0": - version: 3.0.2 - resolution: "get-own-enumerable-property-symbols@npm:3.0.2" - checksum: 8f0331f14159f939830884799f937343c8c0a2c330506094bc12cbee3665d88337fe97a4ea35c002cc2bdba0f5d9975ad7ec3abb925015cdf2a93e76d4759ede - languageName: node - linkType: hard - "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -2773,34 +1955,6 @@ __metadata: languageName: node linkType: hard -"get-symbol-description@npm:^1.0.0": - version: 1.0.0 - resolution: "get-symbol-description@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.1 - checksum: 9ceff8fe968f9270a37a1f73bf3f1f7bda69ca80f4f80850670e0e7b9444ff99323f7ac52f96567f8b5f5fbe7ac717a0d81d3407c7313e82810c6199446a5247 - languageName: node - linkType: hard - -"glob-parent@npm:^5.1.2": - version: 5.1.2 - resolution: "glob-parent@npm:5.1.2" - dependencies: - is-glob: ^4.0.1 - checksum: f4f2bfe2425296e8a47e36864e4f42be38a996db40420fe434565e4480e3322f18eb37589617a98640c5dc8fdec1a387007ee18dbb1f3f5553409c34d17f425e - languageName: node - linkType: hard - -"glob-parent@npm:^6.0.2": - version: 6.0.2 - resolution: "glob-parent@npm:6.0.2" - dependencies: - is-glob: ^4.0.3 - checksum: c13ee97978bef4f55106b71e66428eb1512e71a7466ba49025fc2aec59a5bfb0954d5abd58fc5ee6c9b076eef4e1f6d3375c2e964b88466ca390da4419a786a8 - languageName: node - linkType: hard - "glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -2815,7 +1969,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1, glob@npm:^8.0.3": +"glob@npm:^8.0.1": version: 8.0.3 resolution: "glob@npm:8.0.3" dependencies: @@ -2835,38 +1989,6 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.19.0": - version: 13.19.0 - resolution: "globals@npm:13.19.0" - dependencies: - type-fest: ^0.20.2 - checksum: a000dbd00bcf28f0941d8a29c3522b1c3b8e4bfe4e60e262c477a550c3cbbe8dbe2925a6905f037acd40f9a93c039242e1f7079c76b0fd184bc41dcc3b5c8e2e - languageName: node - linkType: hard - -"globby@npm:^11.1.0": - version: 11.1.0 - resolution: "globby@npm:11.1.0" - dependencies: - array-union: ^2.1.0 - dir-glob: ^3.0.1 - fast-glob: ^3.2.9 - ignore: ^5.2.0 - merge2: ^1.4.1 - slash: ^3.0.0 - checksum: b4be8885e0cfa018fc783792942d53926c35c50b3aefd3fdcfb9d22c627639dc26bd2327a40a0b74b074100ce95bb7187bfeae2f236856aa3de183af7a02aea6 - languageName: node - linkType: hard - -"gopd@npm:^1.0.1": - version: 1.0.1 - resolution: "gopd@npm:1.0.1" - dependencies: - get-intrinsic: ^1.1.3 - checksum: a5ccfb8806e0917a94e0b3de2af2ea4979c1da920bc381667c260e00e7cafdbe844e2cb9c5bcfef4e5412e8bf73bab837285bc35c7ba73aaaf0134d4583393a6 - languageName: node - linkType: hard - "graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" @@ -2874,20 +1996,6 @@ __metadata: languageName: node linkType: hard -"grapheme-splitter@npm:^1.0.4": - version: 1.0.4 - resolution: "grapheme-splitter@npm:1.0.4" - checksum: 0c22ec54dee1b05cd480f78cf14f732cb5b108edc073572c4ec205df4cd63f30f8db8025afc5debc8835a8ddeacf648a1c7992fe3dcd6ad38f9a476d84906620 - languageName: node - linkType: hard - -"has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": - version: 1.0.2 - resolution: "has-bigints@npm:1.0.2" - checksum: 390e31e7be7e5c6fe68b81babb73dfc35d413604d7ee5f56da101417027a4b4ce6a27e46eff97ad040c835b5d228676eae99a9b5c3bc0e23c8e81a49241ff45b - languageName: node - linkType: hard - "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -2902,31 +2010,6 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" - dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": - version: 1.0.3 - resolution: "has-symbols@npm:1.0.3" - checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 - languageName: node - linkType: hard - -"has-tostringtag@npm:^1.0.0": - version: 1.0.0 - resolution: "has-tostringtag@npm:1.0.0" - dependencies: - has-symbols: ^1.0.2 - checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c - languageName: node - linkType: hard - "has-unicode@npm:^2.0.1": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" @@ -3014,15 +2097,6 @@ __metadata: languageName: node linkType: hard -"husky@npm:^7.0.4": - version: 7.0.4 - resolution: "husky@npm:7.0.4" - bin: - husky: lib/bin.js - checksum: c6ec4af63da2c9522da8674a20ad9b48362cc92704896cc8a58c6a2a39d797feb2b806f93fbd83a6d653fbdceb2c3b6e0b602c6b2e8565206ffc2882ef7db9e9 - languageName: node - linkType: hard - "iconv-lite@npm:0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -3041,23 +2115,6 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.1.1, ignore@npm:^5.2.0": - version: 5.2.4 - resolution: "ignore@npm:5.2.4" - checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef - languageName: node - linkType: hard - -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": - version: 3.3.0 - resolution: "import-fresh@npm:3.3.0" - dependencies: - parent-module: ^1.0.0 - resolve-from: ^4.0.0 - checksum: 2cacfad06e652b1edc50be650f7ec3be08c5e5a6f6d12d035c440a42a8cc028e60a5b99ca08a77ab4d6b1346da7d971915828f33cdab730d3d42f08242d09baa - languageName: node - linkType: hard - "import-local@npm:^3.0.2": version: 3.1.0 resolution: "import-local@npm:3.1.0" @@ -3108,17 +2165,6 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.3": - version: 1.0.4 - resolution: "internal-slot@npm:1.0.4" - dependencies: - get-intrinsic: ^1.1.3 - has: ^1.0.3 - side-channel: ^1.0.4 - checksum: 8974588d06bab4f675573a3b52975370facf6486df51bc0567a982c7024fa29495f10b76c0d4dc742dd951d1b72024fdc1e31bb0bedf1678dc7aacacaf5a4f73 - languageName: node - linkType: hard - "ip@npm:^2.0.0": version: 2.0.0 resolution: "ip@npm:2.0.0" @@ -3133,33 +2179,7 @@ __metadata: languageName: node linkType: hard -"is-bigint@npm:^1.0.1": - version: 1.0.4 - resolution: "is-bigint@npm:1.0.4" - dependencies: - has-bigints: ^1.0.1 - checksum: c56edfe09b1154f8668e53ebe8252b6f185ee852a50f9b41e8d921cb2bed425652049fbe438723f6cb48a63ca1aa051e948e7e401e093477c99c84eba244f666 - languageName: node - linkType: hard - -"is-boolean-object@npm:^1.1.0": - version: 1.1.2 - resolution: "is-boolean-object@npm:1.1.2" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: c03b23dbaacadc18940defb12c1c0e3aaece7553ef58b162a0f6bba0c2a7e1551b59f365b91e00d2dbac0522392d576ef322628cb1d036a0fe51eb466db67222 - languageName: node - linkType: hard - -"is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": - version: 1.2.7 - resolution: "is-callable@npm:1.2.7" - checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac - languageName: node - linkType: hard - -"is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": +"is-core-module@npm:^2.9.0": version: 2.11.0 resolution: "is-core-module@npm:2.11.0" dependencies: @@ -3168,22 +2188,6 @@ __metadata: languageName: node linkType: hard -"is-date-object@npm:^1.0.1": - version: 1.0.5 - resolution: "is-date-object@npm:1.0.5" - dependencies: - has-tostringtag: ^1.0.0 - checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 - languageName: node - linkType: hard - "is-fullwidth-code-point@npm:^3.0.0": version: 3.0.0 resolution: "is-fullwidth-code-point@npm:3.0.0" @@ -3198,15 +2202,6 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" - dependencies: - is-extglob: ^2.1.1 - checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 - languageName: node - linkType: hard - "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -3214,22 +2209,6 @@ __metadata: languageName: node linkType: hard -"is-negative-zero@npm:^2.0.2": - version: 2.0.2 - resolution: "is-negative-zero@npm:2.0.2" - checksum: f3232194c47a549da60c3d509c9a09be442507616b69454716692e37ae9f37c4dea264fb208ad0c9f3efd15a796a46b79df07c7e53c6227c32170608b809149a - languageName: node - linkType: hard - -"is-number-object@npm:^1.0.4": - version: 1.0.7 - resolution: "is-number-object@npm:1.0.7" - dependencies: - has-tostringtag: ^1.0.0 - checksum: d1e8d01bb0a7134c74649c4e62da0c6118a0bfc6771ea3c560914d52a627873e6920dd0fd0ebc0e12ad2ff4687eac4c308f7e80320b973b2c8a2c8f97a7524f7 - languageName: node - linkType: hard - "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -3237,20 +2216,6 @@ __metadata: languageName: node linkType: hard -"is-obj@npm:^1.0.1": - version: 1.0.1 - resolution: "is-obj@npm:1.0.1" - checksum: 3ccf0efdea12951e0b9c784e2b00e77e87b2f8bd30b42a498548a8afcc11b3287342a2030c308e473e93a7a19c9ea7854c99a8832a476591c727df2a9c79796c - languageName: node - linkType: hard - -"is-path-inside@npm:^3.0.3": - version: 3.0.3 - resolution: "is-path-inside@npm:3.0.3" - checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 - languageName: node - linkType: hard - "is-potential-custom-element-name@npm:^1.0.1": version: 1.0.1 resolution: "is-potential-custom-element-name@npm:1.0.1" @@ -3258,32 +2223,6 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.1.4": - version: 1.1.4 - resolution: "is-regex@npm:1.1.4" - dependencies: - call-bind: ^1.0.2 - has-tostringtag: ^1.0.0 - checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652 - languageName: node - linkType: hard - -"is-regexp@npm:^1.0.0": - version: 1.0.0 - resolution: "is-regexp@npm:1.0.0" - checksum: be692828e24cba479ec33644326fa98959ec68ba77965e0291088c1a741feaea4919d79f8031708f85fd25e39de002b4520622b55460660b9c369e6f7187faef - languageName: node - linkType: hard - -"is-shared-array-buffer@npm:^1.0.2": - version: 1.0.2 - resolution: "is-shared-array-buffer@npm:1.0.2" - dependencies: - call-bind: ^1.0.2 - checksum: 9508929cf14fdc1afc9d61d723c6e8d34f5e117f0bffda4d97e7a5d88c3a8681f633a74f8e3ad1fe92d5113f9b921dc5ca44356492079612f9a247efbce7032a - languageName: node - linkType: hard - "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -3291,24 +2230,6 @@ __metadata: languageName: node linkType: hard -"is-string@npm:^1.0.5, is-string@npm:^1.0.7": - version: 1.0.7 - resolution: "is-string@npm:1.0.7" - dependencies: - has-tostringtag: ^1.0.0 - checksum: 323b3d04622f78d45077cf89aab783b2f49d24dc641aa89b5ad1a72114cfeff2585efc8c12ef42466dff32bde93d839ad321b26884cf75e5a7892a938b089989 - languageName: node - linkType: hard - -"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": - version: 1.0.4 - resolution: "is-symbol@npm:1.0.4" - dependencies: - has-symbols: ^1.0.2 - checksum: 92805812ef590738d9de49d677cd17dfd486794773fb6fa0032d16452af46e9b91bb43ffe82c983570f015b37136f4b53b28b8523bfb10b0ece7a66c31a54510 - languageName: node - linkType: hard - "is-typedarray@npm:^1.0.0": version: 1.0.0 resolution: "is-typedarray@npm:1.0.0" @@ -3316,15 +2237,6 @@ __metadata: languageName: node linkType: hard -"is-weakref@npm:^1.0.2": - version: 1.0.2 - resolution: "is-weakref@npm:1.0.2" - dependencies: - call-bind: ^1.0.2 - checksum: 95bd9a57cdcb58c63b1c401c60a474b0f45b94719c30f548c891860f051bc2231575c290a6b420c6bc6e7ed99459d424c652bd5bf9a1d5259505dc35b4bf83de - languageName: node - linkType: hard - "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -3873,13 +2785,6 @@ __metadata: languageName: node linkType: hard -"js-sdsl@npm:^4.1.4": - version: 4.2.0 - resolution: "js-sdsl@npm:4.2.0" - checksum: 2cd0885f7212afb355929d72ca105cb37de7e95ad6031e6a32619eaefa46735a7d0fb682641a0ba666e1519cb138fe76abc1eea8a34e224140c9d94c995171f1 - languageName: node - linkType: hard - "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -3899,17 +2804,6 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.0": - version: 4.1.0 - resolution: "js-yaml@npm:4.1.0" - dependencies: - argparse: ^2.0.1 - bin: - js-yaml: bin/js-yaml.js - checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a - languageName: node - linkType: hard - "jsdom@npm:^16.6.0": version: 16.7.0 resolution: "jsdom@npm:16.7.0" @@ -3966,20 +2860,6 @@ __metadata: languageName: node linkType: hard -"json-schema-traverse@npm:^0.4.1": - version: 0.4.1 - resolution: "json-schema-traverse@npm:0.4.1" - checksum: 7486074d3ba247769fda17d5181b345c9fb7d12e0da98b22d1d71a5db9698d8b4bd900a3ec1a4ffdd60846fc2556274a5c894d0c48795f14cb03aeae7b55260b - languageName: node - linkType: hard - -"json-stable-stringify-without-jsonify@npm:^1.0.1": - version: 1.0.1 - resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" - checksum: cff44156ddce9c67c44386ad5cddf91925fe06b1d217f2da9c4910d01f358c6e3989c4d5a02683c7a5667f9727ff05831f7aa8ae66c8ff691c556f0884d49215 - languageName: node - linkType: hard - "json5@npm:2.x, json5@npm:^2.2.1": version: 2.2.2 resolution: "json5@npm:2.2.2" @@ -3989,17 +2869,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.1": - version: 1.0.1 - resolution: "json5@npm:1.0.1" - dependencies: - minimist: ^1.2.0 - bin: - json5: lib/cli.js - checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 - languageName: node - linkType: hard - "jsonc-parser@npm:^3.0.0": version: 3.2.0 resolution: "jsonc-parser@npm:3.2.0" @@ -4021,16 +2890,6 @@ __metadata: languageName: node linkType: hard -"levn@npm:^0.4.1": - version: 0.4.1 - resolution: "levn@npm:0.4.1" - dependencies: - prelude-ls: ^1.2.1 - type-check: ~0.4.0 - checksum: 12c5021c859bd0f5248561bf139121f0358285ec545ebf48bb3d346820d5c61a4309535c7f387ed7d84361cf821e124ce346c6b7cef8ee09a67c1473b46d0fc4 - languageName: node - linkType: hard - "levn@npm:~0.3.0": version: 0.3.0 resolution: "levn@npm:0.3.0" @@ -4041,55 +2900,10 @@ __metadata: languageName: node linkType: hard -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 0c37f9f7fa212b38912b7145e1cd16a5f3cd34d782441c3e6ca653485d326f58b3caccda66efce1c5812bde4961bbde3374fae4b0d11bf1226152337f3894aa5 - languageName: node - linkType: hard - -"lint-staged@npm:^11.2.6": - version: 11.2.6 - resolution: "lint-staged@npm:11.2.6" - dependencies: - cli-truncate: 2.1.0 - colorette: ^1.4.0 - commander: ^8.2.0 - cosmiconfig: ^7.0.1 - debug: ^4.3.2 - enquirer: ^2.3.6 - execa: ^5.1.1 - listr2: ^3.12.2 - micromatch: ^4.0.4 - normalize-path: ^3.0.0 - please-upgrade-node: ^3.2.0 - string-argv: 0.3.1 - stringify-object: 3.3.0 - supports-color: 8.1.1 - bin: - lint-staged: bin/lint-staged.js - checksum: b9071621db351c553579bd18df3d80fb753f851c4f1f72db0aaf12e713eab25b6d8a044dab2957817de7da02054a63f7725a49c763aee09295133f0d554f4d3f - languageName: node - linkType: hard - -"listr2@npm:^3.12.2": - version: 3.14.0 - resolution: "listr2@npm:3.14.0" - dependencies: - cli-truncate: ^2.1.0 - colorette: ^2.0.16 - log-update: ^4.0.0 - p-map: ^4.0.0 - rfdc: ^1.3.0 - rxjs: ^7.5.1 - through: ^2.3.8 - wrap-ansi: ^7.0.0 - peerDependencies: - enquirer: ">= 2.3.0 < 3" - peerDependenciesMeta: - enquirer: - optional: true - checksum: fdb8b2d6bdf5df9371ebd5082bee46c6d0ca3d1e5f2b11fbb5a127839855d5f3da9d4968fce94f0a5ec67cac2459766abbb1faeef621065ebb1829b11ef9476d +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 0c37f9f7fa212b38912b7145e1cd16a5f3cd34d782441c3e6ca653485d326f58b3caccda66efce1c5812bde4961bbde3374fae4b0d11bf1226152337f3894aa5 languageName: node linkType: hard @@ -4102,15 +2916,6 @@ __metadata: languageName: node linkType: hard -"locate-path@npm:^6.0.0": - version: 6.0.0 - resolution: "locate-path@npm:6.0.0" - dependencies: - p-locate: ^5.0.0 - checksum: 72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a - languageName: node - linkType: hard - "lodash.memoize@npm:4.x": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" @@ -4118,13 +2923,6 @@ __metadata: languageName: node linkType: hard -"lodash.merge@npm:^4.6.2": - version: 4.6.2 - resolution: "lodash.merge@npm:4.6.2" - checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 - languageName: node - linkType: hard - "lodash@npm:^4.17.21, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -4132,18 +2930,6 @@ __metadata: languageName: node linkType: hard -"log-update@npm:^4.0.0": - version: 4.0.0 - resolution: "log-update@npm:4.0.0" - dependencies: - ansi-escapes: ^4.3.0 - cli-cursor: ^3.1.0 - slice-ansi: ^4.0.0 - wrap-ansi: ^6.2.0 - checksum: ae2f85bbabc1906034154fb7d4c4477c79b3e703d22d78adee8b3862fa913942772e7fa11713e3d96fb46de4e3cabefbf5d0a544344f03b58d3c4bff52aa9eb2 - languageName: node - linkType: hard - "lru-cache@npm:^6.0.0": version: 6.0.0 resolution: "lru-cache@npm:6.0.0" @@ -4216,12 +3002,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:^4.0.16": - version: 4.2.4 - resolution: "marked@npm:4.2.4" +"marked@npm:^4.2.4": + version: 4.2.5 + resolution: "marked@npm:4.2.5" bin: marked: bin/marked.js - checksum: 5eb5bfa6ee4cf85712a3ccbe2a549c397e8886f5d18312a02696c7e3817625a6b91a8ad27a6ed43b06ddbdfb812f471b1270517c4b8fb068a6a9e5b4d555a5aa + checksum: dd7da20a3983c66b516463fad5dc8d15dc70e137d20b6dc491e134f671e84bd2ed5f859e2c35f21e56830a122e4356b9e574bcde49b72b7ad6bc121a215a1a98 languageName: node linkType: hard @@ -4232,13 +3018,6 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": - version: 1.4.1 - resolution: "merge2@npm:1.4.1" - checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 - languageName: node - linkType: hard - "micromatch@npm:^4.0.4": version: 4.0.5 resolution: "micromatch@npm:4.0.5" @@ -4272,7 +3051,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -4281,7 +3060,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^5.0.1, minimatch@npm:^5.1.0": +"minimatch@npm:^5.0.1, minimatch@npm:^5.1.1": version: 5.1.2 resolution: "minimatch@npm:5.1.2" dependencies: @@ -4290,13 +3069,6 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.6": - version: 1.2.7 - resolution: "minimist@npm:1.2.7" - checksum: 7346574a1038ca23c32e02252f603801f09384dd1d78b69a943a4e8c2c28730b80e96193882d3d3b22a063445f460e48316b29b8a25addca2d7e5e8f75478bec - languageName: node - linkType: hard - "minipass-collect@npm:^1.0.2": version: 1.0.2 resolution: "minipass-collect@npm:1.0.2" @@ -4385,13 +3157,6 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.0.0": - version: 2.0.0 - resolution: "ms@npm:2.0.0" - checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4 - languageName: node - linkType: hard - "ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" @@ -4399,20 +3164,13 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:^2.0.0": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d languageName: node linkType: hard -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -4469,24 +3227,14 @@ __metadata: "@types/chance": ^1.1.3 "@types/jest": ^27.0.3 "@types/node": ^16.11.9 - "@typescript-eslint/eslint-plugin": ^5.4.0 - "@typescript-eslint/parser": ^5.4.0 chance: ^1.1.8 - eslint: ^8.1.0 - eslint-config-prettier: ^8.3.0 - eslint-plugin-import: ^2.25.3 - eslint-plugin-jest: ^25.2.4 - eslint-plugin-node: ^11.1.0 - eslint-plugin-prettier: ^4.0.0 - husky: ^7.0.4 jest: ^27.3.1 - lint-staged: ^11.2.6 - prettier: ^2.4.1 + rome: ^11.0.0 source-map-support: ^0.5.21 ts-jest: ^27.1.0 ts-node: ^10.4.0 - typedoc: ^0.22.9 - typescript: 4.4.3 + typedoc: ^0.23 + typescript: 4.9 languageName: unknown linkType: soft @@ -4536,43 +3284,6 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": - version: 1.12.2 - resolution: "object-inspect@npm:1.12.2" - checksum: a534fc1b8534284ed71f25ce3a496013b7ea030f3d1b77118f6b7b1713829262be9e6243acbcb3ef8c626e2b64186112cb7f6db74e37b2789b9c789ca23048b2 - languageName: node - linkType: hard - -"object-keys@npm:^1.1.1": - version: 1.1.1 - resolution: "object-keys@npm:1.1.1" - checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a - languageName: node - linkType: hard - -"object.assign@npm:^4.1.4": - version: 4.1.4 - resolution: "object.assign@npm:4.1.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - has-symbols: ^1.0.3 - object-keys: ^1.1.1 - checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 - languageName: node - linkType: hard - -"object.values@npm:^1.1.5": - version: 1.1.6 - resolution: "object.values@npm:1.1.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: f6fff9fd817c24cfd8107f50fb33061d81cd11bacc4e3dbb3852e9ff7692fde4dbce823d4333ea27cd9637ef1b6690df5fbb61f1ed314fa2959598dc3ae23d8e - languageName: node - linkType: hard - "once@npm:^1.3.0": version: 1.4.0 resolution: "once@npm:1.4.0" @@ -4582,7 +3293,7 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^5.1.0, onetime@npm:^5.1.2": +"onetime@npm:^5.1.2": version: 5.1.2 resolution: "onetime@npm:5.1.2" dependencies: @@ -4605,20 +3316,6 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.9.1": - version: 0.9.1 - resolution: "optionator@npm:0.9.1" - dependencies: - deep-is: ^0.1.3 - fast-levenshtein: ^2.0.6 - levn: ^0.4.1 - prelude-ls: ^1.2.1 - type-check: ^0.4.0 - word-wrap: ^1.2.3 - checksum: dbc6fa065604b24ea57d734261914e697bd73b69eff7f18e967e8912aa2a40a19a9f599a507fa805be6c13c24c4eae8c71306c239d517d42d4c041c942f508a0 - languageName: node - linkType: hard - "p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -4628,15 +3325,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: ^0.1.0 - checksum: 7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 - languageName: node - linkType: hard - "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -4646,15 +3334,6 @@ __metadata: languageName: node linkType: hard -"p-locate@npm:^5.0.0": - version: 5.0.0 - resolution: "p-locate@npm:5.0.0" - dependencies: - p-limit: ^3.0.2 - checksum: 1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 - languageName: node - linkType: hard - "p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" @@ -4671,16 +3350,7 @@ __metadata: languageName: node linkType: hard -"parent-module@npm:^1.0.0": - version: 1.0.1 - resolution: "parent-module@npm:1.0.1" - dependencies: - callsites: ^3.0.0 - checksum: 6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff - languageName: node - linkType: hard - -"parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": +"parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" dependencies: @@ -4727,13 +3397,6 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^4.0.0": - version: 4.0.0 - resolution: "path-type@npm:4.0.0" - checksum: 5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 - languageName: node - linkType: hard - "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -4764,22 +3427,6 @@ __metadata: languageName: node linkType: hard -"please-upgrade-node@npm:^3.2.0": - version: 3.2.0 - resolution: "please-upgrade-node@npm:3.2.0" - dependencies: - semver-compare: ^1.0.0 - checksum: d87c41581a2a022fbe25965a97006238cd9b8cbbf49b39f78d262548149a9d30bd2bdf35fec3d810e0001e630cd46ef13c7e19c389dea8de7e64db271a2381bb - languageName: node - linkType: hard - -"prelude-ls@npm:^1.2.1": - version: 1.2.1 - resolution: "prelude-ls@npm:1.2.1" - checksum: cd192ec0d0a8e4c6da3bb80e4f62afe336df3f76271ac6deb0e6a36187133b6073a19e9727a1ff108cd8b9982e4768850d413baa71214dd80c7979617dca827a - languageName: node - linkType: hard - "prelude-ls@npm:~1.1.2": version: 1.1.2 resolution: "prelude-ls@npm:1.1.2" @@ -4787,24 +3434,6 @@ __metadata: languageName: node linkType: hard -"prettier-linter-helpers@npm:^1.0.0": - version: 1.0.0 - resolution: "prettier-linter-helpers@npm:1.0.0" - dependencies: - fast-diff: ^1.1.2 - checksum: 00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392 - languageName: node - linkType: hard - -"prettier@npm:^2.4.1": - version: 2.8.1 - resolution: "prettier@npm:2.8.1" - bin: - prettier: bin-prettier.js - checksum: 4f21a0f1269f76fb36f54e9a8a1ea4c11e27478958bf860661fb4b6d7ac69aac1581f8724fa98ea3585e56d42a2ea317a17ff6e3324f40cb11ff9e20b73785cc - languageName: node - linkType: hard - "pretty-format@npm:^27.0.0, pretty-format@npm:^27.5.1": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" @@ -4850,7 +3479,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0, punycode@npm:^2.1.1": +"punycode@npm:^2.1.1": version: 2.1.1 resolution: "punycode@npm:2.1.1" checksum: 823bf443c6dd14f669984dea25757b37993f67e8d94698996064035edd43bed8a5a17a9f12e439c2b35df1078c6bec05a6c86e336209eb1061e8025c481168e8 @@ -4864,13 +3493,6 @@ __metadata: languageName: node linkType: hard -"queue-microtask@npm:^1.2.2": - version: 1.2.3 - resolution: "queue-microtask@npm:1.2.3" - checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4 - languageName: node - linkType: hard - "ramda@npm:^0.27.1": version: 0.27.2 resolution: "ramda@npm:0.27.2" @@ -4896,24 +3518,6 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.4.3": - version: 1.4.3 - resolution: "regexp.prototype.flags@npm:1.4.3" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - functions-have-names: ^1.2.2 - checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 - languageName: node - linkType: hard - -"regexpp@npm:^3.0.0, regexpp@npm:^3.2.0": - version: 3.2.0 - resolution: "regexpp@npm:3.2.0" - checksum: a78dc5c7158ad9ddcfe01aa9144f46e192ddbfa7b263895a70a5c6c73edd9ce85faf7c0430e59ac38839e1734e275b9c3de5c57ee3ab6edc0e0b1bdebefccef8 - languageName: node - linkType: hard - "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -4937,13 +3541,6 @@ __metadata: languageName: node linkType: hard -"resolve-from@npm:^4.0.0": - version: 4.0.0 - resolution: "resolve-from@npm:4.0.0" - checksum: f4ba0b8494846a5066328ad33ef8ac173801a51739eb4d63408c847da9a2e1c1de1e6cbbf72699211f3d13f8fc1325648b169bd15eb7da35688e30a5fb0e4a7f - languageName: node - linkType: hard - "resolve-from@npm:^5.0.0": version: 5.0.0 resolution: "resolve-from@npm:5.0.0" @@ -4958,7 +3555,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.1, resolve@npm:^1.20.0, resolve@npm:^1.22.0": +"resolve@npm:^1.20.0": version: 1.22.1 resolution: "resolve@npm:1.22.1" dependencies: @@ -4971,7 +3568,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin": +"resolve@patch:resolve@^1.20.0#~builtin": version: 1.22.1 resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=c3c19d" dependencies: @@ -4984,16 +3581,6 @@ __metadata: languageName: node linkType: hard -"restore-cursor@npm:^3.1.0": - version: 3.1.0 - resolution: "restore-cursor@npm:3.1.0" - dependencies: - onetime: ^5.1.0 - signal-exit: ^3.0.2 - checksum: f877dd8741796b909f2a82454ec111afb84eb45890eb49ac947d87991379406b3b83ff9673a46012fca0d7844bb989f45cc5b788254cf1a39b6b5a9659de0630 - languageName: node - linkType: hard - "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -5001,20 +3588,6 @@ __metadata: languageName: node linkType: hard -"reusify@npm:^1.0.4": - version: 1.0.4 - resolution: "reusify@npm:1.0.4" - checksum: c3076ebcc22a6bc252cb0b9c77561795256c22b757f40c0d8110b1300723f15ec0fc8685e8d4ea6d7666f36c79ccc793b1939c748bf36f18f542744a4e379fcc - languageName: node - linkType: hard - -"rfdc@npm:^1.3.0": - version: 1.3.0 - resolution: "rfdc@npm:1.3.0" - checksum: fb2ba8512e43519983b4c61bd3fa77c0f410eff6bae68b08614437bc3f35f91362215f7b4a73cbda6f67330b5746ce07db5dd9850ad3edc91271ad6deea0df32 - languageName: node - linkType: hard - "rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" @@ -5026,21 +3599,32 @@ __metadata: languageName: node linkType: hard -"run-parallel@npm:^1.1.9": - version: 1.2.0 - resolution: "run-parallel@npm:1.2.0" - dependencies: - queue-microtask: ^1.2.2 - checksum: cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d - languageName: node - linkType: hard - -"rxjs@npm:^7.5.1": - version: 7.8.0 - resolution: "rxjs@npm:7.8.0" +"rome@npm:^11.0.0": + version: 11.0.0 + resolution: "rome@npm:11.0.0" dependencies: - tslib: ^2.1.0 - checksum: 61b4d4fd323c1043d8d6ceb91f24183b28bcf5def4f01ca111511d5c6b66755bc5578587fe714ef5d67cf4c9f2e26f4490d4e1d8cabf9bd5967687835e9866a2 + "@rometools/cli-darwin-arm64": 11.0.0 + "@rometools/cli-darwin-x64": 11.0.0 + "@rometools/cli-linux-arm64": 11.0.0 + "@rometools/cli-linux-x64": 11.0.0 + "@rometools/cli-win32-arm64": 11.0.0 + "@rometools/cli-win32-x64": 11.0.0 + dependenciesMeta: + "@rometools/cli-darwin-arm64": + optional: true + "@rometools/cli-darwin-x64": + optional: true + "@rometools/cli-linux-arm64": + optional: true + "@rometools/cli-linux-x64": + optional: true + "@rometools/cli-win32-arm64": + optional: true + "@rometools/cli-win32-x64": + optional: true + bin: + rome: bin/rome + checksum: 3c92a47c78a66c62f94b6a3a72ead92a20c23326d2d73785ce88c03897b60b8114c285a636acc24bd11039381ded48f8c58c6cc5c4cc46ce7eed092c90d9a054 languageName: node linkType: hard @@ -5051,17 +3635,6 @@ __metadata: languageName: node linkType: hard -"safe-regex-test@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-regex-test@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 - is-regex: ^1.1.4 - checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 - languageName: node - linkType: hard - "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -5078,14 +3651,7 @@ __metadata: languageName: node linkType: hard -"semver-compare@npm:^1.0.0": - version: 1.0.0 - resolution: "semver-compare@npm:1.0.0" - checksum: dd1d7e2909744cf2cf71864ac718efc990297f9de2913b68e41a214319e70174b1d1793ac16e31183b128c2b9812541300cb324db8168e6cf6b570703b171c68 - languageName: node - linkType: hard - -"semver@npm:7.x, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7": +"semver@npm:7.x, semver@npm:^7.3.2, semver@npm:^7.3.5": version: 7.3.8 resolution: "semver@npm:7.3.8" dependencies: @@ -5096,7 +3662,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.1.0, semver@npm:^6.3.0": +"semver@npm:^6.0.0, semver@npm:^6.3.0": version: 6.3.0 resolution: "semver@npm:6.3.0" bin: @@ -5128,25 +3694,14 @@ __metadata: languageName: node linkType: hard -"shiki@npm:^0.10.1": - version: 0.10.1 - resolution: "shiki@npm:0.10.1" +"shiki@npm:^0.11.1": + version: 0.11.1 + resolution: "shiki@npm:0.11.1" dependencies: jsonc-parser: ^3.0.0 vscode-oniguruma: ^1.6.1 - vscode-textmate: 5.2.0 - checksum: fb746f3cb3de7e545e3b10a6cb658d3938f840e4ccc9a3c90ceb7e69a8f89dbb432171faac1e9f02a03f103684dad88ee5e54b5c4964fa6b579fca6e8e26424d - languageName: node - linkType: hard - -"side-channel@npm:^1.0.4": - version: 1.0.4 - resolution: "side-channel@npm:1.0.4" - dependencies: - call-bind: ^1.0.0 - get-intrinsic: ^1.0.2 - object-inspect: ^1.9.0 - checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 + vscode-textmate: ^6.0.0 + checksum: 2a4ebc3b466816263fc244ae4f67a4ff96aa74d863b9c5e7e4affc50f37fd6d1a781405de0dbf763b777bc33e49a0d441de7ff3fededb8b01e3b8dbb37e2927d languageName: node linkType: hard @@ -5171,28 +3726,6 @@ __metadata: languageName: node linkType: hard -"slice-ansi@npm:^3.0.0": - version: 3.0.0 - resolution: "slice-ansi@npm:3.0.0" - dependencies: - ansi-styles: ^4.0.0 - astral-regex: ^2.0.0 - is-fullwidth-code-point: ^3.0.0 - checksum: 5ec6d022d12e016347e9e3e98a7eb2a592213a43a65f1b61b74d2c78288da0aded781f665807a9f3876b9daa9ad94f64f77d7633a0458876c3a4fdc4eb223f24 - languageName: node - linkType: hard - -"slice-ansi@npm:^4.0.0": - version: 4.0.0 - resolution: "slice-ansi@npm:4.0.0" - dependencies: - ansi-styles: ^4.0.0 - astral-regex: ^2.0.0 - is-fullwidth-code-point: ^3.0.0 - checksum: 4a82d7f085b0e1b070e004941ada3c40d3818563ac44766cca4ceadd2080427d337554f9f99a13aaeb3b4a94d9964d9466c807b3d7b7541d1ec37ee32d308756 - languageName: node - linkType: hard - "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -5270,13 +3803,6 @@ __metadata: languageName: node linkType: hard -"string-argv@npm:0.3.1": - version: 0.3.1 - resolution: "string-argv@npm:0.3.1" - checksum: efbd0289b599bee808ce80820dfe49c9635610715429c6b7cc50750f0437e3c2f697c81e5c390208c13b5d5d12d904a1546172a88579f6ee5cbaaaa4dc9ec5cf - languageName: node - linkType: hard - "string-length@npm:^4.0.1": version: 4.0.2 resolution: "string-length@npm:4.0.2" @@ -5298,28 +3824,6 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.6": - version: 1.0.6 - resolution: "string.prototype.trimend@npm:1.0.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 0fdc34645a639bd35179b5a08227a353b88dc089adf438f46be8a7c197fc3f22f8514c1c9be4629b3cd29c281582730a8cbbad6466c60f76b5f99cf2addb132e - languageName: node - linkType: hard - -"string.prototype.trimstart@npm:^1.0.6": - version: 1.0.6 - resolution: "string.prototype.trimstart@npm:1.0.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 89080feef416621e6ef1279588994305477a7a91648d9436490d56010a1f7adc39167cddac7ce0b9884b8cdbef086987c4dcb2960209f2af8bac0d23ceff4f41 - languageName: node - linkType: hard - "string_decoder@npm:^1.1.1": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" @@ -5329,17 +3833,6 @@ __metadata: languageName: node linkType: hard -"stringify-object@npm:3.3.0": - version: 3.3.0 - resolution: "stringify-object@npm:3.3.0" - dependencies: - get-own-enumerable-property-symbols: ^3.0.0 - is-obj: ^1.0.1 - is-regexp: ^1.0.0 - checksum: 6827a3f35975cfa8572e8cd3ed4f7b262def260af18655c6fde549334acdac49ddba69f3c861ea5a6e9c5a4990fe4ae870b9c0e6c31019430504c94a83b7a154 - languageName: node - linkType: hard - "strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -5349,13 +3842,6 @@ __metadata: languageName: node linkType: hard -"strip-bom@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-bom@npm:3.0.0" - checksum: 8d50ff27b7ebe5ecc78f1fe1e00fcdff7af014e73cf724b46fb81ef889eeb1015fc5184b64e81a2efe002180f3ba431bdd77e300da5c6685d702780fbf0c8d5b - languageName: node - linkType: hard - "strip-bom@npm:^4.0.0": version: 4.0.0 resolution: "strip-bom@npm:4.0.0" @@ -5370,22 +3856,13 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 languageName: node linkType: hard -"supports-color@npm:8.1.1, supports-color@npm:^8.0.0": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: ^4.0.0 - checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 - languageName: node - linkType: hard - "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -5404,6 +3881,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^8.0.0": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: ^4.0.0 + checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 + languageName: node + linkType: hard + "supports-hyperlinks@npm:^2.0.0": version: 2.3.0 resolution: "supports-hyperlinks@npm:2.3.0" @@ -5463,13 +3949,6 @@ __metadata: languageName: node linkType: hard -"text-table@npm:^0.2.0": - version: 0.2.0 - resolution: "text-table@npm:0.2.0" - checksum: b6937a38c80c7f84d9c11dd75e49d5c44f71d95e810a3250bd1f1797fc7117c57698204adf676b71497acc205d769d65c16ae8fa10afad832ae1322630aef10a - languageName: node - linkType: hard - "throat@npm:^6.0.1": version: 6.0.1 resolution: "throat@npm:6.0.1" @@ -5477,13 +3956,6 @@ __metadata: languageName: node linkType: hard -"through@npm:^2.3.8": - version: 2.3.8 - resolution: "through@npm:2.3.8" - checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -5599,52 +4071,6 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.14.1": - version: 3.14.1 - resolution: "tsconfig-paths@npm:3.14.1" - dependencies: - "@types/json5": ^0.0.29 - json5: ^1.0.1 - minimist: ^1.2.6 - strip-bom: ^3.0.0 - checksum: 8afa01c673ebb4782ba53d3a12df97fa837ce524f8ad38ee4e2b2fd57f5ac79abc21c574e9e9eb014d93efe7fe8214001b96233b5c6ea75bd1ea82afe17a4c6d - languageName: node - linkType: hard - -"tslib@npm:^1.8.1": - version: 1.14.1 - resolution: "tslib@npm:1.14.1" - checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd - languageName: node - linkType: hard - -"tslib@npm:^2.1.0": - version: 2.4.1 - resolution: "tslib@npm:2.4.1" - checksum: 19480d6e0313292bd6505d4efe096a6b31c70e21cf08b5febf4da62e95c265c8f571f7b36fcc3d1a17e068032f59c269fab3459d6cd3ed6949eafecf64315fca - languageName: node - linkType: hard - -"tsutils@npm:^3.21.0": - version: 3.21.0 - resolution: "tsutils@npm:3.21.0" - dependencies: - tslib: ^1.8.1 - peerDependencies: - typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - checksum: 1843f4c1b2e0f975e08c4c21caa4af4f7f65a12ac1b81b3b8489366826259323feb3fc7a243123453d2d1a02314205a7634e048d4a8009921da19f99755cdc48 - languageName: node - linkType: hard - -"type-check@npm:^0.4.0, type-check@npm:~0.4.0": - version: 0.4.0 - resolution: "type-check@npm:0.4.0" - dependencies: - prelude-ls: ^1.2.1 - checksum: ec688ebfc9c45d0c30412e41ca9c0cdbd704580eb3a9ccf07b9b576094d7b86a012baebc95681999dd38f4f444afd28504cb3a89f2ef16b31d4ab61a0739025a - languageName: node - linkType: hard - "type-check@npm:~0.3.2": version: 0.3.2 resolution: "type-check@npm:0.3.2" @@ -5661,13 +4087,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^0.20.2": - version: 0.20.2 - resolution: "type-fest@npm:0.20.2" - checksum: 4fb3272df21ad1c552486f8a2f8e115c09a521ad7a8db3d56d53718d0c907b62c6e9141ba5f584af3f6830d0872c521357e512381f24f7c44acae583ad517d73 - languageName: node - linkType: hard - "type-fest@npm:^0.21.3": version: 0.21.3 resolution: "type-fest@npm:0.21.3" @@ -5684,52 +4103,39 @@ __metadata: languageName: node linkType: hard -"typedoc@npm:^0.22.9": - version: 0.22.18 - resolution: "typedoc@npm:0.22.18" +"typedoc@npm:^0.23": + version: 0.23.23 + resolution: "typedoc@npm:0.23.23" dependencies: - glob: ^8.0.3 lunr: ^2.3.9 - marked: ^4.0.16 - minimatch: ^5.1.0 - shiki: ^0.10.1 + marked: ^4.2.4 + minimatch: ^5.1.1 + shiki: ^0.11.1 peerDependencies: - typescript: 4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x || 4.7.x + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x bin: typedoc: bin/typedoc - checksum: b813d8129682f6ed5a4e96bacaf019e4da1d2744ca89fef850d6bb4c034616567ce67e6a7f5cfc5f00aac573f0b45d44b1427aafa262ab88dce6b460cb9e744c + checksum: 2b64f9c9dc1992ec1bbcc688f6cfc8161481872c485ba9226d1797f572469d02f7798ebe96e3626587a6952af685fa1f4aaa0d9a6137fe9fb3d37f677cb41161 languageName: node linkType: hard -"typescript@npm:4.4.3": - version: 4.4.3 - resolution: "typescript@npm:4.4.3" +"typescript@npm:4.9": + version: 4.9.4 + resolution: "typescript@npm:4.9.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 05823f21796d450531a7e4ab299715d38fd9ded0e4ce7400876053f4b5166ca3dde7a68cecfe72d9086039f03c0b6edba36516fb10ed83c5837d9600532ea4c2 + checksum: e782fb9e0031cb258a80000f6c13530288c6d63f1177ed43f770533fdc15740d271554cdae86701c1dd2c83b082cea808b07e97fd68b38a172a83dbf9e0d0ef9 languageName: node linkType: hard -"typescript@patch:typescript@4.4.3#~builtin": - version: 4.4.3 - resolution: "typescript@patch:typescript@npm%3A4.4.3#~builtin::version=4.4.3&hash=bbeadb" +"typescript@patch:typescript@4.9#~builtin": + version: 4.9.4 + resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=ad5954" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 33a916819907e819430802c50405f18c187448d678696a81545f494a7ec16207f8c15340c945df62109bd1d951091a9f15f3d2eb931a3c889f962c8509017697 - languageName: node - linkType: hard - -"unbox-primitive@npm:^1.0.2": - version: 1.0.2 - resolution: "unbox-primitive@npm:1.0.2" - dependencies: - call-bind: ^1.0.2 - has-bigints: ^1.0.2 - has-symbols: ^1.0.3 - which-boxed-primitive: ^1.0.2 - checksum: b7a1cf5862b5e4b5deb091672ffa579aa274f648410009c81cca63fed3b62b610c4f3b773f912ce545bb4e31edc3138975b5bc777fc6e4817dca51affb6380e9 + checksum: 1caaea6cb7f813e64345190fddc4e6c924d0b698ab81189b503763c4a18f7f5501c69362979d36e19c042d89d936443e768a78b0675690b35eb663d19e0eae71 languageName: node linkType: hard @@ -5772,15 +4178,6 @@ __metadata: languageName: node linkType: hard -"uri-js@npm:^4.2.2": - version: 4.4.1 - resolution: "uri-js@npm:4.4.1" - dependencies: - punycode: ^2.1.0 - checksum: 7167432de6817fe8e9e0c9684f1d2de2bb688c94388f7569f7dbdb1587c9f4ca2a77962f134ec90be0cc4d004c939ff0d05acc9f34a0db39a3c797dada262633 - languageName: node - linkType: hard - "url-parse@npm:^1.5.3": version: 1.5.10 resolution: "url-parse@npm:1.5.10" @@ -5823,10 +4220,10 @@ __metadata: languageName: node linkType: hard -"vscode-textmate@npm:5.2.0": - version: 5.2.0 - resolution: "vscode-textmate@npm:5.2.0" - checksum: 5449b42d451080f6f3649b66948f4b5ee4643c4e88cfe3558a3b31c84c78060cfdd288c4958c1690eaa5cd65d09992fa6b7c3bef9d4aa72b3651054a04624d20 +"vscode-textmate@npm:^6.0.0": + version: 6.0.0 + resolution: "vscode-textmate@npm:6.0.0" + checksum: ff6f17a406c2906586afc14ef01cb122e33acd35312e815abb5c924347a777c6783ce3fe7db8b83f1760ebf843c669843b9390f905b69c433b3395af28e4b483 languageName: node linkType: hard @@ -5898,19 +4295,6 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.0.2": - version: 1.0.2 - resolution: "which-boxed-primitive@npm:1.0.2" - dependencies: - is-bigint: ^1.0.1 - is-boolean-object: ^1.1.0 - is-number-object: ^1.0.4 - is-string: ^1.0.5 - is-symbol: ^1.0.3 - checksum: 53ce774c7379071729533922adcca47220228405e1895f26673bbd71bdf7fb09bee38c1d6399395927c6289476b5ae0629863427fd151491b71c4b6cb04f3a5e - languageName: node - linkType: hard - "which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -5931,24 +4315,13 @@ __metadata: languageName: node linkType: hard -"word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": +"word-wrap@npm:~1.2.3": version: 1.2.3 resolution: "word-wrap@npm:1.2.3" checksum: 30b48f91fcf12106ed3186ae4fa86a6a1842416df425be7b60485de14bec665a54a68e4b5156647dec3a70f25e84d270ca8bc8cd23182ed095f5c7206a938c1f languageName: node linkType: hard -"wrap-ansi@npm:^6.2.0": - version: 6.2.0 - resolution: "wrap-ansi@npm:6.2.0" - dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a - languageName: node - linkType: hard - "wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" @@ -6022,13 +4395,6 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^1.10.0": - version: 1.10.2 - resolution: "yaml@npm:1.10.2" - checksum: ce4ada136e8a78a0b08dc10b4b900936912d15de59905b2bf415b4d33c63df1d555d23acb2a41b23cf9fb5da41c256441afca3d6509de7247daa062fd2c5ea5f - languageName: node - linkType: hard - "yargs-parser@npm:20.x, yargs-parser@npm:^20.2.2": version: 20.2.9 resolution: "yargs-parser@npm:20.2.9" @@ -6057,10 +4423,3 @@ __metadata: checksum: 2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 languageName: node linkType: hard - -"yocto-queue@npm:^0.1.0": - version: 0.1.0 - resolution: "yocto-queue@npm:0.1.0" - checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 - languageName: node - linkType: hard