pysits 2.0.0.dev3__tar.gz → 2.0.0.dev5__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.
Files changed (216) hide show
  1. pysits-2.0.0.dev5/.claude/settings.local.json +5 -0
  2. pysits-2.0.0.dev5/.github/actions/setup-pysits/action.yml +43 -0
  3. pysits-2.0.0.dev5/.github/workflows/ruff.yaml +32 -0
  4. pysits-2.0.0.dev5/.github/workflows/tests.yaml +80 -0
  5. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/PKG-INFO +3 -2
  6. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pyproject.toml +4 -1
  7. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/backend/functions.py +3 -0
  8. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/common.py +27 -0
  9. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/decorators.py +43 -0
  10. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/plot.md +35 -9
  11. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/tuning.py +106 -34
  12. pysits-2.0.0.dev5/pysits/models/visual.py +567 -0
  13. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/colors.py +4 -2
  14. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/visualization.py +43 -43
  15. pysits-2.0.0.dev5/pysits/templates/plot.html +24 -0
  16. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/templates/tuning.html +5 -65
  17. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/visualization/__init__.py +10 -2
  18. pysits-2.0.0.dev5/pysits/visualization/base.py +189 -0
  19. pysits-2.0.0.dev5/pysits/visualization/device.py +86 -0
  20. pysits-2.0.0.dev5/pysits/visualization/host.py +64 -0
  21. pysits-2.0.0.dev5/pysits/visualization/options.py +292 -0
  22. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/conftest.py +26 -15
  23. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_classification.py +2 -2
  24. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_conversions.py +19 -0
  25. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_embedding.py +3 -3
  26. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_som.py +8 -9
  27. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_tuning.py +46 -11
  28. pysits-2.0.0.dev5/tests/test_visual_models.py +528 -0
  29. pysits-2.0.0.dev5/tests/test_visualization.py +426 -0
  30. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/uv.lock +1468 -512
  31. pysits-2.0.0.dev3/.claude/settings.local.json +0 -16
  32. pysits-2.0.0.dev3/.github/workflows/ruff.yaml +0 -42
  33. pysits-2.0.0.dev3/.github/workflows/tests.yaml +0 -96
  34. pysits-2.0.0.dev3/pysits/visualization/base.py +0 -177
  35. pysits-2.0.0.dev3/pysits/visualization/image.py +0 -48
  36. pysits-2.0.0.dev3/pysits/visualization/tmap.py +0 -85
  37. pysits-2.0.0.dev3/tests/test_visualization.py +0 -181
  38. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/.gitattributes +0 -0
  39. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/.gitignore +0 -0
  40. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/.pre-commit-config.yaml +0 -0
  41. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/LICENSE +0 -0
  42. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/README.md +0 -0
  43. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/conda/meta.yaml +0 -0
  44. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/__init__.py +0 -0
  45. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/backend/__init__.py +0 -0
  46. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/backend/arrow.py +0 -0
  47. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/backend/loaders.py +0 -0
  48. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/backend/pkgs.py +0 -0
  49. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/__init__.py +0 -0
  50. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/clojure.py +0 -0
  51. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/dsl/__init__.py +0 -0
  52. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/dsl/base.py +0 -0
  53. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/dsl/mask.py +0 -0
  54. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/dsl/tuning.py +0 -0
  55. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/stac.py +0 -0
  56. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/tibble.py +0 -0
  57. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/vector.py +0 -0
  58. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/conversions/xarray.py +0 -0
  59. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/__init__.py +0 -0
  60. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/impute_linear.md +0 -0
  61. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/impute_mean.md +0 -0
  62. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/impute_mean_window.md +0 -0
  63. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/impute_median.md +0 -0
  64. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_accuracy.md +0 -0
  65. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_accuracy_summary.md +0 -0
  66. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_add_base_cube.md +0 -0
  67. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_apply.md +0 -0
  68. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_as_geopandas.md +0 -0
  69. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_bands.md +0 -0
  70. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_barlow_twins.md +0 -0
  71. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_bbox.md +0 -0
  72. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_classify.md +0 -0
  73. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_clean.md +0 -0
  74. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_cluster_clean.md +0 -0
  75. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_cluster_dendro.md +0 -0
  76. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_cluster_frequency.md +0 -0
  77. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_colors.md +0 -0
  78. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_colors_qgis.md +0 -0
  79. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_colors_reset.md +0 -0
  80. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_colors_set.md +0 -0
  81. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_colors_show.md +0 -0
  82. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_combine_predictions.md +0 -0
  83. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_confidence_sampling.md +0 -0
  84. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_config.md +0 -0
  85. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_config_show.md +0 -0
  86. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_config_user_file.md +0 -0
  87. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_contrastive_learning.md +0 -0
  88. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_cube.md +0 -0
  89. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_cube_copy.md +0 -0
  90. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_encode.md +0 -0
  91. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_formula_linear.md +0 -0
  92. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_formula_logref.md +0 -0
  93. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_geo_dist.md +0 -0
  94. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_get_class.md +0 -0
  95. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_get_data.md +0 -0
  96. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_get_probs.md +0 -0
  97. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_kfold_validate.md +0 -0
  98. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_label_classification.md +0 -0
  99. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_labels.md +0 -0
  100. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_labels_summary.md +0 -0
  101. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_lightgbm.md +0 -0
  102. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_lighttae.md +0 -0
  103. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_list_collections.md +0 -0
  104. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_lstm_fcn.md +0 -0
  105. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_merge.md +0 -0
  106. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_mixture_model.md +0 -0
  107. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_mlp.md +0 -0
  108. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_model_export.md +0 -0
  109. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_mosaic.md +0 -0
  110. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_parallel.md +0 -0
  111. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_patterns.md +0 -0
  112. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_pre_train.md +0 -0
  113. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_pred_features.md +0 -0
  114. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_pred_normalize.md +0 -0
  115. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_pred_references.md +0 -0
  116. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_pred_sample.md +0 -0
  117. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_predictors.md +0 -0
  118. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_random_sampling.md +0 -0
  119. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_reclassify.md +0 -0
  120. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_reduce.md +0 -0
  121. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_reduce_imbalance.md +0 -0
  122. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_regularize.md +0 -0
  123. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_resnet.md +0 -0
  124. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_rfor.md +0 -0
  125. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_roi_to_tiles.md +0 -0
  126. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_sample.md +0 -0
  127. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_sampling_design.md +0 -0
  128. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_sankey.md +0 -0
  129. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_segment.md +0 -0
  130. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_select.md +0 -0
  131. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_show_prediction.md +0 -0
  132. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_slic.md +0 -0
  133. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_smooth.md +0 -0
  134. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_snic.md +0 -0
  135. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_som_clean_samples.md +0 -0
  136. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_som_evaluate_cluster.md +0 -0
  137. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_som_map.md +0 -0
  138. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_som_remove_samples.md +0 -0
  139. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_ssl_lejepa.md +0 -0
  140. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_ssl_mae.md +0 -0
  141. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_ssl_vicreg.md +0 -0
  142. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_stats.md +0 -0
  143. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_stratified_sampling.md +0 -0
  144. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_svm.md +0 -0
  145. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_tae.md +0 -0
  146. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_tempcnn.md +0 -0
  147. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_texture.md +0 -0
  148. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_tiles_to_roi.md +0 -0
  149. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_timeline.md +0 -0
  150. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_timeseries_to_csv.md +0 -0
  151. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_to_csv.md +0 -0
  152. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_to_xlsx.md +0 -0
  153. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_train.md +0 -0
  154. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_tuning.md +0 -0
  155. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_tuning_hparams.md +0 -0
  156. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_uncertainty.md +0 -0
  157. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_uncertainty_sampling.md +0 -0
  158. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_validate.md +0 -0
  159. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_variance.md +0 -0
  160. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_view.md +0 -0
  161. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/sits_xgboost.md +0 -0
  162. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/content/summary.md +0 -0
  163. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/docs/decorators.py +0 -0
  164. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/extras/__init__.py +0 -0
  165. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/extras/earthdatalogin.py +0 -0
  166. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/extras/torch.py +0 -0
  167. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/jinja.py +0 -0
  168. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/__init__.py +0 -0
  169. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/base.py +0 -0
  170. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/__init__.py +0 -0
  171. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/accuracy.py +0 -0
  172. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/base.py +0 -0
  173. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/cube.py +0 -0
  174. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/frame.py +0 -0
  175. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/frame_accessor.py +0 -0
  176. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/matrix.py +0 -0
  177. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/table.py +0 -0
  178. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/ts.py +0 -0
  179. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/data/vector.py +0 -0
  180. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/frame.py +0 -0
  181. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/ml.py +0 -0
  182. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/models/resolver.py +0 -0
  183. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/settings.py +0 -0
  184. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/__init__.py +0 -0
  185. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/config.py +0 -0
  186. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/context.py +0 -0
  187. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/cube.py +0 -0
  188. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/data.py +0 -0
  189. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/exporters/__init__.py +0 -0
  190. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/exporters/files.py +0 -0
  191. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/exporters/sf.py +0 -0
  192. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/exporters/xarray.py +0 -0
  193. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/impute.py +0 -0
  194. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/ml.py +0 -0
  195. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/segment.py +0 -0
  196. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/tiles.py +0 -0
  197. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/ts.py +0 -0
  198. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/tuning.py +0 -0
  199. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/sits/utils.py +0 -0
  200. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/templates/cube.html +0 -0
  201. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/pysits/visualization/leaflet.py +0 -0
  202. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_config.py +0 -0
  203. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_cube.py +0 -0
  204. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_data.py +0 -0
  205. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_dsl.py +0 -0
  206. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_exporters.py +0 -0
  207. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_exporters_geopandas.py +0 -0
  208. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_exporters_xarray.py +0 -0
  209. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_extras.py +0 -0
  210. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_indexing.py +0 -0
  211. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_ml_models.py +0 -0
  212. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_resolver.py +0 -0
  213. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_rl_models.py +0 -0
  214. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_tiles.py +0 -0
  215. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_utils.py +0 -0
  216. {pysits-2.0.0.dev3 → pysits-2.0.0.dev5}/tests/test_validation.py +0 -0
@@ -0,0 +1,5 @@
1
+ {
2
+ "disabledMcpjsonServers": [
3
+ "geo-infrastructure"
4
+ ]
5
+ }
@@ -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.4
1
+ Metadata-Version: 2.5
2
2
  Name: pysits
3
- Version: 2.0.0.dev3
3
+ Version: 2.0.0.dev5
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
@@ -29,6 +29,7 @@ Provides-Extra: dev
29
29
  Requires-Dist: affine>=2.4.0; extra == 'dev'
30
30
  Requires-Dist: cloudpickle>=3.1.1; extra == 'dev'
31
31
  Requires-Dist: dask>=2025.3.0; extra == 'dev'
32
+ Requires-Dist: ipykernel>=6.29.0; extra == 'dev'
32
33
  Requires-Dist: odc-stac>=0.5.3; extra == 'dev'
33
34
  Requires-Dist: planetary-computer>=1.0.0; extra == 'dev'
34
35
  Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
@@ -17,7 +17,7 @@
17
17
 
18
18
  [project]
19
19
  name = "pysits"
20
- version = "2.0.0.dev3"
20
+ version = "2.0.0.dev5"
21
21
  description = "Python wrapper for the sits R package"
22
22
  readme = "README.md"
23
23
  requires-python = ">=3.10,<4"
@@ -98,6 +98,9 @@ dev = [
98
98
  # The test suite covers the xarray exporters and reads rasters directly
99
99
  "pysits[xarray]",
100
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",
101
104
  ]
102
105
 
103
106
  [tool.ruff]
@@ -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
 
@@ -58,6 +58,12 @@ TYPE_CONVERSIONS = {
58
58
  EPOCH_START = date(1970, 1, 1)
59
59
 
60
60
 
61
+ #
62
+ # Prefixes of R expressions deparsed as text
63
+ #
64
+ R_DEPARSED_PREFIXES = ("c(", "list(")
65
+
66
+
61
67
  #
62
68
  # Base utilities
63
69
  #
@@ -197,6 +203,27 @@ def eval_r_language(obj):
197
203
  return obj
198
204
 
199
205
 
206
+ def eval_r_deparsed(obj):
207
+ """Evaluate a deparsed R expression.
208
+
209
+ Args:
210
+ obj: The R object to evaluate.
211
+
212
+ Returns:
213
+ The evaluated R object. Objects that are not deparsed R expressions are
214
+ returned unchanged.
215
+ """
216
+ value = obj
217
+
218
+ if isinstance(value, ro.StrVector) and len(value) == 1:
219
+ value = value[0]
220
+
221
+ if isinstance(value, str) and value.startswith(R_DEPARSED_PREFIXES):
222
+ return ro.r(value)
223
+
224
+ return obj
225
+
226
+
200
227
  def convert_to_python(obj, as_type="str"):
201
228
  """Convert an R object to a Python representation.
202
229
 
@@ -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
 
@@ -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
- None: A plot appropriate to the type of `x` is drawn. Maps of cubes
93
- yield color or B/W raster images (optionally overlaid with segment
94
- boundaries for vector cubes); probability, uncertainty, and
95
- variance cubes yield per-class or per-pixel maps; classified cubes
96
- yield color maps where each pixel is colored by its label.
97
- Chart-based plots (patterns, predictions, embeddings, clusters,
98
- t-SNE, model diagnostics) render the corresponding plot. Some
99
- methods (accuracy tables, SOM diagnostics, model summaries) are
100
- called only for their side effect of drawing the plot.
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")
@@ -21,14 +21,65 @@ from collections.abc import Callable
21
21
  from typing import Any
22
22
 
23
23
  from rpy2.rinterface_lib.sexp import NULLType
24
- from rpy2.robjects.vectors import ListVector
25
-
26
- from pysits.conversions.common import convert_to_python, eval_r_language
24
+ from rpy2.robjects.vectors import (
25
+ BoolVector,
26
+ FloatVector,
27
+ IntVector,
28
+ ListVector,
29
+ StrVector,
30
+ )
31
+
32
+ from pysits.conversions.common import (
33
+ R_DEPARSED_PREFIXES,
34
+ convert_to_python,
35
+ eval_r_deparsed,
36
+ eval_r_language,
37
+ )
27
38
  from pysits.models.data.base import SITSData
28
39
  from pysits.models.data.matrix import SITSConfusionMatrix
29
40
  from pysits.models.data.ts import SITSTimeSeriesModel
30
41
 
42
+ #
43
+ # Python types associated with each R vector type
44
+ #
45
+ HPARAM_TYPES = {
46
+ BoolVector: "bool",
47
+ IntVector: "int",
48
+ FloatVector: "float",
49
+ StrVector: "str",
50
+ }
51
+
52
+
53
+ #
54
+ # Tuning results columns that are not hyper-parameters
55
+ #
56
+ TUNING_METRICS = ("accuracy", "kappa", "acc", "samples_validation")
57
+
58
+
59
+ #
60
+ # Utilities
61
+ #
62
+ def convert_hparam(value: Any) -> Any:
63
+ """Convert a hyper-parameter value from R to Python.
64
+
65
+ Args:
66
+ value: The hyper-parameter value to convert.
67
+
68
+ Returns:
69
+ Any: Converted Python object.
70
+ """
71
+ value = eval_r_deparsed(value)
72
+
73
+ for vector_type, as_type in HPARAM_TYPES.items():
74
+ if isinstance(value, vector_type):
75
+ return convert_to_python(value, as_type=as_type)
31
76
 
77
+ return convert_to_python(value)
78
+
79
+
80
+ #
81
+ # Classes
82
+ #
32
83
  class SITSTuningResults(SITSData):
33
84
  """Base class for sits accuracy results."""
34
85
 
@@ -62,14 +113,13 @@ class SITSTuningResults(SITSData):
62
113
  if isinstance(values, NULLType):
63
114
  return [None for i in range(len(self._instance.rx2("accuracy")))]
64
115
 
65
- # Check if value is a vector as string
116
+ # Check if value is a deparsed R expression (e.g., "c(128, 128, 128)")
66
117
  is_vector_as_string = any(
67
- isinstance(x, str) and (x.startswith("c(") or x.startswith("list("))
68
- for x in values
118
+ isinstance(x, str) and x.startswith(R_DEPARSED_PREFIXES) for x in values
69
119
  )
70
120
 
71
121
  if is_vector_as_string:
72
- return list(values)
122
+ return [transform_func(eval_r_deparsed(x)) for x in values]
73
123
 
74
124
  if isinstance(values, ListVector):
75
125
  return [
@@ -138,47 +188,47 @@ class SITSTuningResults(SITSData):
138
188
  ]
139
189
 
140
190
  @property
141
- def cnn_layers(self) -> list[float]:
191
+ def cnn_layers(self) -> list[list[float]]:
142
192
  """Get the CNN layer configurations from the tuning results.
143
193
 
144
194
  Returns:
145
- A list of float values representing the CNN layer configurations.
195
+ A list with the CNN layer configuration of each trial.
146
196
  """
147
197
  return self._convert_from_r_list("cnn_layers", "float")
148
198
 
149
199
  @property
150
- def cnn_kernels(self) -> list[float]:
200
+ def cnn_kernels(self) -> list[list[float]]:
151
201
  """Get the CNN kernel configurations from the tuning results.
152
202
 
153
203
  Returns:
154
- A list of float values representing the CNN kernel configurations.
204
+ A list with the CNN kernel configuration of each trial.
155
205
  """
156
206
  return self._convert_from_r_list("cnn_kernels", "float")
157
207
 
158
208
  @property
159
- def cnn_dropout_rates(self) -> list[float]:
209
+ def cnn_dropout_rates(self) -> list[list[float]]:
160
210
  """Get the CNN dropout rates from the tuning results.
161
211
 
162
212
  Returns:
163
- A list of float values representing the CNN dropout rates.
213
+ A list with the CNN dropout rates of each trial.
164
214
  """
165
215
  return self._convert_from_r_list("cnn_dropout_rates", "float")
166
216
 
167
217
  @property
168
- def dense_layer_nodes(self) -> list[float]:
218
+ def dense_layer_nodes(self) -> list[list[float]]:
169
219
  """Get the dense layer node configurations from the tuning results.
170
220
 
171
221
  Returns:
172
- A list of float values representing the dense layer node configurations.
222
+ A list with the dense layer node configuration of each trial.
173
223
  """
174
224
  return self._convert_from_r_list("dense_layer_nodes", "float")
175
225
 
176
226
  @property
177
- def dense_layer_dropout_rate(self) -> list[float]:
227
+ def dense_layer_dropout_rate(self) -> list[list[float]]:
178
228
  """Get the dense layer dropout rates from the tuning results.
179
229
 
180
230
  Returns:
181
- A list of float values representing the dense layer dropout rates.
231
+ A list with the dense layer dropout rates of each trial.
182
232
  """
183
233
  return self._convert_from_r_list("dense_layer_dropout_rate", "float")
184
234
 
@@ -219,40 +269,33 @@ class SITSTuningResults(SITSData):
219
269
  return [eval_r_language(x).rclass[0] for x in self._instance.rx2("optimizer")]
220
270
 
221
271
  @property
222
- def opt_hparams(self) -> list[dict[str, float]]:
272
+ def opt_hparams(self) -> list[dict[str, Any]]:
223
273
  """Get the optimizer hyperparameters from the tuning results.
224
274
 
225
275
  Returns:
226
276
  A list of dictionaries containing optimizer hyperparameters.
227
- Each dictionary maps parameter names to their float values.
277
+ Each dictionary maps parameter names to their values.
228
278
  """
229
- # Results
230
- results = []
231
-
232
- # Convert to Python
233
- values = self._convert_from_r_list("opt_hparams", "float")
234
-
235
- # Merge dict results
236
- for value in values:
237
- results.append({k: v for d in value for k, v in d.items()})
238
-
239
- return results
279
+ return self._convert_attribute(
280
+ "opt_hparams",
281
+ lambda x: {str(k): convert_hparam(v) for k, v in x.items()},
282
+ )
240
283
 
241
284
  @property
242
- def lr_decay_epochs(self) -> list[float]:
285
+ def lr_decay_epochs(self) -> list[list[float]]:
243
286
  """Get the learning rate decay epochs from the tuning results.
244
287
 
245
288
  Returns:
246
- A list of float values representing the learning rate decay epochs.
289
+ A list with the learning rate decay epochs of each trial.
247
290
  """
248
291
  return self._convert_from_r_list("lr_decay_epochs", "float")
249
292
 
250
293
  @property
251
- def lr_decay_rate(self) -> list[float]:
294
+ def lr_decay_rate(self) -> list[list[float]]:
252
295
  """Get the learning rate decay rates from the tuning results.
253
296
 
254
297
  Returns:
255
- A list of float values representing the learning rate decay rates.
298
+ A list with the learning rate decay rates of each trial.
256
299
  """
257
300
  return self._convert_from_r_list("lr_decay_rate", "float")
258
301
 
@@ -283,9 +326,38 @@ class SITSTuningResults(SITSData):
283
326
  """
284
327
  return self._convert_from_r_list("verbose", "bool")
285
328
 
329
+ @property
330
+ def hparams(self) -> dict[str, list[Any]]:
331
+ """Get all the hyper-parameters tuned, one value per trial.
332
+
333
+ Returns:
334
+ A dictionary mapping each hyper-parameter name to its values.
335
+ """
336
+ return {
337
+ name: getattr(self, name)
338
+ for name in self._instance.names
339
+ if name not in TUNING_METRICS
340
+ }
341
+
286
342
  #
287
343
  # Dunder methods
288
344
  #
345
+ def __getattr__(self, name: str) -> list[Any]:
346
+ """Get a hyper-parameter with no property associated with it.
347
+
348
+ Args:
349
+ name: The name of the hyper-parameter to get.
350
+
351
+ Returns:
352
+ A list with the hyper-parameter values, one per trial.
353
+ """
354
+ if name.startswith("_") or name not in self._instance.names:
355
+ raise AttributeError(
356
+ f"'{type(self).__name__}' object has no attribute '{name}'"
357
+ )
358
+
359
+ return self._convert_attribute(name, convert_hparam)
360
+
289
361
  def __str__(self):
290
362
  """String representation."""
291
363
  return str(self._instance)