pysits 2.0.0.dev2__tar.gz → 2.0.0.dev4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysits-2.0.0.dev4/.github/actions/setup-pysits/action.yml +43 -0
- pysits-2.0.0.dev4/.github/workflows/ruff.yaml +32 -0
- pysits-2.0.0.dev4/.github/workflows/tests.yaml +80 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/PKG-INFO +13 -3
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pyproject.toml +13 -2
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/__init__.py +4 -0
- pysits-2.0.0.dev4/pysits/backend/arrow.py +106 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/backend/functions.py +3 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/backend/pkgs.py +6 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/decorators.py +43 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/tibble.py +36 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/plot.md +35 -9
- pysits-2.0.0.dev4/pysits/models/visual.py +567 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/colors.py +4 -2
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/visualization.py +43 -43
- pysits-2.0.0.dev4/pysits/templates/plot.html +24 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/visualization/__init__.py +10 -2
- pysits-2.0.0.dev4/pysits/visualization/base.py +189 -0
- pysits-2.0.0.dev4/pysits/visualization/device.py +86 -0
- pysits-2.0.0.dev4/pysits/visualization/host.py +64 -0
- pysits-2.0.0.dev4/pysits/visualization/options.py +292 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/conftest.py +26 -15
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_classification.py +2 -2
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_embedding.py +3 -3
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_som.py +8 -9
- pysits-2.0.0.dev4/tests/test_visual_models.py +528 -0
- pysits-2.0.0.dev4/tests/test_visualization.py +426 -0
- pysits-2.0.0.dev4/uv.lock +3641 -0
- pysits-2.0.0.dev2/.claude/settings.local.json +0 -8
- pysits-2.0.0.dev2/.github/workflows/ruff.yaml +0 -42
- pysits-2.0.0.dev2/pysits/visualization/base.py +0 -177
- pysits-2.0.0.dev2/pysits/visualization/image.py +0 -48
- pysits-2.0.0.dev2/pysits/visualization/tmap.py +0 -85
- pysits-2.0.0.dev2/tests/test_visualization.py +0 -181
- pysits-2.0.0.dev2/uv.lock +0 -2527
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/.gitattributes +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/.gitignore +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/.pre-commit-config.yaml +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/LICENSE +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/README.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/conda/meta.yaml +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/backend/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/backend/loaders.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/clojure.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/common.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/dsl/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/dsl/base.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/dsl/mask.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/dsl/tuning.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/stac.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/vector.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/conversions/xarray.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/impute_linear.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/impute_mean.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/impute_mean_window.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/impute_median.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_accuracy.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_accuracy_summary.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_add_base_cube.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_apply.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_as_geopandas.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_bands.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_barlow_twins.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_bbox.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_classify.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_clean.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_cluster_clean.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_cluster_dendro.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_cluster_frequency.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_colors.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_colors_qgis.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_colors_reset.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_colors_set.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_colors_show.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_combine_predictions.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_confidence_sampling.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_config.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_config_show.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_config_user_file.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_contrastive_learning.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_cube.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_cube_copy.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_encode.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_formula_linear.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_formula_logref.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_geo_dist.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_get_class.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_get_data.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_get_probs.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_kfold_validate.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_label_classification.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_labels.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_labels_summary.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_lightgbm.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_lighttae.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_list_collections.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_lstm_fcn.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_merge.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_mixture_model.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_mlp.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_model_export.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_mosaic.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_parallel.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_patterns.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_pre_train.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_pred_features.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_pred_normalize.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_pred_references.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_pred_sample.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_predictors.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_random_sampling.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_reclassify.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_reduce.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_reduce_imbalance.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_regularize.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_resnet.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_rfor.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_roi_to_tiles.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_sample.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_sampling_design.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_sankey.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_segment.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_select.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_show_prediction.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_slic.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_smooth.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_snic.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_som_clean_samples.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_som_evaluate_cluster.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_som_map.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_som_remove_samples.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_ssl_lejepa.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_ssl_mae.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_ssl_vicreg.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_stats.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_stratified_sampling.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_svm.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_tae.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_tempcnn.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_texture.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_tiles_to_roi.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_timeline.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_timeseries_to_csv.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_to_csv.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_to_xlsx.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_train.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_tuning.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_tuning_hparams.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_uncertainty.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_uncertainty_sampling.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_validate.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_variance.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_view.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/sits_xgboost.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/content/summary.md +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/docs/decorators.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/extras/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/extras/earthdatalogin.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/extras/torch.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/jinja.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/base.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/accuracy.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/base.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/cube.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/frame.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/frame_accessor.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/matrix.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/table.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/ts.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/tuning.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/data/vector.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/frame.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/ml.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/models/resolver.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/settings.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/config.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/context.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/cube.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/data.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/exporters/__init__.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/exporters/files.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/exporters/sf.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/exporters/xarray.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/impute.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/ml.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/segment.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/tiles.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/ts.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/tuning.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/sits/utils.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/templates/cube.html +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/templates/tuning.html +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/pysits/visualization/leaflet.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_config.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_conversions.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_cube.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_data.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_dsl.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_exporters.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_exporters_geopandas.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_exporters_xarray.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_extras.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_indexing.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_ml_models.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_resolver.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_rl_models.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_tiles.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_tuning.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_utils.py +0 -0
- {pysits-2.0.0.dev2 → pysits-2.0.0.dev4}/tests/test_validation.py +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Set up pysits
|
|
2
|
+
description: R with the sits stack, and uv-managed Python
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
python-version:
|
|
6
|
+
description: Python version to run pysits on
|
|
7
|
+
required: true
|
|
8
|
+
|
|
9
|
+
runs:
|
|
10
|
+
using: composite
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: r-lib/actions/setup-r@v2
|
|
14
|
+
with:
|
|
15
|
+
Ncpus: '4'
|
|
16
|
+
|
|
17
|
+
# R is installed under `/opt/R`, which the dynamic loader does not search.
|
|
18
|
+
# `rpy2` links against `libR.so` at import, so without this it falls back
|
|
19
|
+
# to ABI mode and then fails to load R's own base packages
|
|
20
|
+
- name: Put libR on the loader path
|
|
21
|
+
shell: bash
|
|
22
|
+
run: |
|
|
23
|
+
echo "LD_LIBRARY_PATH=$(R RHOME)/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> "$GITHUB_ENV"
|
|
24
|
+
|
|
25
|
+
- uses: r-lib/actions/setup-r-dependencies@v2
|
|
26
|
+
with:
|
|
27
|
+
packages: |
|
|
28
|
+
github::e-sensing/sits@dev
|
|
29
|
+
any::arrow
|
|
30
|
+
any::earthdatalogin
|
|
31
|
+
|
|
32
|
+
# The `torch` R package ships without libtorch, and pysits disables the
|
|
33
|
+
# implicit download (`TORCH_INSTALL=0`), so without this operation
|
|
34
|
+
# the deep learning models would fail only once a test train one
|
|
35
|
+
- name: Install libtorch
|
|
36
|
+
shell: bash
|
|
37
|
+
run: Rscript -e 'torch::install_torch()'
|
|
38
|
+
|
|
39
|
+
# After R: `rpy2` has no Linux wheel and is compiled against it
|
|
40
|
+
- uses: astral-sh/setup-uv@v9.0.0
|
|
41
|
+
with:
|
|
42
|
+
python-version: ${{ inputs.python-version }}
|
|
43
|
+
enable-cache: true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Code check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
reason:
|
|
12
|
+
required: false
|
|
13
|
+
description: 'Reason'
|
|
14
|
+
default: 'Manual trigger'
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
lint:
|
|
25
|
+
name: Run ruff
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v7
|
|
30
|
+
- uses: astral-sh/ruff-action@v4.1.0
|
|
31
|
+
with:
|
|
32
|
+
version-file: uv.lock
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
# Run every Saturday at 3:00 AM
|
|
10
|
+
schedule:
|
|
11
|
+
# * is a special character in YAML so you have to quote this string
|
|
12
|
+
- cron: '0 3 * * 6'
|
|
13
|
+
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
inputs:
|
|
16
|
+
reason:
|
|
17
|
+
required: false
|
|
18
|
+
description: 'Reason'
|
|
19
|
+
default: 'Manual trigger'
|
|
20
|
+
|
|
21
|
+
concurrency:
|
|
22
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
23
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
24
|
+
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
28
|
+
env:
|
|
29
|
+
# pak resolves `sits` from GitHub; without a token it shares the anonymous
|
|
30
|
+
# rate limit with every other runner
|
|
31
|
+
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
test:
|
|
35
|
+
name: Python ${{ matrix.python-version }}
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
timeout-minutes: 120
|
|
38
|
+
|
|
39
|
+
strategy:
|
|
40
|
+
# Report every version, so a single break does not hide the others
|
|
41
|
+
fail-fast: false
|
|
42
|
+
matrix:
|
|
43
|
+
# The suite trains various models on CPU. To reduce execution time,
|
|
44
|
+
# a change is only checked against floor and ceiling versions.
|
|
45
|
+
# The weekly test covers all other versions
|
|
46
|
+
python-version: ${{ fromJSON((github.event_name == 'push' || github.event_name == 'pull_request') && '["3.10", "3.14"]' || '["3.10", "3.11", "3.12", "3.13", "3.14"]') }}
|
|
47
|
+
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v7
|
|
50
|
+
|
|
51
|
+
- uses: ./.github/actions/setup-pysits
|
|
52
|
+
with:
|
|
53
|
+
python-version: ${{ matrix.python-version }}
|
|
54
|
+
|
|
55
|
+
- name: Install pysits
|
|
56
|
+
run: uv sync --locked --extra dev
|
|
57
|
+
|
|
58
|
+
- name: Run tests
|
|
59
|
+
run: uv run pytest
|
|
60
|
+
|
|
61
|
+
latest-dependencies:
|
|
62
|
+
name: Latest dependencies
|
|
63
|
+
# `uv.lock` pins what `test` runs against, so a release that breaks pysits
|
|
64
|
+
# stays invisible there until someone relocks. This resolves from scratch.
|
|
65
|
+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
timeout-minutes: 120
|
|
68
|
+
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v7
|
|
71
|
+
|
|
72
|
+
- uses: ./.github/actions/setup-pysits
|
|
73
|
+
with:
|
|
74
|
+
python-version: '3.14'
|
|
75
|
+
|
|
76
|
+
- name: Install pysits, ignoring the lockfile
|
|
77
|
+
run: uv sync --upgrade --extra dev
|
|
78
|
+
|
|
79
|
+
- name: Run tests
|
|
80
|
+
run: uv run pytest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pysits
|
|
3
|
-
Version: 2.0.0.
|
|
3
|
+
Version: 2.0.0.dev4
|
|
4
4
|
Summary: Python wrapper for the sits R package
|
|
5
5
|
Project-URL: Homepage, https://github.com/e-sensing/pysits
|
|
6
6
|
Project-URL: Issue Tracker, https://github.com/e-sensing/pysits/issues
|
|
@@ -20,17 +20,27 @@ Requires-Dist: geopandas>=1.1.0
|
|
|
20
20
|
Requires-Dist: jinja2>=3.1.6
|
|
21
21
|
Requires-Dist: matplotlib>=3.10.1
|
|
22
22
|
Requires-Dist: pandas>=2.2.3
|
|
23
|
+
Requires-Dist: pandas>=2.3.3; python_version >= '3.14'
|
|
23
24
|
Requires-Dist: pillow>=11.1.0
|
|
24
25
|
Requires-Dist: pyarrow>=20.0.0
|
|
25
|
-
Requires-Dist: rpy2<4.0.0,>=3.
|
|
26
|
+
Requires-Dist: rpy2<4.0.0,>=3.6.0
|
|
26
27
|
Requires-Dist: shapely>=2.1.1
|
|
27
28
|
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: affine>=2.4.0; extra == 'dev'
|
|
28
30
|
Requires-Dist: cloudpickle>=3.1.1; extra == 'dev'
|
|
31
|
+
Requires-Dist: dask>=2025.3.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ipykernel>=6.29.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: odc-stac>=0.5.3; extra == 'dev'
|
|
34
|
+
Requires-Dist: planetary-computer>=1.0.0; extra == 'dev'
|
|
29
35
|
Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pystac>=1.15.0; extra == 'dev'
|
|
30
37
|
Requires-Dist: pytest-cov>=6.1.1; extra == 'dev'
|
|
31
38
|
Requires-Dist: pytest-randomly>=3.16.0; extra == 'dev'
|
|
32
39
|
Requires-Dist: pytest>=7.4.4; extra == 'dev'
|
|
40
|
+
Requires-Dist: rasterio>=1.3.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: rioxarray>=0.18.2; extra == 'dev'
|
|
33
42
|
Requires-Dist: ruff>=0.11.12; extra == 'dev'
|
|
43
|
+
Requires-Dist: xarray>=2025.3.0; extra == 'dev'
|
|
34
44
|
Provides-Extra: xarray
|
|
35
45
|
Requires-Dist: affine>=2.4.0; extra == 'xarray'
|
|
36
46
|
Requires-Dist: dask>=2025.3.0; extra == 'xarray'
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
[project]
|
|
19
19
|
name = "pysits"
|
|
20
|
-
version = "2.0.0.
|
|
20
|
+
version = "2.0.0.dev4"
|
|
21
21
|
description = "Python wrapper for the sits R package"
|
|
22
22
|
readme = "README.md"
|
|
23
23
|
requires-python = ">=3.10,<4"
|
|
@@ -58,8 +58,13 @@ classifiers = [
|
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
dependencies = [
|
|
61
|
-
|
|
61
|
+
# 3.6.0 is the first release distributing wheels. Earlier ones are
|
|
62
|
+
# source-only, so pip would compile rpy2 against the local R installation
|
|
63
|
+
"rpy2>=3.6.0,<4.0.0",
|
|
62
64
|
"pandas>=2.2.3",
|
|
65
|
+
# pandas ships no wheels for Python 3.14 before 2.3.3. Without this floor
|
|
66
|
+
# pip builds pandas from source there, and the result segfaults.
|
|
67
|
+
"pandas>=2.3.3; python_version>='3.14'",
|
|
63
68
|
"pillow>=11.1.0",
|
|
64
69
|
"matplotlib>=3.10.1",
|
|
65
70
|
"geopandas>=1.1.0",
|
|
@@ -90,6 +95,12 @@ dev = [
|
|
|
90
95
|
"pytest-cov>=6.1.1",
|
|
91
96
|
"pytest-randomly>=3.16.0",
|
|
92
97
|
"cloudpickle>=3.1.1",
|
|
98
|
+
# The test suite covers the xarray exporters and reads rasters directly
|
|
99
|
+
"pysits[xarray]",
|
|
100
|
+
"rasterio>=1.3.0",
|
|
101
|
+
# Used to render Quarto documents to check that figures reach a
|
|
102
|
+
# document with the geometry the document asked for
|
|
103
|
+
"ipykernel>=6.29.0",
|
|
93
104
|
]
|
|
94
105
|
|
|
95
106
|
[tool.ruff]
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
"""pysits module."""
|
|
19
19
|
|
|
20
|
+
# Importing `backend.arrow` first selects the libarrow allocator, which must
|
|
21
|
+
# happen before anything imports `pyarrow` or loads the R `arrow` package.
|
|
22
|
+
# Keep this import above the others. See `pysits.backend.arrow`.
|
|
23
|
+
from .backend import arrow as _arrow # noqa: F401
|
|
20
24
|
from .conversions.dsl.mask import MaskValue
|
|
21
25
|
from .conversions.dsl.tuning import hparam
|
|
22
26
|
from .settings import __version__
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2025 sits developers.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify it
|
|
5
|
+
# under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
"""Arrow interoperability between R and Python.
|
|
19
|
+
|
|
20
|
+
``pysits`` moves data between R and Python as Arrow IPC streams. That puts two
|
|
21
|
+
independent builds of the libarrow C++ library in a single process: the one
|
|
22
|
+
bundled in the R ``arrow`` package and the one bundled in ``pyarrow``. Their
|
|
23
|
+
symbols collapse into a single namespace, so a call made through ``pyarrow``
|
|
24
|
+
can be served by R's libarrow.
|
|
25
|
+
|
|
26
|
+
Each build keeps a private ``mimalloc`` heap. When the two are mixed, a buffer
|
|
27
|
+
allocated by one library is read back as zeroed memory by the other, and every
|
|
28
|
+
transfer is silently corrupted instead of failing. Both builds also support the
|
|
29
|
+
system allocator, which is shared, so selecting it keeps them interoperable.
|
|
30
|
+
|
|
31
|
+
The allocator is chosen by libarrow the first time it initializes, so importing
|
|
32
|
+
this module sets ``ARROW_DEFAULT_MEMORY_POOL``. It is imported before anything
|
|
33
|
+
that pulls in ``pyarrow`` or the R ``arrow`` package.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import os
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Allocator selected to keep R libarrow and pyarrow interoperable
|
|
40
|
+
#
|
|
41
|
+
ARROW_MEMORY_POOL_ENVVAR = "ARROW_DEFAULT_MEMORY_POOL"
|
|
42
|
+
ARROW_MEMORY_POOL = "system"
|
|
43
|
+
|
|
44
|
+
# Set on import: libarrow reads this once, when it initializes. A value already
|
|
45
|
+
# present in the environment is left alone so users can override it.
|
|
46
|
+
os.environ.setdefault(ARROW_MEMORY_POOL_ENVVAR, ARROW_MEMORY_POOL)
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Values transferred to R and back to verify the round-trip is lossless. A
|
|
50
|
+
# mismatched pair of libarrow builds reads them back as zeros.
|
|
51
|
+
#
|
|
52
|
+
PROBE_COLUMN = "pysits_arrow_probe"
|
|
53
|
+
PROBE_VALUES = [0.5, 1.5, 2.5]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def check_arrow_memory_pool() -> None:
|
|
57
|
+
"""Refuse to load R ``arrow`` package under an unshared allocator.
|
|
58
|
+
|
|
59
|
+
Called before the R ``arrow`` package is loaded. Corruption appears only
|
|
60
|
+
once both libarrow builds are in use, and is silent when it does, so the
|
|
61
|
+
configuration is rejected up front rather than probed for afterwards.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
RuntimeError: If the selected allocator is not the shared one.
|
|
65
|
+
"""
|
|
66
|
+
selected = os.environ.get(ARROW_MEMORY_POOL_ENVVAR)
|
|
67
|
+
|
|
68
|
+
if selected == ARROW_MEMORY_POOL:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
raise RuntimeError(
|
|
72
|
+
f"{ARROW_MEMORY_POOL_ENVVAR} is set to '{selected}', but pysits "
|
|
73
|
+
f"requires '{ARROW_MEMORY_POOL}'.\n\n"
|
|
74
|
+
"The R `arrow` package and `pyarrow` each bundle their own build of "
|
|
75
|
+
"the libarrow C++ library. Loaded together, they must use the system "
|
|
76
|
+
"allocator to share buffers. With any other allocator, data sent "
|
|
77
|
+
"between R and Python is silently replaced by zeros.\n\n"
|
|
78
|
+
f"Unset {ARROW_MEMORY_POOL_ENVVAR}, or set it before starting Python:\n\n"
|
|
79
|
+
f" export {ARROW_MEMORY_POOL_ENVVAR}={ARROW_MEMORY_POOL}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def arrow_interop_error(observed: list) -> RuntimeError:
|
|
84
|
+
"""Build the error raised when an Arrow round-trip loses data.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
observed (list): Values read back from R for `PROBE_VALUES`.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
RuntimeError: Error describing the cause and how to resolve it.
|
|
91
|
+
"""
|
|
92
|
+
import pyarrow as pa
|
|
93
|
+
|
|
94
|
+
pool = pa.default_memory_pool().backend_name
|
|
95
|
+
|
|
96
|
+
return RuntimeError(
|
|
97
|
+
"Data sent to R is coming back corrupted, so pysits cannot run.\n\n"
|
|
98
|
+
f"Sent {PROBE_VALUES}, received {observed}.\n\n"
|
|
99
|
+
"The R `arrow` package and `pyarrow` each bundle their own build of "
|
|
100
|
+
"the libarrow C++ library. Loaded together, they must use the system "
|
|
101
|
+
f"allocator to share buffers, but pyarrow is using '{pool}'.\n\n"
|
|
102
|
+
"This happens when pyarrow is initialized before pysits with a "
|
|
103
|
+
"different allocator. Either import pysits before pyarrow, or set the "
|
|
104
|
+
"environment variable before starting Python:\n\n"
|
|
105
|
+
f" export {ARROW_MEMORY_POOL_ENVVAR}={ARROW_MEMORY_POOL}"
|
|
106
|
+
)
|
|
@@ -25,6 +25,9 @@ from pysits.backend.loaders import (
|
|
|
25
25
|
# Base - plot
|
|
26
26
|
r_fnc_plot = load_function_from_package("base::plot")
|
|
27
27
|
|
|
28
|
+
# Base - print
|
|
29
|
+
r_fnc_print = load_function_from_package("base::print")
|
|
30
|
+
|
|
28
31
|
# Base - set column
|
|
29
32
|
r_fnc_set_column = load_function_from_package("base::$<-")
|
|
30
33
|
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
"""backend packages."""
|
|
19
19
|
|
|
20
|
+
from pysits.backend.arrow import check_arrow_memory_pool
|
|
20
21
|
from pysits.backend.loaders import load_package
|
|
21
22
|
from pysits.settings import __sitsver__
|
|
22
23
|
|
|
@@ -33,4 +34,9 @@ r_pkg_leaflet = load_package("leaflet")
|
|
|
33
34
|
r_pkg_kohonen = load_package("kohonen")
|
|
34
35
|
r_pkg_sf = load_package("sf")
|
|
35
36
|
r_pkg_htmlwidgets = load_package("htmlwidgets")
|
|
37
|
+
|
|
38
|
+
# `arrow` brings a second libarrow build into the process, so the allocator has
|
|
39
|
+
# to be compatible before it is loaded. See `pysits.backend.arrow`.
|
|
40
|
+
check_arrow_memory_pool()
|
|
41
|
+
|
|
36
42
|
r_pkg_arrow = load_package("arrow")
|
|
@@ -94,6 +94,7 @@ def rpy2_fix_type_custom(
|
|
|
94
94
|
for i, arg in enumerate(args):
|
|
95
95
|
if i < len(param_names) and param_names[i] in converters:
|
|
96
96
|
converted_args.append(converters[param_names[i]](arg))
|
|
97
|
+
|
|
97
98
|
else:
|
|
98
99
|
converted_args.append(arg)
|
|
99
100
|
|
|
@@ -102,6 +103,7 @@ def rpy2_fix_type_custom(
|
|
|
102
103
|
for k, v in kwargs.items():
|
|
103
104
|
if k in converters:
|
|
104
105
|
converted_kwargs[k] = converters[k](v)
|
|
106
|
+
|
|
105
107
|
else:
|
|
106
108
|
converted_kwargs[k] = v
|
|
107
109
|
|
|
@@ -112,6 +114,47 @@ def rpy2_fix_type_custom(
|
|
|
112
114
|
return decorator
|
|
113
115
|
|
|
114
116
|
|
|
117
|
+
def rpy2_fix_type_except(
|
|
118
|
+
*names: str,
|
|
119
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
120
|
+
"""Convert arguments to R-compatible objects, keeping some untouched.
|
|
121
|
+
|
|
122
|
+
Behaves like `rpy2_fix_type`, except for the named keyword arguments,
|
|
123
|
+
which reach the wrapped function exactly as they were given. This is
|
|
124
|
+
for arguments that configure pysits itself instead of the R call.
|
|
125
|
+
Converting those to R objects would strip them of the Python interface
|
|
126
|
+
the wrapped function needs.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
*names (str): Names of the keyword arguments to leave untouched.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Callable: A decorator that wraps a function to convert its
|
|
133
|
+
arguments, minus the named ones.
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
>>> @rpy2_fix_type_except("image_args")
|
|
137
|
+
... def my_function(data, image_args=None, **kwargs):
|
|
138
|
+
... # `data` and `kwargs` are R objects, `image_args` is a dict
|
|
139
|
+
... pass
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
|
143
|
+
@functools.wraps(func)
|
|
144
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
145
|
+
kept = {name: kwargs.pop(name) for name in names if name in kwargs}
|
|
146
|
+
|
|
147
|
+
kwargs = fix_reserved_words_parameters(**kwargs)
|
|
148
|
+
converted_args = [convert_to_r(arg) for arg in args]
|
|
149
|
+
converted_kwargs = {k: convert_to_r(v) for k, v in kwargs.items()}
|
|
150
|
+
|
|
151
|
+
return func(*converted_args, **converted_kwargs, **kept)
|
|
152
|
+
|
|
153
|
+
return wrapper
|
|
154
|
+
|
|
155
|
+
return decorator
|
|
156
|
+
|
|
157
|
+
|
|
115
158
|
def function_call(r_function: Callable[P, R], output_wrapper: Callable[[R], T]):
|
|
116
159
|
"""Decorator function to call an R function and post-process the result.
|
|
117
160
|
|
|
@@ -36,6 +36,7 @@ from rpy2.robjects.conversion import localconverter
|
|
|
36
36
|
from rpy2.robjects.vectors import DataFrame as RDataFrame
|
|
37
37
|
from shapely import wkt
|
|
38
38
|
|
|
39
|
+
from pysits.backend.arrow import PROBE_COLUMN, PROBE_VALUES, arrow_interop_error
|
|
39
40
|
from pysits.backend.functions import r_fnc_class, r_fnc_set_column
|
|
40
41
|
from pysits.backend.pkgs import r_pkg_base, r_pkg_sf, r_pkg_sits
|
|
41
42
|
from pysits.models.frame import NestedFrame
|
|
@@ -90,6 +91,39 @@ def _sf_to_shapely(sf_object: RDataFrame) -> list:
|
|
|
90
91
|
#
|
|
91
92
|
# Arrow IPC helpers
|
|
92
93
|
#
|
|
94
|
+
def _check_arrow_interop() -> None:
|
|
95
|
+
"""Verify that Arrow data is not corrupted once R's libarrow is loaded.
|
|
96
|
+
|
|
97
|
+
R libarrow only initializes on first use, so a probe is sent through R to
|
|
98
|
+
force it, and ``pyarrow`` is then re-tested. A mismatched pair of libarrow
|
|
99
|
+
builds corrupts the conversion of Python objects to Arrow arrays, returning
|
|
100
|
+
zeros instead of raising, which would silently damage every nested column.
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
RuntimeError: If either transfer loses data.
|
|
104
|
+
"""
|
|
105
|
+
probe = PandasDataFrame({PROBE_COLUMN: PROBE_VALUES})
|
|
106
|
+
|
|
107
|
+
# Forces R libarrow to initialize, and checks the Python -> R direction
|
|
108
|
+
r_probe = rpy2_globalenv["pysits_read_ipc_raw"](
|
|
109
|
+
robjects.vectors.ByteVector(_dataframe_to_ipc_bytes(probe))
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Load content from R
|
|
113
|
+
observed = list(r_probe.rx2(PROBE_COLUMN))
|
|
114
|
+
|
|
115
|
+
# If it is different, then given an error
|
|
116
|
+
if observed != PROBE_VALUES:
|
|
117
|
+
raise arrow_interop_error(observed)
|
|
118
|
+
|
|
119
|
+
# Both libarrow builds are live now: check the conversion of Python objects
|
|
120
|
+
# to Arrow arrays, which is the path nested columns depend on.
|
|
121
|
+
observed = pa.array(PROBE_VALUES).to_pylist()
|
|
122
|
+
|
|
123
|
+
if observed != PROBE_VALUES:
|
|
124
|
+
raise arrow_interop_error(observed)
|
|
125
|
+
|
|
126
|
+
|
|
93
127
|
def _ensure_r_ipc_functions():
|
|
94
128
|
"""Define R-side IPC reader/writer/unnester functions once."""
|
|
95
129
|
if "pysits_write_ipc_raw" in rpy2_globalenv:
|
|
@@ -162,6 +196,8 @@ def _ensure_r_ipc_functions():
|
|
|
162
196
|
}
|
|
163
197
|
""")
|
|
164
198
|
|
|
199
|
+
_check_arrow_interop()
|
|
200
|
+
|
|
165
201
|
|
|
166
202
|
def _dataframe_to_ipc_bytes(df: PandasDataFrame) -> bytes:
|
|
167
203
|
"""Serialize a pandas DataFrame to Arrow IPC bytes.
|
|
@@ -85,19 +85,34 @@ Args:
|
|
|
85
85
|
area_spar (float): For embedding predictions, the smoothing parameter
|
|
86
86
|
passed to the spline fit (default `0.6`); higher values produce
|
|
87
87
|
smoother profiles.
|
|
88
|
+
image_args (dict): Geometry to render the figure with, with any of the
|
|
89
|
+
keys `width` and `height` (in inches), and `res` (in dots per
|
|
90
|
+
inch). See the notes on figure size below.
|
|
88
91
|
**kwargs (dict): Further specifications for the plot. The keywords
|
|
89
92
|
understood depend on the type of `x` (see below).
|
|
90
93
|
|
|
91
94
|
Returns:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
SITSPlot | SITSPlotList: The rendered figure. Objects that produce
|
|
96
|
+
several figures, samples with more than one band or label, for
|
|
97
|
+
example, return a `SITSPlotList`, which behaves as a sequence of
|
|
98
|
+
`SITSPlot`.
|
|
99
|
+
|
|
100
|
+
Maps of cubes yield color or B/W raster images (optionally
|
|
101
|
+
overlaid with segment boundaries for vector cubes). Probability,
|
|
102
|
+
uncertainty, and variance cubes yield per-class or per-pixel maps.
|
|
103
|
+
Classified cubes yield color maps where each pixel is colored by
|
|
104
|
+
its label. Chart-based plots (patterns, predictions, embeddings,
|
|
105
|
+
clusters, t-SNE, model diagnostics) render the corresponding plot.
|
|
106
|
+
|
|
107
|
+
How the figure reaches the page depends on where the code runs.
|
|
108
|
+
In a notebook, or a Quarto document using the `jupyter` engine,
|
|
109
|
+
the returned figure is rendered as the result of the cell. In
|
|
110
|
+
RStudio, or a Quarto document using the `knitr` engine, the
|
|
111
|
+
figures are handed to `knitr` as it renders the chunk, so each
|
|
112
|
+
one becomes a document figure of its own, with its own caption,
|
|
113
|
+
cross-reference, and layout. Everywhere else plotting displays
|
|
114
|
+
nothing by itself: use `save` to write the figure to a file, or
|
|
115
|
+
`show` to open it in an image viewer.
|
|
101
116
|
|
|
102
117
|
Notes:
|
|
103
118
|
The set of valid keyword arguments depends on the type of `x`;
|
|
@@ -115,3 +130,14 @@ Examples:
|
|
|
115
130
|
# Train a random forest model and plot its important variables
|
|
116
131
|
rf_model = sits_train(samples_modis_ndvi, ml_method=sits_rfor())
|
|
117
132
|
plot(rf_model)
|
|
133
|
+
|
|
134
|
+
# Render a larger figure, and write it to a file
|
|
135
|
+
figure = plot(rf_model, image_args={"width": 12, "height": 8})
|
|
136
|
+
figure.save("model.png")
|
|
137
|
+
|
|
138
|
+
# Set the size of every figure that follows
|
|
139
|
+
set_plot_options(width=8, height=5, dpi=150)
|
|
140
|
+
|
|
141
|
+
# Objects with several bands or labels produce one figure each
|
|
142
|
+
figures = plot(samples_l8_rondonia_2bands)
|
|
143
|
+
figures[0].save("first.png")
|