-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from ctdunc/main
Add Preliminary ISIN Support
- Loading branch information
Showing
9 changed files
with
272 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,4 +89,5 @@ docs/_build/ | |
|
||
# Polars Extension | ||
.so | ||
.dll | ||
.dll | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
from .iban import IbanExt # noqa: E402 | ||
|
||
from .isin import IsinExt # noqa: E402 | ||
__version__ = "0.1.0" | ||
__all__ = ["IbanExt"] | ||
__all__ = ["IbanExt", "IsinExt"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from __future__ import annotations | ||
import polars as pl | ||
from polars.utils.udfs import _get_shared_lib_location | ||
|
||
_lib = _get_shared_lib_location(__file__) | ||
|
||
@pl.api.register_expr_namespace("isin") | ||
class IsinExt: | ||
|
||
""" | ||
This class contains tools for parsing ISIN format data. | ||
Polars Namespace: isin | ||
Example: pl.col("isin_str").isin.country_code() | ||
""" | ||
|
||
def __init__(self, expr: pl.Expr): | ||
self._expr: pl.Expr = expr | ||
|
||
def country_code(self) -> pl.Expr: | ||
""" | ||
Returns country code from the ISIN, or null if it cannot be parsed. | ||
""" | ||
return self._expr.register_plugin( | ||
lib=_lib, | ||
symbol="pl_isin_country_code", | ||
is_elementwise=True, | ||
) | ||
|
||
def check_digit(self) -> pl.Expr: | ||
""" | ||
Returns check digits from the ISIN, or null if it cannot be parsed. | ||
""" | ||
return self._expr.register_plugin( | ||
lib=_lib, | ||
symbol="pl_isin_check_digit", | ||
is_elementwise=True, | ||
) | ||
|
||
def security_id(self) -> pl.Expr: | ||
""" | ||
Returns the 9-digit security identifier of the ISIN, or null if it cannot | ||
be parsed. | ||
""" | ||
return self._expr.register_plugin( | ||
lib=_lib, | ||
symbol="pl_isin_security_id", | ||
is_elementwise=True, | ||
) | ||
|
||
def is_valid(self) -> pl.Expr: | ||
""" | ||
Returns a boolean indicating whether the string is a valid ISIN string. | ||
""" | ||
return self._expr.register_plugin( | ||
lib=_lib, | ||
symbol="pl_isin_valid", | ||
is_elementwise=True, | ||
) | ||
|
||
def extract_all(self) -> pl.Expr: | ||
""" | ||
Returns all information from ISIN and return as a struct. Empty string means the part cannot | ||
be extracted. Running this can be faster than running the corresponding single queries together. | ||
""" | ||
return self._expr.register_plugin( | ||
lib=_lib, | ||
symbol="pl_isin_full", | ||
is_elementwise=True, | ||
) | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
use isin; | ||
use polars::prelude::*; | ||
use pyo3_polars::derive::polars_expr; | ||
|
||
fn isin_full_output(_: &[Field]) -> PolarsResult<Field> { | ||
let cc = Field::new("country_code", DataType::String); | ||
let id = Field::new("security_id", DataType::String); | ||
let cd = Field::new("check_digit", DataType::String); | ||
|
||
let v: Vec<Field> = vec![cc, id, cd]; | ||
Ok(Field::new("", DataType::Struct(v))) | ||
} | ||
|
||
#[polars_expr(output_type_func=isin_full_output)] | ||
fn pl_isin_full(inputs: &[Series]) -> PolarsResult<Series> { | ||
let ca = inputs[0].str()?; | ||
let mut cc_builder = StringChunkedBuilder::new("country_code", ca.len()); | ||
let mut id_builder = StringChunkedBuilder::new("security_id", ca.len()); | ||
let mut cd_builder = StringChunkedBuilder::new("check_digit", ca.len()); | ||
|
||
ca.into_iter().for_each(|op_s| { | ||
if let Some(s) = op_s { | ||
if let Ok(isin) = isin::parse(s) { | ||
cc_builder.append_value(isin.prefix()); | ||
id_builder.append_value(isin.basic_code()); | ||
cd_builder.append_value(isin.check_digit().to_string()); | ||
} else { | ||
cc_builder.append_null(); | ||
id_builder.append_null(); | ||
cd_builder.append_null(); | ||
} | ||
} else { | ||
cc_builder.append_null(); | ||
id_builder.append_null(); | ||
cd_builder.append_null(); | ||
} | ||
}); | ||
|
||
let cc = cc_builder.finish().into_series(); | ||
let id = id_builder.finish().into_series(); | ||
let cd = cd_builder.finish().into_series(); | ||
|
||
let out = StructChunked::new("isin", &[cc, id, cd])?; | ||
Ok(out.into_series()) | ||
} | ||
|
||
#[polars_expr(output_type=String)] | ||
fn pl_isin_country_code(inputs: &[Series]) -> PolarsResult<Series> { | ||
let ca = inputs[0].str()?; | ||
|
||
let mut builder = StringChunkedBuilder::new("country_code", ca.len()); | ||
|
||
ca.into_iter().for_each(|op_s| { | ||
if let Some(s) = op_s { | ||
if let Ok(isin) = isin::parse(s) { | ||
builder.append_value(isin.prefix()); | ||
} else { | ||
builder.append_null(); | ||
} | ||
} else { | ||
builder.append_null(); | ||
} | ||
}); | ||
|
||
let out = builder.finish(); | ||
Ok(out.into_series()) | ||
} | ||
|
||
#[polars_expr(output_type=String)] | ||
fn pl_isin_security_id(inputs: &[Series]) -> PolarsResult<Series> { | ||
let ca = inputs[0].str()?; | ||
|
||
let mut builder = StringChunkedBuilder::new("security_id", ca.len()); | ||
|
||
ca.into_iter().for_each(|op_s| { | ||
if let Some(s) = op_s { | ||
if let Ok(isin) = isin::parse(s) { | ||
builder.append_value(isin.basic_code()); | ||
} else { | ||
builder.append_null(); | ||
} | ||
} else { | ||
builder.append_null(); | ||
} | ||
}); | ||
|
||
let out = builder.finish(); | ||
Ok(out.into_series()) | ||
} | ||
|
||
#[polars_expr(output_type=String)] | ||
fn pl_isin_check_digit(inputs: &[Series]) -> PolarsResult<Series> { | ||
let ca = inputs[0].str()?; | ||
|
||
let mut builder = StringChunkedBuilder::new("check_digit", ca.len()); | ||
|
||
ca.into_iter().for_each(|op_s| { | ||
if let Some(s) = op_s { | ||
if let Ok(isin) = isin::parse(s) { | ||
builder.append_value(isin.check_digit().to_string()); | ||
} else { | ||
builder.append_null(); | ||
} | ||
} else { | ||
builder.append_null(); | ||
} | ||
}); | ||
|
||
let out = builder.finish(); | ||
Ok(out.into_series()) | ||
} | ||
|
||
#[polars_expr(output_type=Boolean)] | ||
fn pl_isin_valid(inputs: &[Series]) -> PolarsResult<Series> { | ||
let ca = inputs[0].str()?; | ||
|
||
let mut builder = BooleanChunkedBuilder::new("isin_valid", ca.len()); | ||
|
||
ca.into_iter().for_each(|op_s| { | ||
if let Some(s) = op_s { | ||
builder.append_value(isin::validate(s)); | ||
} else { | ||
builder.append_value(false); | ||
} | ||
}); | ||
|
||
let out = builder.finish(); | ||
Ok(out.into_series()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
mod iban_parsing; | ||
mod isin_parsing; | ||
mod utils; | ||
use pyo3::{pymodule, types::PyModule, PyResult, Python}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters