Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Python wrapper for the [sits](https://github.com/e-sensing/sits) R package.

## 📦 Installation
## Installation

To install `pysits` with pip:

Expand All @@ -18,7 +18,7 @@ pip install git+https://github.com/e-sensing/pysits.git

> To use `pysits`, you must have [sits](https://github.com/e-sensing/sits) installed. For more information, refer to the [official sits documentation](https://e-sensing.github.io/sitsbook/setup.html).

## 🛠 Development setup (for contributors)
## Development setup (for contributors)

To set up a local development environment:

Expand All @@ -42,23 +42,23 @@ source .venv/bin/activate # or .venv\Scripts\activate on Windows
uv pip install -e ".[dev]"
```

### 🔍 Run tests
### Run tests

We use `pytest` for testing:

```bash
pytest
```

### 🧹 Code formatting
### Code formatting

To keep the codebase clean and consistent we use [ruff](https://github.com/astral-sh/ruff):

```bash
ruff format .
```

### 🧪 Linting
### Linting

We use [ruff](https://github.com/astral-sh/ruff) for static analysis:

Expand All @@ -68,18 +68,18 @@ ruff check .

> The `examples/` directory is excluded from linting.

## 📚 Learn more
## Learn more

To learn all about `sits`, including its concepts, API, and real-world examples, we recommend accessing the [official sits book](https://e-sensing.github.io/sitsbook/). The book provides examples in both R and Python.

## 🤝 Contributing
## Contributing

We welcome contributions! Please:

- Fork the repository
- Create a feature branch
- Submit a pull request with a clear description

## 📄 License
## License

`pysits` is distributed under the GPL-2.0 license. See [LICENSE](./LICENSE) for more details.
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ xarray = [
"xarray>=2025.3.0",
"dask>=2025.3.0",
"rioxarray>=0.18.2",
"affine>=2.4.0"
"affine>=2.4.0",
"odc-stac>=0.5.3",
"pystac>=1.15.0",
"planetary-computer>=1.0.0"
]

dev = [
Expand Down
33 changes: 24 additions & 9 deletions pysits/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@
sits_colors_set,
sits_colors_show,
)
from .sits.config import sits_config, sits_config_show, sits_config_user_file
from .sits.config import (
sits_config,
sits_config_show,
sits_config_user_file,
sits_config_value,
sits_parallel,
)
from .sits.context import (
cerrado_2classes,
point_mt_6bands,
Expand Down Expand Up @@ -72,30 +78,36 @@
impute_mean,
impute_mean_window,
impute_median,
sits_impute,
)
from .sits.ml import (
sits_barlow_twins,
sits_contrastive_learning,
sits_formula_linear,
sits_formula_logref,
sits_kfold_validate,
sits_lightgbm,
sits_lighttae,
sits_mlp,
sits_model_export,
sits_pre_train,
sits_resnet,
sits_rfor,
sits_ssl_lejepa,
sits_ssl_mae,
sits_ssl_vicreg,
sits_svm,
sits_tae,
sits_tempcnn,
sits_train,
sits_xgboost,
)
from .sits.segment import sits_segment, sits_slic, sits_snic
from .sits.tiles import sits_mgrs_to_roi, sits_roi_to_tiles, sits_tiles_to_roi
from .sits.tiles import sits_roi_to_tiles, sits_tiles_to_roi
from .sits.ts import (
sits_cluster_clean,
sits_cluster_dendro,
sits_cluster_frequency,
sits_encode,
sits_geo_dist,
sits_get_class,
sits_get_data,
Expand All @@ -109,15 +121,13 @@
sits_reduce_imbalance,
sits_sample,
sits_sampling_design,
sits_sgolay,
sits_show_prediction,
sits_som_clean_samples,
sits_som_evaluate_cluster,
sits_som_map,
sits_stats,
sits_stratified_sampling,
sits_validate,
sits_whittaker,
)
from .sits.tuning import sits_tuning, sits_tuning_hparams
from .sits.utils import (
Expand Down Expand Up @@ -158,6 +168,8 @@
"sits_config",
"sits_config_show",
"sits_config_user_file",
"sits_config_value",
"sits_parallel",
# Data management
"sits_bands",
"sits_timeline",
Expand Down Expand Up @@ -190,16 +202,19 @@
"sits_model_export",
"sits_formula_linear",
"sits_formula_logref",
"sits_ssl_mae",
"sits_ssl_lejepa",
"sits_ssl_vicreg",
"sits_barlow_twins",
"sits_contrastive_learning",
"sits_pre_train",
# Impute
"sits_impute",
"impute_linear",
"impute_mean",
"impute_median",
"impute_mean_window",
# Time-series
"sits_show_prediction",
"sits_sgolay",
"sits_whittaker",
"sits_get_data",
"sits_get_class",
"sits_get_probs",
Expand All @@ -219,8 +234,8 @@
"sits_reduce_imbalance",
"sits_sampling_design",
"sits_stratified_sampling",
"sits_encode",
# Tiles
"sits_mgrs_to_roi",
"sits_tiles_to_roi",
"sits_roi_to_tiles",
# Segments
Expand Down
11 changes: 10 additions & 1 deletion pysits/backend/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

"""backend functions."""

from pysits.backend.loaders import load_function_from_package
from pysits.backend.loaders import (
load_function_from_package,
load_internal_function_from_package,
)

# Base - plot
r_fnc_plot = load_function_from_package("base::plot")
Expand All @@ -40,6 +43,9 @@
# Base - class (base)
r_fnc_class = load_function_from_package("base::class")

# Base - eval (base)
r_fnc_eval = load_function_from_package("base::eval")

# Base - as.data.frame (base)
r_fnc_as_data_frame = load_function_from_package("base::as_data_frame")

Expand All @@ -48,3 +54,6 @@

# Base - rownames (base)
r_fnc_rownames = load_function_from_package("base::rownames")

# sits - configuration (internal)
r_fnc_sits_conf = load_internal_function_from_package("sits:::.conf")
39 changes: 39 additions & 0 deletions pysits/backend/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,42 @@ def load_function_from_package(name: str) -> Callable[..., Any]:

# Return function
return getattr(pkg, func_name)


def load_internal_function_from_package(name: str) -> Callable[..., Any]:
"""Load an internal R function from a specified package.

Internal functions are those not exported by a package. They are addressed
in R with the ``package:::function`` notation.

Args:
name (str): The fully qualified name of the R function in the format
'package:::function'. For example, 'sits:::.conf'.

Returns:
Callable[..., Any]: A Python callable that wraps the R function.
The exact signature depends on the underlying R function.

Raises:
ValueError: If the ``name`` doesn't follow the 'package:::function' format.

PackageNotFoundError: If the specified R package is not installed.

Examples:
>>> conf = load_internal_function_from_package('sits:::.conf')
>>> scale = conf('sources', 'BDC', 'collections', 'MOD13Q1-6.1')
"""
# Parse package and function
package_name, _, func_name = name.partition(":::")

if not package_name or not func_name:
raise ValueError(
f"Invalid function name format: {name}. "
"Expected format: 'package:::function'"
)

# Import the package
importr(package_name, on_conflict="warn")

# Return function
return rpy2_r_interface(name)
21 changes: 21 additions & 0 deletions pysits/conversions/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
from pandas import DataFrame as PandasDataFrame
from rpy2.rinterface_lib.sexp import NULLType
from rpy2.robjects import pandas2ri
from rpy2.robjects.language import LangVector
from rpy2.robjects.robject import RObjectMixin

from pysits.backend.functions import r_fnc_eval
from pysits.backend.pkgs import r_pkg_tibble
from pysits.conversions.dsl.base import DSLObject
from pysits.conversions.tibble import geopandas_to_tibble, pandas_to_tibble
Expand Down Expand Up @@ -179,6 +181,22 @@ def convert_to_r(obj):
raise TypeError(f"Cannot convert object of type {obj_type} to R format")


def eval_r_language(obj):
"""Evaluate an unevaluated R expression.

Args:
obj: The R object to evaluate.

Returns:
The evaluated R object. Objects that are not R expressions are returned
unchanged.
"""
if isinstance(obj, LangVector):
return r_fnc_eval(obj)

return obj


def convert_to_python(obj, as_type="str"):
"""Convert an R object to a Python representation.

Expand Down Expand Up @@ -206,6 +224,9 @@ def convert_to_python(obj, as_type="str"):
def _convert(value, type_):
result = []

# R expressions must be evaluated to be converted
value = eval_r_language(value)

if isinstance(value, ro.ListVector):
for k, v in value.items():
result.append(
Expand Down
Loading
Loading