numerblox


Namenumerblox JSON
Version 1.3.1 PyPI version JSON
download
home_pageNone
SummarySolid Numerai Pipelines
upload_time2024-03-29 12:46:16
maintainerNone
docs_urlNone
authorCrowdCent
requires_python<3.12,>=3.9
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![](https://img.shields.io/pypi/v/numerblox.png)
![](https://img.shields.io/pypi/pyversions/numerblox.png)
![](https://img.shields.io/github/contributors/crowdcent/numerblox.png)
![](https://img.shields.io/codecov/c/gh/carlolepelaars/numerblox/master)
![](https://img.shields.io/pypi/dm/numerblox)


# NumerBlox

NumerBlox offers components that help with developing strong Numerai models and inference pipelines. From downloading data to submitting predictions, NumerBlox has you covered.

All components can be used standalone and all processors are fully compatible to use within [scikit-learn](https://scikit-learn.org/) pipelines.  

**Documentation:**
[crowdcent.github.io/numerblox](https://crowdcent.github.io/numerblox)

## 1. Installation

Install numerblox from PyPi by running:

`pip install numerblox`

Alternatively you can clone this repository and install it in
development mode by installing using `poetry`:

```bash
git clone https://github.com/crowdcent/numerblox.git
pip install poetry
cd numerblox
poetry install
```

Installation without dev dependencies can be done by adding `--only main` to the `poetry install` line.

Test your installation using one of the education notebooks in
[examples](https://github.com/crowdcent/numerblox/examples). Good places to start are [quickstart.ipynb](https://github.com/crowdcent/numerblox/examples/quickstart.ipynb) and [numerframe_tutorial.ipynb](https://github.com/crowdcent/numerblox/examples/numerframe_tutorial.ipynb). Run it in your
Notebook environment to quickly test if your installation has succeeded.
The documentation contains examples and explanations for each component of NumerBlox.

## 2. Core functionality

NumerBlox has the following features for both Numerai Classic and Signals:

**[Data Download](https://crowdcent.github.io/numerblox/download/):** Automated retrieval of Numerai datasets.

**[NumerFrame](https://crowdcent.github.io/numerblox/numerframe/):** A custom Pandas DataFrame for easier Numerai data manipulation.

**[Preprocessors](https://crowdcent.github.io/numerblox/preprocessing/):** Customizable techniques for data preprocessing.

**[Target Engineering](https://crowdcent.github.io/numerblox/targets/):** Tools for creating new target variables.

**[Postprocessors](https://crowdcent.github.io/numerblox/neutralization/):** Ensembling, neutralization, and penalization.

**[MetaPipeline](https://crowdcent.github.io/numerblox/meta/):** An era-aware pipeline extension of scikit-learn's Pipeline. Specifically designed to integrate with era-specific Postprocessors such as neutralization and ensembling. Can be optionally bypassed for custom implementations.

**[MetaEstimators](https://crowdcent.github.io/numerblox/meta/):** Era-aware estimators that extend scikit-learn's functionality. Includes features like CrossValEstimator which allow for era-specific, multiple-folds fitting seamlessly integrated into the pipeline.

**[Evaluation](https://crowdcent.github.io/numerblox/evaluation/):** Comprehensive metrics aligned with Numerai's evaluation criteria.

**[Submitters](https://crowdcent.github.io/numerblox/submission/):** Facilitates secure and easy submission of predictions.

Example notebooks for each of these components can be found in the [examples](https://github.com/crowdcent/numerblox/examples). Also check out [the documentation](https://crowdcent.github.io/numerblox) for more information.


## 3. Quick Start

Below are two examples of how NumerBlox can be used to train and do inference on Numerai data. For a full overview of all components check out the documentation. More advanced examples to leverage NumerBlox to the fullest can be found in the [End-To-End Example section](https://crowdcent.github.io/numerblox/end_to_end/).

### 3.1 Simple example

The example below shows how NumerBlox simplifies training and inference on an XGBoost model.
NumerBlox is used here for easy downloading, data parsing, evaluation, inference and submission. You can experiment with this setup yourself in the example notebook [quickstart.ipynb](https://github.com/crowdcent/numerblox/examples/quickstart.ipynb).

```python
import pandas as pd
from xgboost import XGBRegressor
from numerblox.misc import Key
from numerblox.numerframe import create_numerframe
from numerblox.download import NumeraiClassicDownloader
from numerblox.prediction_loaders import ExamplePredictions
from numerblox.evaluation import NumeraiClassicEvaluator
from numerblox.submission import NumeraiClassicSubmitter

# Download data
downloader = NumeraiClassicDownloader("data")
# Training and validation data
downloader.download_training_data("train_val", version="4.3")
df = create_numerframe("data/train_val/train_int8.parquet")

# Train
X, y = df.get_feature_target_pair(multi_target=False)
xgb = XGBRegressor()
xgb.fit(X.values, y.values)

# Evaluate
val_df = create_numerframe("data/train_val/validation_int8.parquet")
val_df['prediction'] = xgb.predict(val_df.get_feature_data)
val_df['example_preds'] = ExamplePredictions("v4.3/validation_example_preds.parquet").fit_transform(None)['prediction'].values
evaluator = NumeraiClassicEvaluator()
metrics = evaluator.full_evaluation(val_df, 
                                    example_col="example_preds", 
                                    pred_cols=["prediction"], 
                                    target_col="target")

# Inference
downloader.download_live_data("current_round", version="4.3")
live_df = create_numerframe(file_path="data/current_round/live_int8.parquet")
live_X, live_y = live_df.get_feature_target_pair(multi_target=False)
preds = xgb.predict(live_X)

# Submit
NUMERAI_PUBLIC_ID = "YOUR_PUBLIC_ID"
NUMERAI_SECRET_KEY = "YOUR_SECRET_KEY"
key = Key(pub_id=NUMERAI_PUBLIC_ID, secret_key=NUMERAI_SECRET_KEY)
submitter = NumeraiClassicSubmitter(directory_path="sub_current_round", key=key)
# Your prediction file with 'id' as index and defined 'cols' below.
pred_dataf = pd.DataFrame(preds, index=live_df.index, columns=["prediction"])
# Only works with valid key credentials and model_name
submitter.full_submission(dataf=pred_dataf,
                          cols="prediction",
                          file_name="submission.csv",
                          model_name="MY_MODEL_NAME")
```

### 3.2. Advanced NumerBlox modeling

This example showcases how you can really push NumerBlox to create powerful pipelines. This pipeline approaches the Numerai Classic data as a classification problem. It fits multiple cross validation folds, reduces the classification probabilties to single values and create a weighted ensemble of these where the most recent folds get a higher weight. Lastly, the predictions are neutralized. The model is evaluated in validation data, inference is done on live data and a submission is done.
Lastly, we remove the download and submission directories to clean up the environment. This is especially convenient if you are running daily inference on your own server or a cloud VM.

```py
from xgboost import XGBClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import TimeSeriesSplit
from numerblox.meta import CrossValEstimator, make_meta_pipeline
from numerblox.prediction_loaders import ExamplePredictions
from numerblox.ensemble import NumeraiEnsemble, PredictionReducer
from numerblox.neutralizers import FeatureNeutralizer

# Download data
downloader = NumeraiClassicDownloader("data")
# Training and validation data
downloader.download_training_data("train_val", version="4.3")
df = create_numerframe("data/train_val/train_int8.parquet")

# Setup model pipeline
model = XGBClassifier()
crossval = CrossValEstimator(estimator=model, cv=TimeSeriesSplit(n_splits=5), predict_func='predict_proba')
pred_rud = PredictionReducer(n_models=5, n_classes=5)
ens = NumeraiEnsemble(donate_weighted=True)
neut = FeatureNeutralizer(proportion=0.5)
full_pipe = make_meta_pipeline(preproc_pipe, crossval, pred_rud, ens, neut)

# Train
X, y = df.get_feature_target_pair(multi_target=False)
y_int = (y * 4).astype(int)
era_series = df.get_era_data
features = df.get_feature_data
full_pipe.fit(X, y_int, era_series=era_series)

# Evaluate
val_df = create_numerframe("data/train_val/validation_int8.parquet")
val_X, _ = val_df.get_feature_target_pair(multi_target=False)
val_eras = val_df.get_era_data
val_features = val_df.get_feature_data
val_df['prediction'] = full_pipe.predict(val_X, era_series=val_eras, features=val_features)
val_df['example_preds'] = ExamplePredictions("v4.3/validation_example_preds.parquet").fit_transform(None)['prediction'].values
evaluator = NumeraiClassicEvaluator()
metrics = evaluator.full_evaluation(val_df, 
                                    example_col="example_preds", 
                                    pred_cols=["prediction"], 
                                    target_col="target")

# Inference
downloader.download_live_data("current_round", version="4.3")
live_df = create_numerframe(file_path="data/current_round/live_int8.parquet")
live_X, live_y = live_df.get_feature_target_pair(multi_target=False)
live_eras = live_df.get_era_data
live_features = live_df.get_feature_data
preds = full_pipe.predict(live_X, era_series=live_eras, features=live_features)

# Submit
NUMERAI_PUBLIC_ID = "YOUR_PUBLIC_ID"
NUMERAI_SECRET_KEY = "YOUR_SECRET_KEY"
key = Key(pub_id=NUMERAI_PUBLIC_ID, secret_key=NUMERAI_SECRET_KEY)
submitter = NumeraiClassicSubmitter(directory_path="sub_current_round", key=key)
# Your prediction file with 'id' as index and defined 'cols' below.
pred_dataf = pd.DataFrame(preds, index=live_df.index, columns=["prediction"])
# Only works with valid key credentials and model_name
submitter.full_submission(dataf=pred_dataf,
                          cols="prediction",
                          file_name="submission.csv",
                          model_name="MY_MODEL_NAME")

# Clean up environment
downloader.remove_base_directory()
submitter.remove_base_directory()
```

## 4. Contributing

Be sure to read the [How To Contribute section](https://crowdcent.github.io/numerblox/contributing/) section in the documentation for detailed instructions on
contributing.

If you have questions or want to discuss new ideas for NumerBlox,
please create a Github issue first.

## 5. Crediting sources

Some of the components in this library may be based on forum posts,
notebooks or ideas made public by the Numerai community. We have done
our best to ask all parties who posted a specific piece of code for
their permission and credit their work in the documentation. If your
code is used in this library without credits, please let us know, so we
can add a link to your article/code.

If you are contributing to NumerBlox and are using ideas posted
earlier by someone else, make sure to credit them by posting a link to
their article/code in documentation.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "numerblox",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.12,>=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "CrowdCent",
    "author_email": "support@crowdcent.com",
    "download_url": "https://files.pythonhosted.org/packages/45/3e/c551fde3f4b0f0d8001fc3bfdd42089167be18010e5ebb21871089e3721b/numerblox-1.3.1.tar.gz",
    "platform": null,
    "description": "![](https://img.shields.io/pypi/v/numerblox.png)\n![](https://img.shields.io/pypi/pyversions/numerblox.png)\n![](https://img.shields.io/github/contributors/crowdcent/numerblox.png)\n![](https://img.shields.io/codecov/c/gh/carlolepelaars/numerblox/master)\n![](https://img.shields.io/pypi/dm/numerblox)\n\n\n# NumerBlox\n\nNumerBlox offers components that help with developing strong Numerai models and inference pipelines. From downloading data to submitting predictions, NumerBlox has you covered.\n\nAll components can be used standalone and all processors are fully compatible to use within [scikit-learn](https://scikit-learn.org/) pipelines.  \n\n**Documentation:**\n[crowdcent.github.io/numerblox](https://crowdcent.github.io/numerblox)\n\n## 1. Installation\n\nInstall numerblox from PyPi by running:\n\n`pip install numerblox`\n\nAlternatively you can clone this repository and install it in\ndevelopment mode by installing using `poetry`:\n\n```bash\ngit clone https://github.com/crowdcent/numerblox.git\npip install poetry\ncd numerblox\npoetry install\n```\n\nInstallation without dev dependencies can be done by adding `--only main` to the `poetry install` line.\n\nTest your installation using one of the education notebooks in\n[examples](https://github.com/crowdcent/numerblox/examples). Good places to start are [quickstart.ipynb](https://github.com/crowdcent/numerblox/examples/quickstart.ipynb) and [numerframe_tutorial.ipynb](https://github.com/crowdcent/numerblox/examples/numerframe_tutorial.ipynb). Run it in your\nNotebook environment to quickly test if your installation has succeeded.\nThe documentation contains examples and explanations for each component of NumerBlox.\n\n## 2. Core functionality\n\nNumerBlox has the following features for both Numerai Classic and Signals:\n\n**[Data Download](https://crowdcent.github.io/numerblox/download/):** Automated retrieval of Numerai datasets.\n\n**[NumerFrame](https://crowdcent.github.io/numerblox/numerframe/):** A custom Pandas DataFrame for easier Numerai data manipulation.\n\n**[Preprocessors](https://crowdcent.github.io/numerblox/preprocessing/):** Customizable techniques for data preprocessing.\n\n**[Target Engineering](https://crowdcent.github.io/numerblox/targets/):** Tools for creating new target variables.\n\n**[Postprocessors](https://crowdcent.github.io/numerblox/neutralization/):** Ensembling, neutralization, and penalization.\n\n**[MetaPipeline](https://crowdcent.github.io/numerblox/meta/):** An era-aware pipeline extension of scikit-learn's Pipeline. Specifically designed to integrate with era-specific Postprocessors such as neutralization and ensembling. Can be optionally bypassed for custom implementations.\n\n**[MetaEstimators](https://crowdcent.github.io/numerblox/meta/):** Era-aware estimators that extend scikit-learn's functionality. Includes features like CrossValEstimator which allow for era-specific, multiple-folds fitting seamlessly integrated into the pipeline.\n\n**[Evaluation](https://crowdcent.github.io/numerblox/evaluation/):** Comprehensive metrics aligned with Numerai's evaluation criteria.\n\n**[Submitters](https://crowdcent.github.io/numerblox/submission/):** Facilitates secure and easy submission of predictions.\n\nExample notebooks for each of these components can be found in the [examples](https://github.com/crowdcent/numerblox/examples). Also check out [the documentation](https://crowdcent.github.io/numerblox) for more information.\n\n\n## 3. Quick Start\n\nBelow are two examples of how NumerBlox can be used to train and do inference on Numerai data. For a full overview of all components check out the documentation. More advanced examples to leverage NumerBlox to the fullest can be found in the [End-To-End Example section](https://crowdcent.github.io/numerblox/end_to_end/).\n\n### 3.1 Simple example\n\nThe example below shows how NumerBlox simplifies training and inference on an XGBoost model.\nNumerBlox is used here for easy downloading, data parsing, evaluation, inference and submission. You can experiment with this setup yourself in the example notebook [quickstart.ipynb](https://github.com/crowdcent/numerblox/examples/quickstart.ipynb).\n\n```python\nimport pandas as pd\nfrom xgboost import XGBRegressor\nfrom numerblox.misc import Key\nfrom numerblox.numerframe import create_numerframe\nfrom numerblox.download import NumeraiClassicDownloader\nfrom numerblox.prediction_loaders import ExamplePredictions\nfrom numerblox.evaluation import NumeraiClassicEvaluator\nfrom numerblox.submission import NumeraiClassicSubmitter\n\n# Download data\ndownloader = NumeraiClassicDownloader(\"data\")\n# Training and validation data\ndownloader.download_training_data(\"train_val\", version=\"4.3\")\ndf = create_numerframe(\"data/train_val/train_int8.parquet\")\n\n# Train\nX, y = df.get_feature_target_pair(multi_target=False)\nxgb = XGBRegressor()\nxgb.fit(X.values, y.values)\n\n# Evaluate\nval_df = create_numerframe(\"data/train_val/validation_int8.parquet\")\nval_df['prediction'] = xgb.predict(val_df.get_feature_data)\nval_df['example_preds'] = ExamplePredictions(\"v4.3/validation_example_preds.parquet\").fit_transform(None)['prediction'].values\nevaluator = NumeraiClassicEvaluator()\nmetrics = evaluator.full_evaluation(val_df, \n                                    example_col=\"example_preds\", \n                                    pred_cols=[\"prediction\"], \n                                    target_col=\"target\")\n\n# Inference\ndownloader.download_live_data(\"current_round\", version=\"4.3\")\nlive_df = create_numerframe(file_path=\"data/current_round/live_int8.parquet\")\nlive_X, live_y = live_df.get_feature_target_pair(multi_target=False)\npreds = xgb.predict(live_X)\n\n# Submit\nNUMERAI_PUBLIC_ID = \"YOUR_PUBLIC_ID\"\nNUMERAI_SECRET_KEY = \"YOUR_SECRET_KEY\"\nkey = Key(pub_id=NUMERAI_PUBLIC_ID, secret_key=NUMERAI_SECRET_KEY)\nsubmitter = NumeraiClassicSubmitter(directory_path=\"sub_current_round\", key=key)\n# Your prediction file with 'id' as index and defined 'cols' below.\npred_dataf = pd.DataFrame(preds, index=live_df.index, columns=[\"prediction\"])\n# Only works with valid key credentials and model_name\nsubmitter.full_submission(dataf=pred_dataf,\n                          cols=\"prediction\",\n                          file_name=\"submission.csv\",\n                          model_name=\"MY_MODEL_NAME\")\n```\n\n### 3.2. Advanced NumerBlox modeling\n\nThis example showcases how you can really push NumerBlox to create powerful pipelines. This pipeline approaches the Numerai Classic data as a classification problem. It fits multiple cross validation folds, reduces the classification probabilties to single values and create a weighted ensemble of these where the most recent folds get a higher weight. Lastly, the predictions are neutralized. The model is evaluated in validation data, inference is done on live data and a submission is done.\nLastly, we remove the download and submission directories to clean up the environment. This is especially convenient if you are running daily inference on your own server or a cloud VM.\n\n```py\nfrom xgboost import XGBClassifier\nfrom sklearn.tree import DecisionTreeClassifier\nfrom sklearn.model_selection import TimeSeriesSplit\nfrom numerblox.meta import CrossValEstimator, make_meta_pipeline\nfrom numerblox.prediction_loaders import ExamplePredictions\nfrom numerblox.ensemble import NumeraiEnsemble, PredictionReducer\nfrom numerblox.neutralizers import FeatureNeutralizer\n\n# Download data\ndownloader = NumeraiClassicDownloader(\"data\")\n# Training and validation data\ndownloader.download_training_data(\"train_val\", version=\"4.3\")\ndf = create_numerframe(\"data/train_val/train_int8.parquet\")\n\n# Setup model pipeline\nmodel = XGBClassifier()\ncrossval = CrossValEstimator(estimator=model, cv=TimeSeriesSplit(n_splits=5), predict_func='predict_proba')\npred_rud = PredictionReducer(n_models=5, n_classes=5)\nens = NumeraiEnsemble(donate_weighted=True)\nneut = FeatureNeutralizer(proportion=0.5)\nfull_pipe = make_meta_pipeline(preproc_pipe, crossval, pred_rud, ens, neut)\n\n# Train\nX, y = df.get_feature_target_pair(multi_target=False)\ny_int = (y * 4).astype(int)\nera_series = df.get_era_data\nfeatures = df.get_feature_data\nfull_pipe.fit(X, y_int, era_series=era_series)\n\n# Evaluate\nval_df = create_numerframe(\"data/train_val/validation_int8.parquet\")\nval_X, _ = val_df.get_feature_target_pair(multi_target=False)\nval_eras = val_df.get_era_data\nval_features = val_df.get_feature_data\nval_df['prediction'] = full_pipe.predict(val_X, era_series=val_eras, features=val_features)\nval_df['example_preds'] = ExamplePredictions(\"v4.3/validation_example_preds.parquet\").fit_transform(None)['prediction'].values\nevaluator = NumeraiClassicEvaluator()\nmetrics = evaluator.full_evaluation(val_df, \n                                    example_col=\"example_preds\", \n                                    pred_cols=[\"prediction\"], \n                                    target_col=\"target\")\n\n# Inference\ndownloader.download_live_data(\"current_round\", version=\"4.3\")\nlive_df = create_numerframe(file_path=\"data/current_round/live_int8.parquet\")\nlive_X, live_y = live_df.get_feature_target_pair(multi_target=False)\nlive_eras = live_df.get_era_data\nlive_features = live_df.get_feature_data\npreds = full_pipe.predict(live_X, era_series=live_eras, features=live_features)\n\n# Submit\nNUMERAI_PUBLIC_ID = \"YOUR_PUBLIC_ID\"\nNUMERAI_SECRET_KEY = \"YOUR_SECRET_KEY\"\nkey = Key(pub_id=NUMERAI_PUBLIC_ID, secret_key=NUMERAI_SECRET_KEY)\nsubmitter = NumeraiClassicSubmitter(directory_path=\"sub_current_round\", key=key)\n# Your prediction file with 'id' as index and defined 'cols' below.\npred_dataf = pd.DataFrame(preds, index=live_df.index, columns=[\"prediction\"])\n# Only works with valid key credentials and model_name\nsubmitter.full_submission(dataf=pred_dataf,\n                          cols=\"prediction\",\n                          file_name=\"submission.csv\",\n                          model_name=\"MY_MODEL_NAME\")\n\n# Clean up environment\ndownloader.remove_base_directory()\nsubmitter.remove_base_directory()\n```\n\n## 4. Contributing\n\nBe sure to read the [How To Contribute section](https://crowdcent.github.io/numerblox/contributing/) section in the documentation for detailed instructions on\ncontributing.\n\nIf you have questions or want to discuss new ideas for NumerBlox,\nplease create a Github issue first.\n\n## 5. Crediting sources\n\nSome of the components in this library may be based on forum posts,\nnotebooks or ideas made public by the Numerai community. We have done\nour best to ask all parties who posted a specific piece of code for\ntheir permission and credit their work in the documentation. If your\ncode is used in this library without credits, please let us know, so we\ncan add a link to your article/code.\n\nIf you are contributing to NumerBlox and are using ideas posted\nearlier by someone else, make sure to credit them by posting a link to\ntheir article/code in documentation.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Solid Numerai Pipelines",
    "version": "1.3.1",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "185e75b7b20534efbcfe9aebf101c3dc94cdc105eac69db1e86b9591cd911d46",
                "md5": "f5652c3d01d8e1b2d4ebdb80c6efa1ae",
                "sha256": "923298de035a8704556ee8a5a9e4c1fc04b41eb571acd15ce6d1e24502453232"
            },
            "downloads": -1,
            "filename": "numerblox-1.3.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f5652c3d01d8e1b2d4ebdb80c6efa1ae",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.12,>=3.9",
            "size": 111178,
            "upload_time": "2024-03-29T12:46:14",
            "upload_time_iso_8601": "2024-03-29T12:46:14.146313Z",
            "url": "https://files.pythonhosted.org/packages/18/5e/75b7b20534efbcfe9aebf101c3dc94cdc105eac69db1e86b9591cd911d46/numerblox-1.3.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "453ec551fde3f4b0f0d8001fc3bfdd42089167be18010e5ebb21871089e3721b",
                "md5": "cd8b7325b6db13295d97e5300e6ca25e",
                "sha256": "5e8abdef4d5a75db13c97587b3b6dce8d198b2879c99ee595520b2ff7819c331"
            },
            "downloads": -1,
            "filename": "numerblox-1.3.1.tar.gz",
            "has_sig": false,
            "md5_digest": "cd8b7325b6db13295d97e5300e6ca25e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.12,>=3.9",
            "size": 106883,
            "upload_time": "2024-03-29T12:46:16",
            "upload_time_iso_8601": "2024-03-29T12:46:16.729767Z",
            "url": "https://files.pythonhosted.org/packages/45/3e/c551fde3f4b0f0d8001fc3bfdd42089167be18010e5ebb21871089e3721b/numerblox-1.3.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-29 12:46:16",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "numerblox"
}
        
Elapsed time: 0.49240s