Metadata-Version: 2.4
Name: torchriver
Version: 0.1.1
Summary: River low-flow and drought forecasting with PyTorch
Author: TorchRiver Contributors
License-Expression: MIT
Keywords: rivers,drought,low-flow,pytorch,gradio,time-series,hydrology
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Hydrology
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Requires-Dist: pandas>=1.5
Requires-Dist: torch>=2.0
Requires-Dist: scikit-learn>=1.2
Requires-Dist: joblib>=1.2
Requires-Dist: matplotlib>=3.7
Requires-Dist: gradio>=4.0
Requires-Dist: opendatasets>=0.1.22
Requires-Dist: kaggle>=1.6
Requires-Dist: openpyxl>=3.1
Requires-Dist: pyarrow>=12
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: nbformat>=5; extra == "dev"
Requires-Dist: build>=1; extra == "dev"
Requires-Dist: twine>=5; extra == "dev"
Provides-Extra: ui
Requires-Dist: gradio>=4.0; extra == "ui"
Requires-Dist: gradio_client>=1.0; extra == "ui"
Provides-Extra: kaggle
Requires-Dist: opendatasets>=0.1.22; extra == "kaggle"
Requires-Dist: kaggle>=1.6; extra == "kaggle"
Provides-Extra: all
Requires-Dist: torchriver[dev,kaggle,ui]; extra == "all"
Dynamic: license-file

# TorchRiver

TorchRiver is a Python library for river low-flow / drought early warning. It was extracted from the original `PyTorch_elbe.ipynb` notebook and generalized from an Elbe-only workflow into a reusable library for any compatible river dataset.

The official notebook-derived architecture is `river_cnn_lstm`:

```text
Conv1D -> ReLU -> MaxPool1D -> LSTM -> Dense -> probability/logit
```

`elbe_cnn_lstm` is accepted only as a legacy alias. New code should use `river_cnn_lstm`.


## PyPI / Colab installation

After publishing to PyPI:

```python
%pip install -q torchriver
```

Before publishing, build locally and upload the wheel to Colab:

```bash
python -m pip install -U build twine
python -m build
python -m twine check dist/*
```

Then in Colab upload `dist/torchriver-0.1.0-py3-none-any.whl`:

```python
from google.colab import files
uploaded = files.upload()
wheel = next(name for name in uploaded if name.endswith(".whl"))
%pip install -q "./{wheel}"

import torchriver
print(torchriver.__version__)
```

See `COLAB_INSTALL.md`, `PUBLISHING.md`, and `notebooks/08_colab_install_from_upload.ipynb`.

## Installation from ZIP or source

```bash
unzip torchriver_final.zip
cd torchriver_final
pip install -e .
```

## Quick start: synthetic data

```python
from torchriver import RiverProject

project = RiverProject(title="نظام إنذار مبكر للأنهار", lang="ar", direction="rtl")

project.make_synthetic(
    days=240,
    station_codes=["R001", "R002", "R003"],
    station_names={"R001": "River One", "R002": "River Two", "R003": "River Three"},
)

project.use_model(
    "river_cnn_lstm",
    horizons=[7, 14, 30],
    lookback=30,
    device="auto",
    window_mode="per_station",
    balance_classes=True,
)

project.train(epochs=2, batch_size=512, lr=0.001, seed=42)
print(project.predict(station="R001", horizon=14))
project.save_model("river_warning_model.triver", include_recent_data=True)
```

## Kaggle example

```python
from torchriver import RiverProject

project = RiverProject(title="نظام إنذار مبكر للأنهار", lang="ar", direction="rtl")
project.load_kaggle(
    "https://www.kaggle.com/datasets/assemelqirsh/elbe-river-data-lake",
    nrows=200_000,
    mapping={
        "date": "date",
        "station": "station_code",
        "station_name": "station",
        "discharge": "discharge_m3s",
        "low_flow_threshold": "low_flow_q10_threshold_m3s",
        "target": "target_lowflow",
    },
    station_names={"501010": "SCHÖNA", "501060": "DRESDEN", "502010": "AKEN"},
    fallback_synthetic=False,
)
project.use_model("river_cnn_lstm", horizons=[7, 14, 30], lookback=30)
project.train(epochs=2, batch_size=512, lr=0.001)
project.launch_river_style(title="منصة توقع انخفاض تدفق الأنهار", share=True, api=True, direction="rtl")
```

TorchRiver never stores Kaggle API keys. Use `KAGGLE_USERNAME` and `KAGGLE_KEY`, Kaggle's standard `~/.kaggle/kaggle.json`, or opendatasets' secure prompt.

## Compatible data format

Default columns:

```text
date
station_code
station
discharge_m3s
low_flow_q10_threshold_m3s
sdi_30
precipitation_mm
temperature_c
ndwi
target_lowflow
```

Custom schemas are supported through mapping:

```python
project.load_file(
    "my_river_data.csv",
    mapping={
        "date": "day",
        "station": "gauge_id",
        "station_name": "gauge_name",
        "discharge": "Q_m3s",
        "low_flow_threshold": "Q10",
        "target": "low_flow_flag",
    },
)
```

If a threshold is missing, TorchRiver computes Q10 per station. If the target is missing, it creates:

```python
target_lowflow = discharge_m3s < low_flow_q10_threshold_m3s
```

## Prediction

```python
project.predict(station="501060", horizon=14)
project.predict_all(horizon=14)
project.diagnose_predictions(horizon=14)
```

Prediction always uses the last `lookback` days for the same station. TorchRiver does not reuse the same last 30 rows for every station.

If predictions are almost constant, TorchRiver prints a warning and `diagnose_predictions()` returns suggestions such as checking target distribution, feature variance, class imbalance, `window_mode='per_station'`, and `legacy_sigmoid=False`.

## Gradio API

```python
project.launch_river_style(share=True, api=True)
```

Client usage:

```python
from gradio_client import Client

client = Client("GRADIO_URL")
client.predict("501060 - DRESDEN", "+14 days", api_name="/predict")
```

## Save/load

```python
project.save_model("river_warning_model.triver", include_recent_data=True)

from torchriver import load_river_model
loaded = load_river_model("river_warning_model.triver")
loaded.predict(station="501060", horizon=14)
loaded.launch_river_style(share=True, api=True)
```

## Notebook-style artifacts

```python
project.export_notebook_artifacts("production_artifacts")
```

This writes `production_model_7d.pt`, `production_model_14d.pt`, `production_model_30d.pt`, `feature_cols.joblib`, `production_scaler.joblib`, and `metadata.joblib`.

## Tests

```bash
pytest
```


## Complete release bundle quick path

For Colab, upload the file `dist/torchriver-0.1.1-py3-none-any.whl`, then run the first install cell in any notebook under `notebooks/`.

Recommended notebooks:

1. `notebooks/01_colab_install_wheel.ipynb` — install and smoke test.
2. `notebooks/02_quick_start_synthetic_train_predict.ipynb` — train without external data.
3. `notebooks/03_kaggle_url_train_elbe.ipynb` — train from the Elbe Kaggle URL using `KAGGLE_USERNAME` and a hidden token prompt.
4. `notebooks/04_kaggle_or_default_switch.ipynb` — choose Kaggle or default synthetic data.
5. `notebooks/08_save_load_triver.ipynb` — save/load `.triver`.
6. `notebooks/09_gradio_rtl_arabic.ipynb` — launch Arabic RTL Gradio UI.

Minimal Kaggle runner:

```python
import os, getpass
from torchriver import RiverProject

DATA_SOURCE = "kaggle"
KAGGLE_URL = "https://www.kaggle.com/datasets/assemelqirsh/elbe-river-data-lake"
KAGGLE_USERNAME = "amrtweg"
ASK_KAGGLE_TOKEN = True
NROWS = 200_000

if DATA_SOURCE == "kaggle":
    os.environ["KAGGLE_USERNAME"] = KAGGLE_USERNAME
    if ASK_KAGGLE_TOKEN and not os.environ.get("KAGGLE_KEY"):
        os.environ["KAGGLE_KEY"] = getpass.getpass("Paste Kaggle API key/token hidden: ")

project = RiverProject(title="نظام إنذار مبكر لنهر Elbe", lang="ar", direction="rtl")
project.load_kaggle(KAGGLE_URL, nrows=NROWS, fallback_synthetic=False)
display(project.df.head())
display(project.show_summary())

project.clean()
project.use_model("elbe_cnn_lstm", horizons=[7, 14, 30], lookback=30, window_mode="per_station", balance_classes=True)
project.train(epochs=2, batch_size=512, lr=0.001)
display(project.predict_all(14))
project.save_model("elbe_kaggle_model.triver")
```
