RSATSIModel 2.5.0__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.
- rsatsimodel-2.5.0/LICENSE +3 -0
- rsatsimodel-2.5.0/MANIFEST.in +3 -0
- rsatsimodel-2.5.0/PKG-INFO +159 -0
- rsatsimodel-2.5.0/README.md +143 -0
- rsatsimodel-2.5.0/RSATSIModel.egg-info/PKG-INFO +159 -0
- rsatsimodel-2.5.0/RSATSIModel.egg-info/SOURCES.txt +31 -0
- rsatsimodel-2.5.0/RSATSIModel.egg-info/dependency_links.txt +1 -0
- rsatsimodel-2.5.0/RSATSIModel.egg-info/requires.txt +5 -0
- rsatsimodel-2.5.0/RSATSIModel.egg-info/top_level.txt +1 -0
- rsatsimodel-2.5.0/atsimodel/__init__.py +5 -0
- rsatsimodel-2.5.0/atsimodel/atsifunction.py +20 -0
- rsatsimodel-2.5.0/atsimodel/chl_models.py +37 -0
- rsatsimodel-2.5.0/atsimodel/classification.py +17 -0
- rsatsimodel-2.5.0/atsimodel/core.py +14 -0
- rsatsimodel-2.5.0/atsimodel/io.py +30 -0
- rsatsimodel-2.5.0/atsimodel/metrics.py +41 -0
- rsatsimodel-2.5.0/atsimodel/olci_features.py +55 -0
- rsatsimodel-2.5.0/atsimodel/plotting.py +19 -0
- rsatsimodel-2.5.0/atsimodel/preprocessing.py +29 -0
- rsatsimodel-2.5.0/atsimodel/rs_core.py +92 -0
- rsatsimodel-2.5.0/atsimodel/thresholds.py +62 -0
- rsatsimodel-2.5.0/atsimodel/utils.py +28 -0
- rsatsimodel-2.5.0/atsimodel/validation.py +70 -0
- rsatsimodel-2.5.0/examples/sample_atsi_input.xlsx +0 -0
- rsatsimodel-2.5.0/examples/sample_rs_atsi_input.csv +25 -0
- rsatsimodel-2.5.0/examples/sample_rs_atsi_input.txt +25 -0
- rsatsimodel-2.5.0/examples/sample_rs_atsi_input.xlsx +0 -0
- rsatsimodel-2.5.0/examples/sample_rs_atsi_rhow_input.csv +25 -0
- rsatsimodel-2.5.0/examples/sample_rs_atsi_rhow_input.txt +25 -0
- rsatsimodel-2.5.0/examples/sample_rs_atsi_rhow_input.xlsx +0 -0
- rsatsimodel-2.5.0/pyproject.toml +21 -0
- rsatsimodel-2.5.0/setup.cfg +4 -0
- rsatsimodel-2.5.0/setup.py +2 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: RSATSIModel
|
|
3
|
+
Version: 2.5.0
|
|
4
|
+
Summary: Python package for computing ATSI and RS-ATSI using CHL, SAL, and Sentinel-3 OLCI-style data.
|
|
5
|
+
Author-email: Dr Md Galal Uddin <jalaluddinbd1987@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pandas>=1.5.0
|
|
11
|
+
Requires-Dist: numpy>=1.23.0
|
|
12
|
+
Requires-Dist: matplotlib>=3.6.0
|
|
13
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
14
|
+
Requires-Dist: scikit-learn>=1.2.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# REMOTE SENSING (RS)-DRIVEN ATSIModel
|
|
18
|
+
|
|
19
|
+
## Assessment of Trophic Status Index (ATSI) and RS-ATSI Python Package
|
|
20
|
+
|
|
21
|
+
**ATSIModel** is an open-source Python package for computing:
|
|
22
|
+
1. **ATSI** from in-situ **chlorophyll-a (CHL)** and **salinity (SAL)** data.
|
|
23
|
+
2. **RS-ATSI** from **Sentinel-3 OLCI style remote-sensing inputs**, with CHL prediction, ATSI derivation, and a structured validation framework.
|
|
24
|
+
|
|
25
|
+
The package is designed for **transitional, estuarine, and coastal waters**, and supports both standard ATSI computation and remote-sensing based ATSI workflows.
|
|
26
|
+
|
|
27
|
+
## Author
|
|
28
|
+
|
|
29
|
+
**Dr Md Galal Uddin**
|
|
30
|
+
School of Engineering, University of Galway, Ireland
|
|
31
|
+
Email: **jalaluddinbd1987@gmail.com**
|
|
32
|
+
|
|
33
|
+
### Research Profiles
|
|
34
|
+
- **Google Scholar**: https://scholar.google.com/citations?user=g6xaAOkAAAAJ&hl=en
|
|
35
|
+
|
|
36
|
+
## Scientific Background
|
|
37
|
+
|
|
38
|
+
ATSI is implemented as a **CHL-driven, salinity-conditioned trophic scoring framework**.
|
|
39
|
+
This package extends that framework into a **remote-sensing compatible RS-ATSI workflow**.
|
|
40
|
+
|
|
41
|
+
## Standard ATSI workflow
|
|
42
|
+
|
|
43
|
+
Required columns:
|
|
44
|
+
|
|
45
|
+
| Column | Description | Unit |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| CHL | Chlorophyll-a concentration | µg/L |
|
|
48
|
+
| SAL | Salinity | PSU |
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from atsimodel.core import run_atsi
|
|
54
|
+
df = run_atsi("examples/sample_atsi_input.xlsx")
|
|
55
|
+
print(df.head())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Export:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from atsimodel.utils import export_single
|
|
62
|
+
export_single(df, out_base="outputs/ATSI_results", formats=("xlsx", "csv", "txt", "json"))
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## RS-ATSI workflow
|
|
66
|
+
|
|
67
|
+
Recommended input columns:
|
|
68
|
+
- `CHL`
|
|
69
|
+
- `SAL`
|
|
70
|
+
- `Rhow_1`, `Rhow_2`, `Rhow_3`, `Rhow_4`, `Rhow_5`, `Rhow_6`, `Rhow_7`, `Rhow_8`, `Rhow_9`, `Rhow_10`, `Rhow_11`
|
|
71
|
+
|
|
72
|
+
Recommended optional columns:
|
|
73
|
+
- `SITE`
|
|
74
|
+
- `SEASON`
|
|
75
|
+
- `ZONE`
|
|
76
|
+
- `SPLIT`
|
|
77
|
+
|
|
78
|
+
Example RS table:
|
|
79
|
+
|
|
80
|
+
| SITE | SEASON | ZONE | SPLIT | CHL | SAL | Rhow_1 | Rhow_2 | Rhow_3 | Rhow_4 | Rhow_5 | Rhow_6 | Rhow_7 | Rhow_8 | Rhow_9 | Rhow_10 | Rhow_11 |
|
|
81
|
+
|---|---|---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
82
|
+
| S1 | Winter | Upper | TRAIN | 8.0 | 31.0 | 0.011 | 0.012 | 0.013 | 0.014 | 0.015 | 0.016 | 0.017 | 0.018 | 0.019 | 0.020 | 0.021 |
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from atsimodel.rs_core import run_rs_atsi_pipeline
|
|
88
|
+
|
|
89
|
+
results = run_rs_atsi_pipeline(
|
|
90
|
+
input_file="examples/sample_rs_atsi_input.xlsx",
|
|
91
|
+
output_dir="outputs/RS_ATSI_demo"
|
|
92
|
+
)
|
|
93
|
+
print(results["chl_validation_overall"])
|
|
94
|
+
print(results["atsi_validation_overall"])
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Validation design
|
|
98
|
+
|
|
99
|
+
### 1. CHL validation
|
|
100
|
+
- R²
|
|
101
|
+
- RMSE
|
|
102
|
+
- MAE
|
|
103
|
+
- Bias
|
|
104
|
+
- train / test / independent validation
|
|
105
|
+
- site-wise validation
|
|
106
|
+
|
|
107
|
+
### 2. ATSI validation
|
|
108
|
+
- RS-ATSI vs in-situ ATSI
|
|
109
|
+
- confusion matrix by trophic class
|
|
110
|
+
- agreement by station and season
|
|
111
|
+
|
|
112
|
+
### 3. Spatial validation
|
|
113
|
+
- hotspot consistency
|
|
114
|
+
- estuarine gradient realism
|
|
115
|
+
- upper / middle / lower estuary comparison
|
|
116
|
+
|
|
117
|
+
## Exported outputs
|
|
118
|
+
|
|
119
|
+
The package exports:
|
|
120
|
+
- Excel workbook
|
|
121
|
+
- CSV files
|
|
122
|
+
- TXT files
|
|
123
|
+
- JSON files
|
|
124
|
+
|
|
125
|
+
Typical output tables include:
|
|
126
|
+
- `predictions_all`
|
|
127
|
+
- `chl_validation_overall`
|
|
128
|
+
- `chl_validation_by_site_test`
|
|
129
|
+
- `atsi_validation_overall`
|
|
130
|
+
- `atsi_validation_by_site_test`
|
|
131
|
+
- `atsi_confusion_matrix_test`
|
|
132
|
+
- `station_season_agreement_test`
|
|
133
|
+
- `spatial_zone_summary_test`
|
|
134
|
+
|
|
135
|
+
## Scientific notes
|
|
136
|
+
|
|
137
|
+
- ATSI remains **CHL-driven**
|
|
138
|
+
- SAL is used to determine **threshold context**
|
|
139
|
+
- ATSI values are clipped to **0–100**
|
|
140
|
+
- Current class translation is operational:
|
|
141
|
+
- Unpolluted
|
|
142
|
+
- Moderate
|
|
143
|
+
- Eutrophic
|
|
144
|
+
- Hypertrophic
|
|
145
|
+
|
|
146
|
+
## Limitations
|
|
147
|
+
|
|
148
|
+
This release does not yet include:
|
|
149
|
+
- uncertainty quantification
|
|
150
|
+
- prediction intervals
|
|
151
|
+
- raster ingestion
|
|
152
|
+
- geospatial map production
|
|
153
|
+
- SHAP explainability
|
|
154
|
+
|
|
155
|
+
## Contact
|
|
156
|
+
|
|
157
|
+
**Dr Md Galal Uddin**
|
|
158
|
+
School of Engineering, University of Galway, Ireland
|
|
159
|
+
Email: **jalaluddinbd1987@gmail.com**
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# REMOTE SENSING (RS)-DRIVEN ATSIModel
|
|
2
|
+
|
|
3
|
+
## Assessment of Trophic Status Index (ATSI) and RS-ATSI Python Package
|
|
4
|
+
|
|
5
|
+
**ATSIModel** is an open-source Python package for computing:
|
|
6
|
+
1. **ATSI** from in-situ **chlorophyll-a (CHL)** and **salinity (SAL)** data.
|
|
7
|
+
2. **RS-ATSI** from **Sentinel-3 OLCI style remote-sensing inputs**, with CHL prediction, ATSI derivation, and a structured validation framework.
|
|
8
|
+
|
|
9
|
+
The package is designed for **transitional, estuarine, and coastal waters**, and supports both standard ATSI computation and remote-sensing based ATSI workflows.
|
|
10
|
+
|
|
11
|
+
## Author
|
|
12
|
+
|
|
13
|
+
**Dr Md Galal Uddin**
|
|
14
|
+
School of Engineering, University of Galway, Ireland
|
|
15
|
+
Email: **jalaluddinbd1987@gmail.com**
|
|
16
|
+
|
|
17
|
+
### Research Profiles
|
|
18
|
+
- **Google Scholar**: https://scholar.google.com/citations?user=g6xaAOkAAAAJ&hl=en
|
|
19
|
+
|
|
20
|
+
## Scientific Background
|
|
21
|
+
|
|
22
|
+
ATSI is implemented as a **CHL-driven, salinity-conditioned trophic scoring framework**.
|
|
23
|
+
This package extends that framework into a **remote-sensing compatible RS-ATSI workflow**.
|
|
24
|
+
|
|
25
|
+
## Standard ATSI workflow
|
|
26
|
+
|
|
27
|
+
Required columns:
|
|
28
|
+
|
|
29
|
+
| Column | Description | Unit |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| CHL | Chlorophyll-a concentration | µg/L |
|
|
32
|
+
| SAL | Salinity | PSU |
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from atsimodel.core import run_atsi
|
|
38
|
+
df = run_atsi("examples/sample_atsi_input.xlsx")
|
|
39
|
+
print(df.head())
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Export:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from atsimodel.utils import export_single
|
|
46
|
+
export_single(df, out_base="outputs/ATSI_results", formats=("xlsx", "csv", "txt", "json"))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## RS-ATSI workflow
|
|
50
|
+
|
|
51
|
+
Recommended input columns:
|
|
52
|
+
- `CHL`
|
|
53
|
+
- `SAL`
|
|
54
|
+
- `Rhow_1`, `Rhow_2`, `Rhow_3`, `Rhow_4`, `Rhow_5`, `Rhow_6`, `Rhow_7`, `Rhow_8`, `Rhow_9`, `Rhow_10`, `Rhow_11`
|
|
55
|
+
|
|
56
|
+
Recommended optional columns:
|
|
57
|
+
- `SITE`
|
|
58
|
+
- `SEASON`
|
|
59
|
+
- `ZONE`
|
|
60
|
+
- `SPLIT`
|
|
61
|
+
|
|
62
|
+
Example RS table:
|
|
63
|
+
|
|
64
|
+
| SITE | SEASON | ZONE | SPLIT | CHL | SAL | Rhow_1 | Rhow_2 | Rhow_3 | Rhow_4 | Rhow_5 | Rhow_6 | Rhow_7 | Rhow_8 | Rhow_9 | Rhow_10 | Rhow_11 |
|
|
65
|
+
|---|---|---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
66
|
+
| S1 | Winter | Upper | TRAIN | 8.0 | 31.0 | 0.011 | 0.012 | 0.013 | 0.014 | 0.015 | 0.016 | 0.017 | 0.018 | 0.019 | 0.020 | 0.021 |
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from atsimodel.rs_core import run_rs_atsi_pipeline
|
|
72
|
+
|
|
73
|
+
results = run_rs_atsi_pipeline(
|
|
74
|
+
input_file="examples/sample_rs_atsi_input.xlsx",
|
|
75
|
+
output_dir="outputs/RS_ATSI_demo"
|
|
76
|
+
)
|
|
77
|
+
print(results["chl_validation_overall"])
|
|
78
|
+
print(results["atsi_validation_overall"])
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Validation design
|
|
82
|
+
|
|
83
|
+
### 1. CHL validation
|
|
84
|
+
- R²
|
|
85
|
+
- RMSE
|
|
86
|
+
- MAE
|
|
87
|
+
- Bias
|
|
88
|
+
- train / test / independent validation
|
|
89
|
+
- site-wise validation
|
|
90
|
+
|
|
91
|
+
### 2. ATSI validation
|
|
92
|
+
- RS-ATSI vs in-situ ATSI
|
|
93
|
+
- confusion matrix by trophic class
|
|
94
|
+
- agreement by station and season
|
|
95
|
+
|
|
96
|
+
### 3. Spatial validation
|
|
97
|
+
- hotspot consistency
|
|
98
|
+
- estuarine gradient realism
|
|
99
|
+
- upper / middle / lower estuary comparison
|
|
100
|
+
|
|
101
|
+
## Exported outputs
|
|
102
|
+
|
|
103
|
+
The package exports:
|
|
104
|
+
- Excel workbook
|
|
105
|
+
- CSV files
|
|
106
|
+
- TXT files
|
|
107
|
+
- JSON files
|
|
108
|
+
|
|
109
|
+
Typical output tables include:
|
|
110
|
+
- `predictions_all`
|
|
111
|
+
- `chl_validation_overall`
|
|
112
|
+
- `chl_validation_by_site_test`
|
|
113
|
+
- `atsi_validation_overall`
|
|
114
|
+
- `atsi_validation_by_site_test`
|
|
115
|
+
- `atsi_confusion_matrix_test`
|
|
116
|
+
- `station_season_agreement_test`
|
|
117
|
+
- `spatial_zone_summary_test`
|
|
118
|
+
|
|
119
|
+
## Scientific notes
|
|
120
|
+
|
|
121
|
+
- ATSI remains **CHL-driven**
|
|
122
|
+
- SAL is used to determine **threshold context**
|
|
123
|
+
- ATSI values are clipped to **0–100**
|
|
124
|
+
- Current class translation is operational:
|
|
125
|
+
- Unpolluted
|
|
126
|
+
- Moderate
|
|
127
|
+
- Eutrophic
|
|
128
|
+
- Hypertrophic
|
|
129
|
+
|
|
130
|
+
## Limitations
|
|
131
|
+
|
|
132
|
+
This release does not yet include:
|
|
133
|
+
- uncertainty quantification
|
|
134
|
+
- prediction intervals
|
|
135
|
+
- raster ingestion
|
|
136
|
+
- geospatial map production
|
|
137
|
+
- SHAP explainability
|
|
138
|
+
|
|
139
|
+
## Contact
|
|
140
|
+
|
|
141
|
+
**Dr Md Galal Uddin**
|
|
142
|
+
School of Engineering, University of Galway, Ireland
|
|
143
|
+
Email: **jalaluddinbd1987@gmail.com**
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: RSATSIModel
|
|
3
|
+
Version: 2.5.0
|
|
4
|
+
Summary: Python package for computing ATSI and RS-ATSI using CHL, SAL, and Sentinel-3 OLCI-style data.
|
|
5
|
+
Author-email: Dr Md Galal Uddin <jalaluddinbd1987@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pandas>=1.5.0
|
|
11
|
+
Requires-Dist: numpy>=1.23.0
|
|
12
|
+
Requires-Dist: matplotlib>=3.6.0
|
|
13
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
14
|
+
Requires-Dist: scikit-learn>=1.2.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# REMOTE SENSING (RS)-DRIVEN ATSIModel
|
|
18
|
+
|
|
19
|
+
## Assessment of Trophic Status Index (ATSI) and RS-ATSI Python Package
|
|
20
|
+
|
|
21
|
+
**ATSIModel** is an open-source Python package for computing:
|
|
22
|
+
1. **ATSI** from in-situ **chlorophyll-a (CHL)** and **salinity (SAL)** data.
|
|
23
|
+
2. **RS-ATSI** from **Sentinel-3 OLCI style remote-sensing inputs**, with CHL prediction, ATSI derivation, and a structured validation framework.
|
|
24
|
+
|
|
25
|
+
The package is designed for **transitional, estuarine, and coastal waters**, and supports both standard ATSI computation and remote-sensing based ATSI workflows.
|
|
26
|
+
|
|
27
|
+
## Author
|
|
28
|
+
|
|
29
|
+
**Dr Md Galal Uddin**
|
|
30
|
+
School of Engineering, University of Galway, Ireland
|
|
31
|
+
Email: **jalaluddinbd1987@gmail.com**
|
|
32
|
+
|
|
33
|
+
### Research Profiles
|
|
34
|
+
- **Google Scholar**: https://scholar.google.com/citations?user=g6xaAOkAAAAJ&hl=en
|
|
35
|
+
|
|
36
|
+
## Scientific Background
|
|
37
|
+
|
|
38
|
+
ATSI is implemented as a **CHL-driven, salinity-conditioned trophic scoring framework**.
|
|
39
|
+
This package extends that framework into a **remote-sensing compatible RS-ATSI workflow**.
|
|
40
|
+
|
|
41
|
+
## Standard ATSI workflow
|
|
42
|
+
|
|
43
|
+
Required columns:
|
|
44
|
+
|
|
45
|
+
| Column | Description | Unit |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| CHL | Chlorophyll-a concentration | µg/L |
|
|
48
|
+
| SAL | Salinity | PSU |
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from atsimodel.core import run_atsi
|
|
54
|
+
df = run_atsi("examples/sample_atsi_input.xlsx")
|
|
55
|
+
print(df.head())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Export:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from atsimodel.utils import export_single
|
|
62
|
+
export_single(df, out_base="outputs/ATSI_results", formats=("xlsx", "csv", "txt", "json"))
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## RS-ATSI workflow
|
|
66
|
+
|
|
67
|
+
Recommended input columns:
|
|
68
|
+
- `CHL`
|
|
69
|
+
- `SAL`
|
|
70
|
+
- `Rhow_1`, `Rhow_2`, `Rhow_3`, `Rhow_4`, `Rhow_5`, `Rhow_6`, `Rhow_7`, `Rhow_8`, `Rhow_9`, `Rhow_10`, `Rhow_11`
|
|
71
|
+
|
|
72
|
+
Recommended optional columns:
|
|
73
|
+
- `SITE`
|
|
74
|
+
- `SEASON`
|
|
75
|
+
- `ZONE`
|
|
76
|
+
- `SPLIT`
|
|
77
|
+
|
|
78
|
+
Example RS table:
|
|
79
|
+
|
|
80
|
+
| SITE | SEASON | ZONE | SPLIT | CHL | SAL | Rhow_1 | Rhow_2 | Rhow_3 | Rhow_4 | Rhow_5 | Rhow_6 | Rhow_7 | Rhow_8 | Rhow_9 | Rhow_10 | Rhow_11 |
|
|
81
|
+
|---|---|---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
82
|
+
| S1 | Winter | Upper | TRAIN | 8.0 | 31.0 | 0.011 | 0.012 | 0.013 | 0.014 | 0.015 | 0.016 | 0.017 | 0.018 | 0.019 | 0.020 | 0.021 |
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from atsimodel.rs_core import run_rs_atsi_pipeline
|
|
88
|
+
|
|
89
|
+
results = run_rs_atsi_pipeline(
|
|
90
|
+
input_file="examples/sample_rs_atsi_input.xlsx",
|
|
91
|
+
output_dir="outputs/RS_ATSI_demo"
|
|
92
|
+
)
|
|
93
|
+
print(results["chl_validation_overall"])
|
|
94
|
+
print(results["atsi_validation_overall"])
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Validation design
|
|
98
|
+
|
|
99
|
+
### 1. CHL validation
|
|
100
|
+
- R²
|
|
101
|
+
- RMSE
|
|
102
|
+
- MAE
|
|
103
|
+
- Bias
|
|
104
|
+
- train / test / independent validation
|
|
105
|
+
- site-wise validation
|
|
106
|
+
|
|
107
|
+
### 2. ATSI validation
|
|
108
|
+
- RS-ATSI vs in-situ ATSI
|
|
109
|
+
- confusion matrix by trophic class
|
|
110
|
+
- agreement by station and season
|
|
111
|
+
|
|
112
|
+
### 3. Spatial validation
|
|
113
|
+
- hotspot consistency
|
|
114
|
+
- estuarine gradient realism
|
|
115
|
+
- upper / middle / lower estuary comparison
|
|
116
|
+
|
|
117
|
+
## Exported outputs
|
|
118
|
+
|
|
119
|
+
The package exports:
|
|
120
|
+
- Excel workbook
|
|
121
|
+
- CSV files
|
|
122
|
+
- TXT files
|
|
123
|
+
- JSON files
|
|
124
|
+
|
|
125
|
+
Typical output tables include:
|
|
126
|
+
- `predictions_all`
|
|
127
|
+
- `chl_validation_overall`
|
|
128
|
+
- `chl_validation_by_site_test`
|
|
129
|
+
- `atsi_validation_overall`
|
|
130
|
+
- `atsi_validation_by_site_test`
|
|
131
|
+
- `atsi_confusion_matrix_test`
|
|
132
|
+
- `station_season_agreement_test`
|
|
133
|
+
- `spatial_zone_summary_test`
|
|
134
|
+
|
|
135
|
+
## Scientific notes
|
|
136
|
+
|
|
137
|
+
- ATSI remains **CHL-driven**
|
|
138
|
+
- SAL is used to determine **threshold context**
|
|
139
|
+
- ATSI values are clipped to **0–100**
|
|
140
|
+
- Current class translation is operational:
|
|
141
|
+
- Unpolluted
|
|
142
|
+
- Moderate
|
|
143
|
+
- Eutrophic
|
|
144
|
+
- Hypertrophic
|
|
145
|
+
|
|
146
|
+
## Limitations
|
|
147
|
+
|
|
148
|
+
This release does not yet include:
|
|
149
|
+
- uncertainty quantification
|
|
150
|
+
- prediction intervals
|
|
151
|
+
- raster ingestion
|
|
152
|
+
- geospatial map production
|
|
153
|
+
- SHAP explainability
|
|
154
|
+
|
|
155
|
+
## Contact
|
|
156
|
+
|
|
157
|
+
**Dr Md Galal Uddin**
|
|
158
|
+
School of Engineering, University of Galway, Ireland
|
|
159
|
+
Email: **jalaluddinbd1987@gmail.com**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.py
|
|
6
|
+
RSATSIModel.egg-info/PKG-INFO
|
|
7
|
+
RSATSIModel.egg-info/SOURCES.txt
|
|
8
|
+
RSATSIModel.egg-info/dependency_links.txt
|
|
9
|
+
RSATSIModel.egg-info/requires.txt
|
|
10
|
+
RSATSIModel.egg-info/top_level.txt
|
|
11
|
+
atsimodel/__init__.py
|
|
12
|
+
atsimodel/atsifunction.py
|
|
13
|
+
atsimodel/chl_models.py
|
|
14
|
+
atsimodel/classification.py
|
|
15
|
+
atsimodel/core.py
|
|
16
|
+
atsimodel/io.py
|
|
17
|
+
atsimodel/metrics.py
|
|
18
|
+
atsimodel/olci_features.py
|
|
19
|
+
atsimodel/plotting.py
|
|
20
|
+
atsimodel/preprocessing.py
|
|
21
|
+
atsimodel/rs_core.py
|
|
22
|
+
atsimodel/thresholds.py
|
|
23
|
+
atsimodel/utils.py
|
|
24
|
+
atsimodel/validation.py
|
|
25
|
+
examples/sample_atsi_input.xlsx
|
|
26
|
+
examples/sample_rs_atsi_input.csv
|
|
27
|
+
examples/sample_rs_atsi_input.txt
|
|
28
|
+
examples/sample_rs_atsi_input.xlsx
|
|
29
|
+
examples/sample_rs_atsi_rhow_input.csv
|
|
30
|
+
examples/sample_rs_atsi_rhow_input.txt
|
|
31
|
+
examples/sample_rs_atsi_rhow_input.xlsx
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
atsimodel
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
def clip_0_100(x):
|
|
5
|
+
if pd.isna(x):
|
|
6
|
+
return np.nan
|
|
7
|
+
return float(np.clip(x, 0.0, 100.0))
|
|
8
|
+
|
|
9
|
+
def compute_atsi_score(chl_value, chl_threshold_low, chl_threshold_upper):
|
|
10
|
+
NFh = 100.0
|
|
11
|
+
NFl = 0.0
|
|
12
|
+
if pd.isna(chl_value) or pd.isna(chl_threshold_low) or pd.isna(chl_threshold_upper):
|
|
13
|
+
return np.nan
|
|
14
|
+
if chl_threshold_upper <= 0:
|
|
15
|
+
return np.nan
|
|
16
|
+
atsi = (NFh - NFl) - (((chl_value - chl_threshold_low) / chl_threshold_upper) * NFh)
|
|
17
|
+
return clip_0_100(atsi)
|
|
18
|
+
|
|
19
|
+
def compute_atsi_series(df, chl_col="CHL", low_col="CHL_TL", upper_col="CHL_TU"):
|
|
20
|
+
return df.apply(lambda row: compute_atsi_score(row[chl_col], row[low_col], row[upper_col]), axis=1)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
import numpy as np
|
|
3
|
+
from sklearn.ensemble import RandomForestRegressor
|
|
4
|
+
from sklearn.impute import SimpleImputer
|
|
5
|
+
from sklearn.pipeline import Pipeline
|
|
6
|
+
from .metrics import regression_metrics
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class CHLModelBundle:
|
|
10
|
+
model: object
|
|
11
|
+
feature_cols: list
|
|
12
|
+
train_metrics: dict
|
|
13
|
+
test_metrics: dict
|
|
14
|
+
|
|
15
|
+
def fit_rf_chl_model(train_df, test_df, feature_cols, target_col="CHL", random_state=42):
|
|
16
|
+
X_train = train_df[feature_cols].copy()
|
|
17
|
+
y_train = train_df[target_col].copy()
|
|
18
|
+
X_test = test_df[feature_cols].copy()
|
|
19
|
+
y_test = test_df[target_col].copy()
|
|
20
|
+
model = Pipeline(steps=[
|
|
21
|
+
("imputer", SimpleImputer(strategy="median")),
|
|
22
|
+
("rf", RandomForestRegressor(n_estimators=500, random_state=random_state, n_jobs=-1)),
|
|
23
|
+
])
|
|
24
|
+
model.fit(X_train, y_train)
|
|
25
|
+
pred_train = np.clip(model.predict(X_train), 0, None)
|
|
26
|
+
pred_test = np.clip(model.predict(X_test), 0, None)
|
|
27
|
+
train_metrics = regression_metrics(y_train, pred_train, label="Train")
|
|
28
|
+
test_metrics = regression_metrics(y_test, pred_test, label="Test")
|
|
29
|
+
bundle = CHLModelBundle(model=model, feature_cols=feature_cols, train_metrics=train_metrics, test_metrics=test_metrics)
|
|
30
|
+
return bundle, pred_train, pred_test
|
|
31
|
+
|
|
32
|
+
def predict_chl(df, bundle, output_col="CHL_RS"):
|
|
33
|
+
X = df[bundle.feature_cols].copy()
|
|
34
|
+
pred = np.clip(bundle.model.predict(X), 0, None)
|
|
35
|
+
out = df.copy()
|
|
36
|
+
out[output_col] = pred
|
|
37
|
+
return out
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
def classify_atsi(score):
|
|
4
|
+
if pd.isna(score):
|
|
5
|
+
return None
|
|
6
|
+
score = max(0.0, min(100.0, float(score)))
|
|
7
|
+
if score >= 75:
|
|
8
|
+
return "Unpolluted"
|
|
9
|
+
elif score >= 50:
|
|
10
|
+
return "Moderate"
|
|
11
|
+
elif score >= 25:
|
|
12
|
+
return "Eutrophic"
|
|
13
|
+
else:
|
|
14
|
+
return "Hypertrophic"
|
|
15
|
+
|
|
16
|
+
def classify_series(series):
|
|
17
|
+
return series.apply(classify_atsi)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .io import load_data
|
|
2
|
+
from .preprocessing import validate_atsi_input
|
|
3
|
+
from .thresholds import assign_thresholds
|
|
4
|
+
from .atsifunction import compute_atsi_series
|
|
5
|
+
from .classification import classify_series
|
|
6
|
+
|
|
7
|
+
def run_atsi(file_path):
|
|
8
|
+
df = load_data(file_path)
|
|
9
|
+
df = validate_atsi_input(df)
|
|
10
|
+
df = assign_thresholds(df, sal_col="SAL")
|
|
11
|
+
df["ATSI"] = compute_atsi_series(df)
|
|
12
|
+
df["ATSI"] = df["ATSI"].clip(lower=0, upper=100)
|
|
13
|
+
df["ATSI_Class"] = classify_series(df["ATSI"])
|
|
14
|
+
return df
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
def load_data(file_path):
|
|
5
|
+
file_path = Path(file_path)
|
|
6
|
+
if not file_path.exists():
|
|
7
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
8
|
+
suffix = file_path.suffix.lower()
|
|
9
|
+
if suffix in [".xlsx", ".xls"]:
|
|
10
|
+
return pd.read_excel(file_path)
|
|
11
|
+
if suffix == ".csv":
|
|
12
|
+
return pd.read_csv(file_path)
|
|
13
|
+
if suffix in [".txt", ".tsv"]:
|
|
14
|
+
return pd.read_csv(file_path, sep=None, engine="python")
|
|
15
|
+
raise ValueError(f"Unsupported file format: {suffix}. Use xlsx/xls/csv/txt/tsv.")
|
|
16
|
+
|
|
17
|
+
def save_table(df, file_path):
|
|
18
|
+
file_path = Path(file_path)
|
|
19
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
suffix = file_path.suffix.lower()
|
|
21
|
+
if suffix in [".xlsx", ".xls"]:
|
|
22
|
+
df.to_excel(file_path, index=False)
|
|
23
|
+
elif suffix == ".csv":
|
|
24
|
+
df.to_csv(file_path, index=False)
|
|
25
|
+
elif suffix in [".txt", ".tsv"]:
|
|
26
|
+
df.to_csv(file_path, index=False, sep="\\t")
|
|
27
|
+
elif suffix == ".json":
|
|
28
|
+
df.to_json(file_path, orient="records", indent=4)
|
|
29
|
+
else:
|
|
30
|
+
raise ValueError(f"Unsupported output format: {suffix}")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error, confusion_matrix
|
|
4
|
+
|
|
5
|
+
def rmse(y_true, y_pred):
|
|
6
|
+
return float(np.sqrt(mean_squared_error(y_true, y_pred)))
|
|
7
|
+
|
|
8
|
+
def mbe(y_true, y_pred):
|
|
9
|
+
y_true = np.asarray(y_true)
|
|
10
|
+
y_pred = np.asarray(y_pred)
|
|
11
|
+
return float(np.mean(y_pred - y_true))
|
|
12
|
+
|
|
13
|
+
def agreement_rate(y_true_labels, y_pred_labels):
|
|
14
|
+
y_true_labels = pd.Series(y_true_labels).astype(str)
|
|
15
|
+
y_pred_labels = pd.Series(y_pred_labels).astype(str)
|
|
16
|
+
return float((y_true_labels == y_pred_labels).mean())
|
|
17
|
+
|
|
18
|
+
def regression_metrics(y_true, y_pred, label="All"):
|
|
19
|
+
y_true = pd.Series(y_true).astype(float)
|
|
20
|
+
y_pred = pd.Series(y_pred).astype(float)
|
|
21
|
+
mask = y_true.notna() & y_pred.notna()
|
|
22
|
+
y_true = y_true[mask]
|
|
23
|
+
y_pred = y_pred[mask]
|
|
24
|
+
if len(y_true) == 0:
|
|
25
|
+
return {"Group": label, "N": 0, "R2": np.nan, "RMSE": np.nan, "MAE": np.nan, "Bias": np.nan}
|
|
26
|
+
return {
|
|
27
|
+
"Group": label,
|
|
28
|
+
"N": int(len(y_true)),
|
|
29
|
+
"R2": float(r2_score(y_true, y_pred)) if len(y_true) >= 2 else np.nan,
|
|
30
|
+
"RMSE": rmse(y_true, y_pred),
|
|
31
|
+
"MAE": float(mean_absolute_error(y_true, y_pred)),
|
|
32
|
+
"Bias": mbe(y_true, y_pred),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def confusion_matrix_table(y_true_labels, y_pred_labels, labels=None):
|
|
36
|
+
y_true_labels = pd.Series(y_true_labels).astype(str)
|
|
37
|
+
y_pred_labels = pd.Series(y_pred_labels).astype(str)
|
|
38
|
+
if labels is None:
|
|
39
|
+
labels = sorted(set(y_true_labels.unique()).union(set(y_pred_labels.unique())))
|
|
40
|
+
cm = confusion_matrix(y_true_labels, y_pred_labels, labels=labels)
|
|
41
|
+
return pd.DataFrame(cm, index=[f"True_{x}" for x in labels], columns=[f"Pred_{x}" for x in labels])
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
# Sentinel-3 OLCI reflectance style inputs expected from tabular exports
|
|
5
|
+
DEFAULT_OLCI_FEATURES = [f"RHOW_{i}" for i in range(1, 12)]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def add_olci_features(df):
|
|
9
|
+
"""
|
|
10
|
+
Add engineered features from Sentinel-3 OLCI-style reflectance inputs.
|
|
11
|
+
|
|
12
|
+
Required raw inputs:
|
|
13
|
+
RHOW_1 ... RHOW_11
|
|
14
|
+
|
|
15
|
+
These are intended to match user-exported tabular reflectance columns such as:
|
|
16
|
+
Rhow_1, Rhow_2, ..., Rhow_11
|
|
17
|
+
"""
|
|
18
|
+
df = df.copy()
|
|
19
|
+
eps = 1e-9
|
|
20
|
+
|
|
21
|
+
for c in DEFAULT_OLCI_FEATURES:
|
|
22
|
+
df[c] = pd.to_numeric(df[c], errors="coerce")
|
|
23
|
+
|
|
24
|
+
# Core ratios
|
|
25
|
+
df["R_6_1"] = df["RHOW_6"] / (df["RHOW_1"] + eps)
|
|
26
|
+
df["R_6_2"] = df["RHOW_6"] / (df["RHOW_2"] + eps)
|
|
27
|
+
df["R_7_3"] = df["RHOW_7"] / (df["RHOW_3"] + eps)
|
|
28
|
+
df["R_8_4"] = df["RHOW_8"] / (df["RHOW_4"] + eps)
|
|
29
|
+
df["R_9_5"] = df["RHOW_9"] / (df["RHOW_5"] + eps)
|
|
30
|
+
df["R_10_6"] = df["RHOW_10"] / (df["RHOW_6"] + eps)
|
|
31
|
+
df["R_11_7"] = df["RHOW_11"] / (df["RHOW_7"] + eps)
|
|
32
|
+
|
|
33
|
+
# Normalized differences
|
|
34
|
+
pairs = [(6,1),(6,2),(7,3),(8,4),(9,5),(10,6),(11,7)]
|
|
35
|
+
for a,b in pairs:
|
|
36
|
+
df[f"ND_{a}_{b}"] = (df[f"RHOW_{a}"] - df[f"RHOW_{b}"]) / (df[f"RHOW_{a}"] + df[f"RHOW_{b}"] + eps)
|
|
37
|
+
|
|
38
|
+
# Adjacent band slopes / differences
|
|
39
|
+
for i in range(1, 11):
|
|
40
|
+
df[f"DIFF_{i}_{i+1}"] = df[f"RHOW_{i+1}"] - df[f"RHOW_{i}"]
|
|
41
|
+
|
|
42
|
+
# Logs
|
|
43
|
+
for c in DEFAULT_OLCI_FEATURES:
|
|
44
|
+
df[f"LOG_{c}"] = np.log1p(np.clip(df[c], a_min=0, a_max=None))
|
|
45
|
+
|
|
46
|
+
return df
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_default_model_features():
|
|
50
|
+
feats = DEFAULT_OLCI_FEATURES.copy()
|
|
51
|
+
feats += ["R_6_1", "R_6_2", "R_7_3", "R_8_4", "R_9_5", "R_10_6", "R_11_7"]
|
|
52
|
+
feats += [f"ND_{a}_{b}" for a,b in [(6,1),(6,2),(7,3),(8,4),(9,5),(10,6),(11,7)]]
|
|
53
|
+
feats += [f"DIFF_{i}_{i+1}" for i in range(1, 11)]
|
|
54
|
+
feats += [f"LOG_RHOW_{i}" for i in range(1, 12)]
|
|
55
|
+
return feats
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
|
|
3
|
+
def plot_atsi_distribution(df, score_col="ATSI"):
|
|
4
|
+
plt.figure(figsize=(8, 5))
|
|
5
|
+
plt.hist(df[score_col].dropna(), bins=20, edgecolor="black")
|
|
6
|
+
plt.xlabel("ATSI Score")
|
|
7
|
+
plt.ylabel("Frequency")
|
|
8
|
+
plt.title("Distribution of ATSI Scores")
|
|
9
|
+
plt.tight_layout()
|
|
10
|
+
plt.show()
|
|
11
|
+
|
|
12
|
+
def plot_chl_vs_atsi(df, chl_col="CHL", score_col="ATSI"):
|
|
13
|
+
plt.figure(figsize=(8, 5))
|
|
14
|
+
plt.scatter(df[chl_col], df[score_col])
|
|
15
|
+
plt.xlabel("CHL (µg/L)")
|
|
16
|
+
plt.ylabel("ATSI Score")
|
|
17
|
+
plt.title("CHL vs ATSI")
|
|
18
|
+
plt.tight_layout()
|
|
19
|
+
plt.show()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
def standardize_columns(df):
|
|
4
|
+
df = df.copy()
|
|
5
|
+
df.columns = [str(c).strip().upper() for c in df.columns]
|
|
6
|
+
return df
|
|
7
|
+
|
|
8
|
+
def validate_atsi_input(df):
|
|
9
|
+
df = standardize_columns(df)
|
|
10
|
+
required = ["CHL", "SAL"]
|
|
11
|
+
missing = [c for c in required if c not in df.columns]
|
|
12
|
+
if missing:
|
|
13
|
+
raise ValueError(f"Missing required columns: {missing}. ATSI requires CHL and SAL.")
|
|
14
|
+
df["CHL"] = pd.to_numeric(df["CHL"], errors="coerce")
|
|
15
|
+
df["SAL"] = pd.to_numeric(df["SAL"], errors="coerce")
|
|
16
|
+
return df
|
|
17
|
+
|
|
18
|
+
def validate_rs_input(df, feature_cols, sal_col="SAL", target_col=None):
|
|
19
|
+
df = standardize_columns(df)
|
|
20
|
+
feature_cols = [c.upper() for c in feature_cols]
|
|
21
|
+
sal_col = sal_col.upper()
|
|
22
|
+
target_col = target_col.upper() if target_col else None
|
|
23
|
+
required = feature_cols + [sal_col] + ([target_col] if target_col else [])
|
|
24
|
+
missing = [c for c in required if c not in df.columns]
|
|
25
|
+
if missing:
|
|
26
|
+
raise ValueError(f"Missing RS input columns: {missing}")
|
|
27
|
+
for c in feature_cols + [sal_col] + ([target_col] if target_col else []):
|
|
28
|
+
df[c] = pd.to_numeric(df[c], errors="coerce")
|
|
29
|
+
return df
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from sklearn.model_selection import train_test_split
|
|
3
|
+
from .io import load_data
|
|
4
|
+
from .preprocessing import validate_rs_input, standardize_columns
|
|
5
|
+
from .olci_features import add_olci_features, get_default_model_features
|
|
6
|
+
from .chl_models import fit_rf_chl_model, predict_chl
|
|
7
|
+
from .thresholds import assign_thresholds
|
|
8
|
+
from .atsifunction import compute_atsi_series
|
|
9
|
+
from .classification import classify_series
|
|
10
|
+
from .validation import validate_chl_overall, validate_chl_by_group, validate_atsi_regression, validate_atsi_by_group, validate_atsi_classification, validate_station_season_agreement, spatial_zone_summary
|
|
11
|
+
from .utils import export_results_dict
|
|
12
|
+
|
|
13
|
+
def _split_train_test_independent(df, split_col="SPLIT"):
|
|
14
|
+
df = df.copy()
|
|
15
|
+
if split_col in df.columns:
|
|
16
|
+
train_df = df[df[split_col].astype(str).str.upper() == "TRAIN"].copy()
|
|
17
|
+
test_df = df[df[split_col].astype(str).str.upper() == "TEST"].copy()
|
|
18
|
+
ind_df = df[df[split_col].astype(str).str.upper().isin(["INDEPENDENT", "VALIDATION"])].copy()
|
|
19
|
+
if len(train_df) == 0 or len(test_df) == 0:
|
|
20
|
+
raise ValueError("When SPLIT exists, it must contain at least TRAIN and TEST rows.")
|
|
21
|
+
return train_df, test_df, ind_df
|
|
22
|
+
train_df, test_df = train_test_split(df, test_size=0.25, random_state=42)
|
|
23
|
+
return train_df.copy(), test_df.copy(), pd.DataFrame(columns=df.columns)
|
|
24
|
+
|
|
25
|
+
def _compute_atsi_from_chl_sal(df, chl_col, sal_col="SAL", out_score_col="ATSI_RS", out_class_col="ATSI_RS_CLASS"):
|
|
26
|
+
out = df.copy()
|
|
27
|
+
out = out.rename(columns={sal_col: "SAL"})
|
|
28
|
+
out = assign_thresholds(out, sal_col="SAL")
|
|
29
|
+
out[out_score_col] = compute_atsi_series(out, chl_col=chl_col, low_col="CHL_TL", upper_col="CHL_TU").clip(lower=0, upper=100)
|
|
30
|
+
out[out_class_col] = classify_series(out[out_score_col])
|
|
31
|
+
return out
|
|
32
|
+
|
|
33
|
+
def run_rs_atsi_pipeline(input_file, output_dir="outputs_rs_atsi", target_col="CHL", sal_col="SAL", feature_cols=None, site_col="SITE", season_col="SEASON", zone_col="ZONE"):
|
|
34
|
+
df = load_data(input_file)
|
|
35
|
+
df = standardize_columns(df)
|
|
36
|
+
df = add_olci_features(df)
|
|
37
|
+
if feature_cols is None:
|
|
38
|
+
feature_cols = get_default_model_features()
|
|
39
|
+
feature_cols = [c.upper() for c in feature_cols]
|
|
40
|
+
df = validate_rs_input(df, feature_cols=feature_cols, sal_col=sal_col, target_col=target_col)
|
|
41
|
+
train_df, test_df, ind_df = _split_train_test_independent(df, split_col="SPLIT")
|
|
42
|
+
bundle, pred_train, pred_test = fit_rf_chl_model(train_df, test_df, feature_cols=feature_cols, target_col=target_col.upper())
|
|
43
|
+
train_df["CHL_RS"] = pred_train
|
|
44
|
+
test_df["CHL_RS"] = pred_test
|
|
45
|
+
if len(ind_df) > 0:
|
|
46
|
+
ind_df = predict_chl(ind_df, bundle, output_col="CHL_RS")
|
|
47
|
+
train_df = _compute_atsi_from_chl_sal(train_df, chl_col=target_col.upper(), sal_col=sal_col.upper(), out_score_col="ATSI_INSITU", out_class_col="ATSI_INSITU_CLASS")
|
|
48
|
+
train_df = _compute_atsi_from_chl_sal(train_df, chl_col="CHL_RS", sal_col=sal_col.upper(), out_score_col="ATSI_RS", out_class_col="ATSI_RS_CLASS")
|
|
49
|
+
test_df = _compute_atsi_from_chl_sal(test_df, chl_col=target_col.upper(), sal_col=sal_col.upper(), out_score_col="ATSI_INSITU", out_class_col="ATSI_INSITU_CLASS")
|
|
50
|
+
test_df = _compute_atsi_from_chl_sal(test_df, chl_col="CHL_RS", sal_col=sal_col.upper(), out_score_col="ATSI_RS", out_class_col="ATSI_RS_CLASS")
|
|
51
|
+
if len(ind_df) > 0:
|
|
52
|
+
ind_df = _compute_atsi_from_chl_sal(ind_df, chl_col=target_col.upper(), sal_col=sal_col.upper(), out_score_col="ATSI_INSITU", out_class_col="ATSI_INSITU_CLASS")
|
|
53
|
+
ind_df = _compute_atsi_from_chl_sal(ind_df, chl_col="CHL_RS", sal_col=sal_col.upper(), out_score_col="ATSI_RS", out_class_col="ATSI_RS_CLASS")
|
|
54
|
+
validation_tables = {}
|
|
55
|
+
validation_tables["chl_validation_overall"] = pd.concat([
|
|
56
|
+
validate_chl_overall(train_df, actual_col=target_col.upper(), pred_col="CHL_RS", group_label="Train"),
|
|
57
|
+
validate_chl_overall(test_df, actual_col=target_col.upper(), pred_col="CHL_RS", group_label="Test"),
|
|
58
|
+
validate_chl_overall(ind_df, actual_col=target_col.upper(), pred_col="CHL_RS", group_label="Independent") if len(ind_df) > 0 else pd.DataFrame(),
|
|
59
|
+
], ignore_index=True)
|
|
60
|
+
validation_tables["chl_validation_by_site_test"] = validate_chl_by_group(test_df, actual_col=target_col.upper(), pred_col="CHL_RS", group_col=site_col.upper())
|
|
61
|
+
if len(ind_df) > 0:
|
|
62
|
+
validation_tables["chl_validation_by_site_independent"] = validate_chl_by_group(ind_df, actual_col=target_col.upper(), pred_col="CHL_RS", group_col=site_col.upper())
|
|
63
|
+
validation_tables["atsi_validation_overall"] = pd.concat([
|
|
64
|
+
validate_atsi_regression(train_df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_label="Train"),
|
|
65
|
+
validate_atsi_regression(test_df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_label="Test"),
|
|
66
|
+
validate_atsi_regression(ind_df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_label="Independent") if len(ind_df) > 0 else pd.DataFrame(),
|
|
67
|
+
], ignore_index=True)
|
|
68
|
+
validation_tables["atsi_validation_by_site_test"] = validate_atsi_by_group(test_df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_col=site_col.upper())
|
|
69
|
+
if len(ind_df) > 0:
|
|
70
|
+
validation_tables["atsi_validation_by_site_independent"] = validate_atsi_by_group(ind_df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_col=site_col.upper())
|
|
71
|
+
class_test = validate_atsi_classification(test_df, actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS")
|
|
72
|
+
validation_tables["atsi_class_summary_test"] = class_test["summary"]
|
|
73
|
+
validation_tables["atsi_confusion_matrix_test"] = class_test["confusion_matrix"]
|
|
74
|
+
if len(ind_df) > 0:
|
|
75
|
+
class_ind = validate_atsi_classification(ind_df, actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS")
|
|
76
|
+
validation_tables["atsi_class_summary_independent"] = class_ind["summary"]
|
|
77
|
+
validation_tables["atsi_confusion_matrix_independent"] = class_ind["confusion_matrix"]
|
|
78
|
+
validation_tables["station_season_agreement_test"] = validate_station_season_agreement(test_df, site_col=site_col.upper(), season_col=season_col.upper(), actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS")
|
|
79
|
+
if len(ind_df) > 0:
|
|
80
|
+
validation_tables["station_season_agreement_independent"] = validate_station_season_agreement(ind_df, site_col=site_col.upper(), season_col=season_col.upper(), actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS")
|
|
81
|
+
validation_tables["spatial_zone_summary_test"] = spatial_zone_summary(test_df, zone_col=zone_col.upper(), actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS")
|
|
82
|
+
if len(ind_df) > 0:
|
|
83
|
+
validation_tables["spatial_zone_summary_independent"] = spatial_zone_summary(ind_df, zone_col=zone_col.upper(), actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS")
|
|
84
|
+
validation_tables["chl_model_metrics_summary"] = pd.DataFrame([bundle.train_metrics, bundle.test_metrics])
|
|
85
|
+
predictions_all = pd.concat([
|
|
86
|
+
train_df.assign(DATASET="Train"),
|
|
87
|
+
test_df.assign(DATASET="Test"),
|
|
88
|
+
ind_df.assign(DATASET="Independent") if len(ind_df) > 0 else pd.DataFrame()
|
|
89
|
+
], ignore_index=True)
|
|
90
|
+
results = {"predictions_all": predictions_all, **validation_tables}
|
|
91
|
+
export_results_dict(results, output_dir=output_dir)
|
|
92
|
+
return results
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
SAL_THRESHOLD_TABLE = {
|
|
4
|
+
0: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
5
|
+
1: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
6
|
+
2: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
7
|
+
3: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
8
|
+
4: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
9
|
+
5: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
10
|
+
6: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
11
|
+
7: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
12
|
+
8: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
13
|
+
9: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
14
|
+
10: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
15
|
+
11: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
16
|
+
12: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
17
|
+
13: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
18
|
+
14: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
19
|
+
15: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
20
|
+
16: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
21
|
+
17: {"CHL_TL": 15.0, "CHL_TU": 30.0},
|
|
22
|
+
18: {"CHL_TL": 14.7, "CHL_TU": 29.4},
|
|
23
|
+
19: {"CHL_TL": 14.4, "CHL_TU": 28.9},
|
|
24
|
+
20: {"CHL_TL": 14.2, "CHL_TU": 28.3},
|
|
25
|
+
21: {"CHL_TL": 13.9, "CHL_TU": 27.8},
|
|
26
|
+
22: {"CHL_TL": 13.6, "CHL_TU": 27.2},
|
|
27
|
+
23: {"CHL_TL": 13.3, "CHL_TU": 26.7},
|
|
28
|
+
24: {"CHL_TL": 13.1, "CHL_TU": 26.1},
|
|
29
|
+
25: {"CHL_TL": 12.8, "CHL_TU": 25.6},
|
|
30
|
+
26: {"CHL_TL": 12.5, "CHL_TU": 25.0},
|
|
31
|
+
27: {"CHL_TL": 12.2, "CHL_TU": 24.4},
|
|
32
|
+
28: {"CHL_TL": 11.9, "CHL_TU": 23.9},
|
|
33
|
+
29: {"CHL_TL": 11.7, "CHL_TU": 23.3},
|
|
34
|
+
30: {"CHL_TL": 11.4, "CHL_TU": 22.8},
|
|
35
|
+
31: {"CHL_TL": 11.1, "CHL_TU": 22.2},
|
|
36
|
+
32: {"CHL_TL": 10.8, "CHL_TU": 21.7},
|
|
37
|
+
33: {"CHL_TL": 10.6, "CHL_TU": 21.1},
|
|
38
|
+
34: {"CHL_TL": 10.3, "CHL_TU": 20.6},
|
|
39
|
+
35: {"CHL_TL": 10.0, "CHL_TU": 20.0}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def round_salinity_to_median_bin(sal):
|
|
43
|
+
if pd.isna(sal):
|
|
44
|
+
return None
|
|
45
|
+
sal_bin = int(round(float(sal)))
|
|
46
|
+
return max(0, min(35, sal_bin))
|
|
47
|
+
|
|
48
|
+
def get_chl_thresholds_from_salinity(sal):
|
|
49
|
+
sal_bin = round_salinity_to_median_bin(sal)
|
|
50
|
+
if sal_bin is None:
|
|
51
|
+
return {"SAL_BIN": None, "CHL_TL": None, "CHL_TU": None}
|
|
52
|
+
vals = SAL_THRESHOLD_TABLE[sal_bin]
|
|
53
|
+
return {"SAL_BIN": sal_bin, "CHL_TL": vals["CHL_TL"], "CHL_TU": vals["CHL_TU"]}
|
|
54
|
+
|
|
55
|
+
def assign_thresholds(df, sal_col="SAL"):
|
|
56
|
+
df = df.copy()
|
|
57
|
+
sal_col = sal_col.upper()
|
|
58
|
+
out = df[sal_col].apply(get_chl_thresholds_from_salinity).apply(pd.Series)
|
|
59
|
+
df["SAL_BIN"] = out["SAL_BIN"]
|
|
60
|
+
df["CHL_TL"] = out["CHL_TL"]
|
|
61
|
+
df["CHL_TU"] = out["CHL_TU"]
|
|
62
|
+
return df
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
def export_results_dict(results_dict, output_dir):
|
|
5
|
+
output_dir = Path(output_dir)
|
|
6
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
7
|
+
excel_path = output_dir / "RS_ATSI_results_bundle.xlsx"
|
|
8
|
+
with pd.ExcelWriter(excel_path, engine="openpyxl") as writer:
|
|
9
|
+
for key, value in results_dict.items():
|
|
10
|
+
if isinstance(value, pd.DataFrame):
|
|
11
|
+
value.to_excel(writer, sheet_name=key[:31], index=False)
|
|
12
|
+
for key, value in results_dict.items():
|
|
13
|
+
if isinstance(value, pd.DataFrame):
|
|
14
|
+
value.to_csv(output_dir / f"{key}.csv", index=False)
|
|
15
|
+
value.to_csv(output_dir / f"{key}.txt", index=False, sep="\t")
|
|
16
|
+
value.to_json(output_dir / f"{key}.json", orient="records", indent=4)
|
|
17
|
+
|
|
18
|
+
def export_single(df, out_base="ATSI_results", formats=("xlsx", "csv", "txt", "json")):
|
|
19
|
+
out_base = Path(out_base)
|
|
20
|
+
out_base.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
if "xlsx" in formats:
|
|
22
|
+
df.to_excel(f"{out_base}.xlsx", index=False)
|
|
23
|
+
if "csv" in formats:
|
|
24
|
+
df.to_csv(f"{out_base}.csv", index=False)
|
|
25
|
+
if "txt" in formats:
|
|
26
|
+
df.to_csv(f"{out_base}.txt", index=False, sep="\t")
|
|
27
|
+
if "json" in formats:
|
|
28
|
+
df.to_json(f"{out_base}.json", orient="records", indent=4)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from .metrics import regression_metrics, agreement_rate, confusion_matrix_table
|
|
3
|
+
from .classification import classify_series
|
|
4
|
+
|
|
5
|
+
def validate_chl_overall(df, actual_col="CHL", pred_col="CHL_RS", group_label="Overall"):
|
|
6
|
+
return pd.DataFrame([regression_metrics(df[actual_col], df[pred_col], label=group_label)])
|
|
7
|
+
|
|
8
|
+
def validate_chl_by_group(df, actual_col="CHL", pred_col="CHL_RS", group_col="SITE"):
|
|
9
|
+
rows = []
|
|
10
|
+
if group_col not in df.columns:
|
|
11
|
+
return pd.DataFrame()
|
|
12
|
+
for g, sub in df.groupby(group_col):
|
|
13
|
+
rows.append(regression_metrics(sub[actual_col], sub[pred_col], label=str(g)))
|
|
14
|
+
return pd.DataFrame(rows)
|
|
15
|
+
|
|
16
|
+
def validate_atsi_regression(df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_label="Overall"):
|
|
17
|
+
return pd.DataFrame([regression_metrics(df[actual_col], df[pred_col], label=group_label)])
|
|
18
|
+
|
|
19
|
+
def validate_atsi_by_group(df, actual_col="ATSI_INSITU", pred_col="ATSI_RS", group_col="SITE"):
|
|
20
|
+
rows = []
|
|
21
|
+
if group_col not in df.columns:
|
|
22
|
+
return pd.DataFrame()
|
|
23
|
+
for g, sub in df.groupby(group_col):
|
|
24
|
+
rows.append(regression_metrics(sub[actual_col], sub[pred_col], label=str(g)))
|
|
25
|
+
return pd.DataFrame(rows)
|
|
26
|
+
|
|
27
|
+
def validate_atsi_classification(df, actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS",
|
|
28
|
+
actual_class_col=None, pred_class_col=None):
|
|
29
|
+
out = {}
|
|
30
|
+
df = df.copy()
|
|
31
|
+
if actual_class_col is None:
|
|
32
|
+
actual_class_col = "__ATSI_CLASS_TRUE__"
|
|
33
|
+
df[actual_class_col] = classify_series(df[actual_score_col])
|
|
34
|
+
if pred_class_col is None:
|
|
35
|
+
pred_class_col = "__ATSI_CLASS_PRED__"
|
|
36
|
+
df[pred_class_col] = classify_series(df[pred_score_col])
|
|
37
|
+
out["summary"] = pd.DataFrame([{
|
|
38
|
+
"Agreement_Rate": agreement_rate(df[actual_class_col], df[pred_class_col]),
|
|
39
|
+
"N": int(len(df))
|
|
40
|
+
}])
|
|
41
|
+
out["confusion_matrix"] = confusion_matrix_table(df[actual_class_col], df[pred_class_col])
|
|
42
|
+
return out
|
|
43
|
+
|
|
44
|
+
def validate_station_season_agreement(df, site_col="SITE", season_col="SEASON",
|
|
45
|
+
actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS"):
|
|
46
|
+
rows = []
|
|
47
|
+
df = df.copy()
|
|
48
|
+
df["ATSI_CLASS_TRUE"] = classify_series(df[actual_score_col])
|
|
49
|
+
df["ATSI_CLASS_PRED"] = classify_series(df[pred_score_col])
|
|
50
|
+
group_cols = []
|
|
51
|
+
if site_col in df.columns: group_cols.append(site_col)
|
|
52
|
+
if season_col in df.columns: group_cols.append(season_col)
|
|
53
|
+
if not group_cols: return pd.DataFrame()
|
|
54
|
+
for g, sub in df.groupby(group_cols):
|
|
55
|
+
if not isinstance(g, tuple): g = (g,)
|
|
56
|
+
row = {col: val for col, val in zip(group_cols, g)}
|
|
57
|
+
row["Agreement_Rate"] = agreement_rate(sub["ATSI_CLASS_TRUE"], sub["ATSI_CLASS_PRED"])
|
|
58
|
+
row["N"] = int(len(sub))
|
|
59
|
+
rows.append(row)
|
|
60
|
+
return pd.DataFrame(rows)
|
|
61
|
+
|
|
62
|
+
def spatial_zone_summary(df, zone_col="ZONE", actual_score_col="ATSI_INSITU", pred_score_col="ATSI_RS"):
|
|
63
|
+
if zone_col not in df.columns:
|
|
64
|
+
return pd.DataFrame()
|
|
65
|
+
rows = []
|
|
66
|
+
for g, sub in df.groupby(zone_col):
|
|
67
|
+
row = regression_metrics(sub[actual_score_col], sub[pred_score_col], label=str(g))
|
|
68
|
+
row[zone_col] = g
|
|
69
|
+
rows.append(row)
|
|
70
|
+
return pd.DataFrame(rows)
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
SITE,SEASON,ZONE,SPLIT,CHL,SAL,Rhow_1,Rhow_2,Rhow_3,Rhow_4,Rhow_5,Rhow_6,Rhow_7,Rhow_8,Rhow_9,Rhow_10,Rhow_11
|
|
2
|
+
S1,Winter,Upper,TRAIN,17.694,20.95,0.0154,0.01285,0.00539,0.02886,0.0062,0.00795,0.02391,0.00521,0.02326,0.00531,0.02649
|
|
3
|
+
S2,Spring,Middle,TRAIN,3.238,18.24,0.01458,0.01779,0.02632,0.01676,0.02376,0.01314,0.02592,0.02152,0.02945,0.00215,0.02053
|
|
4
|
+
S3,Summer,Lower,TRAIN,7.068,25.54,0.0047,0.00304,0.00619,0.0207,0.02697,0.00515,0.01458,0.01374,0.00182,0.01121,0.01388
|
|
5
|
+
S4,Autumn,Upper,TRAIN,6.241,29.65,0.00847,0.01516,0.00188,0.02096,0.01436,0.01993,0.00716,0.02757,0.00408,0.02278,0.02706
|
|
6
|
+
S5,Winter,Middle,TRAIN,6.046,32.7,0.00109,0.01869,0.01722,0.00542,0.00212,0.01428,0.0029,0.01287,0.00545,0.02678,0.01874
|
|
7
|
+
S6,Spring,Lower,TRAIN,20.678,28.01,0.01205,0.02257,0.01344,0.00521,0.01014,0.01242,0.00331,0.0018,0.01785,0.00777,0.02077
|
|
8
|
+
S1,Summer,Upper,TRAIN,23.237,32.67,0.0177,0.01347,0.0259,0.0144,0.00144,0.02434,0.00497,0.02206,0.01349,0.02838,0.02167
|
|
9
|
+
S2,Autumn,Middle,TRAIN,8.361,31.84,0.01339,0.00978,0.00566,0.01614,0.01113,0.01431,0.00881,0.02959,0.00286,0.0033,0.01374
|
|
10
|
+
S3,Winter,Lower,TRAIN,20.854,21.49,0.02522,0.00117,0.01145,0.00889,0.02012,0.02106,0.02354,0.01452,0.0091,0.00327,0.0095
|
|
11
|
+
S4,Spring,Upper,TRAIN,22.468,31.86,0.01888,0.02294,0.01957,0.00622,0.00869,0.02368,0.00766,0.02863,0.01233,0.02146,0.02672
|
|
12
|
+
S5,Summer,Middle,TRAIN,13.798,29.69,0.00872,0.00325,0.028,0.01561,0.01991,0.01883,0.01323,0.00572,0.01567,0.00297,0.0117
|
|
13
|
+
S6,Autumn,Lower,TRAIN,7.634,22.45,0.02452,0.01521,0.00859,0.02013,0.0279,0.02888,0.01429,0.00717,0.02292,0.02915,0.01367
|
|
14
|
+
S1,Winter,Upper,TRAIN,20.958,30.75,0.01549,0.00983,0.02095,0.00763,0.01452,0.01698,0.00998,0.0109,0.01518,0.00895,0.00901
|
|
15
|
+
S2,Spring,Middle,TRAIN,6.917,31.84,0.02301,0.02538,0.00532,0.00733,0.00701,0.00735,0.00613,0.01277,0.02495,0.00473,0.01706
|
|
16
|
+
S3,Summer,Lower,TEST,19.054,22.79,0.01742,0.02856,0.02246,0.02869,0.02776,0.01671,0.00877,0.02708,0.01667,0.02329,0.01661
|
|
17
|
+
S4,Autumn,Upper,TEST,16.489,26.43,0.01369,0.01025,0.0095,0.02202,0.02843,0.00809,0.02572,0.0292,0.01136,0.01325,0.01352
|
|
18
|
+
S5,Winter,Middle,TEST,23.33,19.14,0.01249,0.02704,0.00807,0.002,0.00491,0.00342,0.02983,0.0158,0.02437,0.0271,0.01125
|
|
19
|
+
S6,Spring,Lower,TEST,7.334,27.33,0.00164,0.01079,0.00766,0.02947,0.02709,0.02293,0.01175,0.02121,0.0064,0.01528,0.01671
|
|
20
|
+
S1,Summer,Upper,TEST,20.38,21.81,0.01461,0.02455,0.01175,0.00125,0.00947,0.00867,0.01439,0.0192,0.02679,0.00123,0.01208
|
|
21
|
+
S2,Autumn,Middle,TEST,13.918,30.24,0.01908,0.02417,0.01053,0.00868,0.00472,0.0101,0.01071,0.02596,0.02053,0.00574,0.02359
|
|
22
|
+
S3,Winter,Lower,INDEPENDENT,7.326,20.78,0.02844,0.02,0.01877,0.0276,0.00442,0.02966,0.0116,0.00413,0.02865,0.00388,0.00545
|
|
23
|
+
S4,Spring,Upper,INDEPENDENT,5.816,23.0,0.01362,0.00763,0.0168,0.00333,0.02302,0.01262,0.02093,0.01729,0.00709,0.00626,0.02189
|
|
24
|
+
S5,Summer,Middle,INDEPENDENT,13.449,18.23,0.01508,0.00499,0.02274,0.02579,0.00356,0.0138,0.02232,0.02444,0.00775,0.00885,0.00705
|
|
25
|
+
S6,Autumn,Lower,INDEPENDENT,15.403,18.52,0.01605,0.01331,0.00955,0.0052,0.02313,0.0277,0.01019,0.02345,0.01424,0.00319,0.02803
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
SITE SEASON ZONE SPLIT CHL SAL Rhow_1 Rhow_2 Rhow_3 Rhow_4 Rhow_5 Rhow_6 Rhow_7 Rhow_8 Rhow_9 Rhow_10 Rhow_11
|
|
2
|
+
S1 Winter Upper TRAIN 17.694 20.95 0.0154 0.01285 0.00539 0.02886 0.0062 0.00795 0.02391 0.00521 0.02326 0.00531 0.02649
|
|
3
|
+
S2 Spring Middle TRAIN 3.238 18.24 0.01458 0.01779 0.02632 0.01676 0.02376 0.01314 0.02592 0.02152 0.02945 0.00215 0.02053
|
|
4
|
+
S3 Summer Lower TRAIN 7.068 25.54 0.0047 0.00304 0.00619 0.0207 0.02697 0.00515 0.01458 0.01374 0.00182 0.01121 0.01388
|
|
5
|
+
S4 Autumn Upper TRAIN 6.241 29.65 0.00847 0.01516 0.00188 0.02096 0.01436 0.01993 0.00716 0.02757 0.00408 0.02278 0.02706
|
|
6
|
+
S5 Winter Middle TRAIN 6.046 32.7 0.00109 0.01869 0.01722 0.00542 0.00212 0.01428 0.0029 0.01287 0.00545 0.02678 0.01874
|
|
7
|
+
S6 Spring Lower TRAIN 20.678 28.01 0.01205 0.02257 0.01344 0.00521 0.01014 0.01242 0.00331 0.0018 0.01785 0.00777 0.02077
|
|
8
|
+
S1 Summer Upper TRAIN 23.237 32.67 0.0177 0.01347 0.0259 0.0144 0.00144 0.02434 0.00497 0.02206 0.01349 0.02838 0.02167
|
|
9
|
+
S2 Autumn Middle TRAIN 8.361 31.84 0.01339 0.00978 0.00566 0.01614 0.01113 0.01431 0.00881 0.02959 0.00286 0.0033 0.01374
|
|
10
|
+
S3 Winter Lower TRAIN 20.854 21.49 0.02522 0.00117 0.01145 0.00889 0.02012 0.02106 0.02354 0.01452 0.0091 0.00327 0.0095
|
|
11
|
+
S4 Spring Upper TRAIN 22.468 31.86 0.01888 0.02294 0.01957 0.00622 0.00869 0.02368 0.00766 0.02863 0.01233 0.02146 0.02672
|
|
12
|
+
S5 Summer Middle TRAIN 13.798 29.69 0.00872 0.00325 0.028 0.01561 0.01991 0.01883 0.01323 0.00572 0.01567 0.00297 0.0117
|
|
13
|
+
S6 Autumn Lower TRAIN 7.634 22.45 0.02452 0.01521 0.00859 0.02013 0.0279 0.02888 0.01429 0.00717 0.02292 0.02915 0.01367
|
|
14
|
+
S1 Winter Upper TRAIN 20.958 30.75 0.01549 0.00983 0.02095 0.00763 0.01452 0.01698 0.00998 0.0109 0.01518 0.00895 0.00901
|
|
15
|
+
S2 Spring Middle TRAIN 6.917 31.84 0.02301 0.02538 0.00532 0.00733 0.00701 0.00735 0.00613 0.01277 0.02495 0.00473 0.01706
|
|
16
|
+
S3 Summer Lower TEST 19.054 22.79 0.01742 0.02856 0.02246 0.02869 0.02776 0.01671 0.00877 0.02708 0.01667 0.02329 0.01661
|
|
17
|
+
S4 Autumn Upper TEST 16.489 26.43 0.01369 0.01025 0.0095 0.02202 0.02843 0.00809 0.02572 0.0292 0.01136 0.01325 0.01352
|
|
18
|
+
S5 Winter Middle TEST 23.33 19.14 0.01249 0.02704 0.00807 0.002 0.00491 0.00342 0.02983 0.0158 0.02437 0.0271 0.01125
|
|
19
|
+
S6 Spring Lower TEST 7.334 27.33 0.00164 0.01079 0.00766 0.02947 0.02709 0.02293 0.01175 0.02121 0.0064 0.01528 0.01671
|
|
20
|
+
S1 Summer Upper TEST 20.38 21.81 0.01461 0.02455 0.01175 0.00125 0.00947 0.00867 0.01439 0.0192 0.02679 0.00123 0.01208
|
|
21
|
+
S2 Autumn Middle TEST 13.918 30.24 0.01908 0.02417 0.01053 0.00868 0.00472 0.0101 0.01071 0.02596 0.02053 0.00574 0.02359
|
|
22
|
+
S3 Winter Lower INDEPENDENT 7.326 20.78 0.02844 0.02 0.01877 0.0276 0.00442 0.02966 0.0116 0.00413 0.02865 0.00388 0.00545
|
|
23
|
+
S4 Spring Upper INDEPENDENT 5.816 23.0 0.01362 0.00763 0.0168 0.00333 0.02302 0.01262 0.02093 0.01729 0.00709 0.00626 0.02189
|
|
24
|
+
S5 Summer Middle INDEPENDENT 13.449 18.23 0.01508 0.00499 0.02274 0.02579 0.00356 0.0138 0.02232 0.02444 0.00775 0.00885 0.00705
|
|
25
|
+
S6 Autumn Lower INDEPENDENT 15.403 18.52 0.01605 0.01331 0.00955 0.0052 0.02313 0.0277 0.01019 0.02345 0.01424 0.00319 0.02803
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
SITE,SEASON,ZONE,SPLIT,CHL,SAL,Rhow_1,Rhow_2,Rhow_3,Rhow_4,Rhow_5,Rhow_6,Rhow_7,Rhow_8,Rhow_9,Rhow_10,Rhow_11
|
|
2
|
+
S1,Winter,Upper,TRAIN,17.694,20.95,0.0154,0.01285,0.00539,0.02886,0.0062,0.00795,0.02391,0.00521,0.02326,0.00531,0.02649
|
|
3
|
+
S2,Spring,Middle,TRAIN,3.238,18.24,0.01458,0.01779,0.02632,0.01676,0.02376,0.01314,0.02592,0.02152,0.02945,0.00215,0.02053
|
|
4
|
+
S3,Summer,Lower,TRAIN,7.068,25.54,0.0047,0.00304,0.00619,0.0207,0.02697,0.00515,0.01458,0.01374,0.00182,0.01121,0.01388
|
|
5
|
+
S4,Autumn,Upper,TRAIN,6.241,29.65,0.00847,0.01516,0.00188,0.02096,0.01436,0.01993,0.00716,0.02757,0.00408,0.02278,0.02706
|
|
6
|
+
S5,Winter,Middle,TRAIN,6.046,32.7,0.00109,0.01869,0.01722,0.00542,0.00212,0.01428,0.0029,0.01287,0.00545,0.02678,0.01874
|
|
7
|
+
S6,Spring,Lower,TRAIN,20.678,28.01,0.01205,0.02257,0.01344,0.00521,0.01014,0.01242,0.00331,0.0018,0.01785,0.00777,0.02077
|
|
8
|
+
S1,Summer,Upper,TRAIN,23.237,32.67,0.0177,0.01347,0.0259,0.0144,0.00144,0.02434,0.00497,0.02206,0.01349,0.02838,0.02167
|
|
9
|
+
S2,Autumn,Middle,TRAIN,8.361,31.84,0.01339,0.00978,0.00566,0.01614,0.01113,0.01431,0.00881,0.02959,0.00286,0.0033,0.01374
|
|
10
|
+
S3,Winter,Lower,TRAIN,20.854,21.49,0.02522,0.00117,0.01145,0.00889,0.02012,0.02106,0.02354,0.01452,0.0091,0.00327,0.0095
|
|
11
|
+
S4,Spring,Upper,TRAIN,22.468,31.86,0.01888,0.02294,0.01957,0.00622,0.00869,0.02368,0.00766,0.02863,0.01233,0.02146,0.02672
|
|
12
|
+
S5,Summer,Middle,TRAIN,13.798,29.69,0.00872,0.00325,0.028,0.01561,0.01991,0.01883,0.01323,0.00572,0.01567,0.00297,0.0117
|
|
13
|
+
S6,Autumn,Lower,TRAIN,7.634,22.45,0.02452,0.01521,0.00859,0.02013,0.0279,0.02888,0.01429,0.00717,0.02292,0.02915,0.01367
|
|
14
|
+
S1,Winter,Upper,TRAIN,20.958,30.75,0.01549,0.00983,0.02095,0.00763,0.01452,0.01698,0.00998,0.0109,0.01518,0.00895,0.00901
|
|
15
|
+
S2,Spring,Middle,TRAIN,6.917,31.84,0.02301,0.02538,0.00532,0.00733,0.00701,0.00735,0.00613,0.01277,0.02495,0.00473,0.01706
|
|
16
|
+
S3,Summer,Lower,TEST,19.054,22.79,0.01742,0.02856,0.02246,0.02869,0.02776,0.01671,0.00877,0.02708,0.01667,0.02329,0.01661
|
|
17
|
+
S4,Autumn,Upper,TEST,16.489,26.43,0.01369,0.01025,0.0095,0.02202,0.02843,0.00809,0.02572,0.0292,0.01136,0.01325,0.01352
|
|
18
|
+
S5,Winter,Middle,TEST,23.33,19.14,0.01249,0.02704,0.00807,0.002,0.00491,0.00342,0.02983,0.0158,0.02437,0.0271,0.01125
|
|
19
|
+
S6,Spring,Lower,TEST,7.334,27.33,0.00164,0.01079,0.00766,0.02947,0.02709,0.02293,0.01175,0.02121,0.0064,0.01528,0.01671
|
|
20
|
+
S1,Summer,Upper,TEST,20.38,21.81,0.01461,0.02455,0.01175,0.00125,0.00947,0.00867,0.01439,0.0192,0.02679,0.00123,0.01208
|
|
21
|
+
S2,Autumn,Middle,TEST,13.918,30.24,0.01908,0.02417,0.01053,0.00868,0.00472,0.0101,0.01071,0.02596,0.02053,0.00574,0.02359
|
|
22
|
+
S3,Winter,Lower,INDEPENDENT,7.326,20.78,0.02844,0.02,0.01877,0.0276,0.00442,0.02966,0.0116,0.00413,0.02865,0.00388,0.00545
|
|
23
|
+
S4,Spring,Upper,INDEPENDENT,5.816,23.0,0.01362,0.00763,0.0168,0.00333,0.02302,0.01262,0.02093,0.01729,0.00709,0.00626,0.02189
|
|
24
|
+
S5,Summer,Middle,INDEPENDENT,13.449,18.23,0.01508,0.00499,0.02274,0.02579,0.00356,0.0138,0.02232,0.02444,0.00775,0.00885,0.00705
|
|
25
|
+
S6,Autumn,Lower,INDEPENDENT,15.403,18.52,0.01605,0.01331,0.00955,0.0052,0.02313,0.0277,0.01019,0.02345,0.01424,0.00319,0.02803
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
SITE SEASON ZONE SPLIT CHL SAL Rhow_1 Rhow_2 Rhow_3 Rhow_4 Rhow_5 Rhow_6 Rhow_7 Rhow_8 Rhow_9 Rhow_10 Rhow_11
|
|
2
|
+
S1 Winter Upper TRAIN 17.694 20.95 0.0154 0.01285 0.00539 0.02886 0.0062 0.00795 0.02391 0.00521 0.02326 0.00531 0.02649
|
|
3
|
+
S2 Spring Middle TRAIN 3.238 18.24 0.01458 0.01779 0.02632 0.01676 0.02376 0.01314 0.02592 0.02152 0.02945 0.00215 0.02053
|
|
4
|
+
S3 Summer Lower TRAIN 7.068 25.54 0.0047 0.00304 0.00619 0.0207 0.02697 0.00515 0.01458 0.01374 0.00182 0.01121 0.01388
|
|
5
|
+
S4 Autumn Upper TRAIN 6.241 29.65 0.00847 0.01516 0.00188 0.02096 0.01436 0.01993 0.00716 0.02757 0.00408 0.02278 0.02706
|
|
6
|
+
S5 Winter Middle TRAIN 6.046 32.7 0.00109 0.01869 0.01722 0.00542 0.00212 0.01428 0.0029 0.01287 0.00545 0.02678 0.01874
|
|
7
|
+
S6 Spring Lower TRAIN 20.678 28.01 0.01205 0.02257 0.01344 0.00521 0.01014 0.01242 0.00331 0.0018 0.01785 0.00777 0.02077
|
|
8
|
+
S1 Summer Upper TRAIN 23.237 32.67 0.0177 0.01347 0.0259 0.0144 0.00144 0.02434 0.00497 0.02206 0.01349 0.02838 0.02167
|
|
9
|
+
S2 Autumn Middle TRAIN 8.361 31.84 0.01339 0.00978 0.00566 0.01614 0.01113 0.01431 0.00881 0.02959 0.00286 0.0033 0.01374
|
|
10
|
+
S3 Winter Lower TRAIN 20.854 21.49 0.02522 0.00117 0.01145 0.00889 0.02012 0.02106 0.02354 0.01452 0.0091 0.00327 0.0095
|
|
11
|
+
S4 Spring Upper TRAIN 22.468 31.86 0.01888 0.02294 0.01957 0.00622 0.00869 0.02368 0.00766 0.02863 0.01233 0.02146 0.02672
|
|
12
|
+
S5 Summer Middle TRAIN 13.798 29.69 0.00872 0.00325 0.028 0.01561 0.01991 0.01883 0.01323 0.00572 0.01567 0.00297 0.0117
|
|
13
|
+
S6 Autumn Lower TRAIN 7.634 22.45 0.02452 0.01521 0.00859 0.02013 0.0279 0.02888 0.01429 0.00717 0.02292 0.02915 0.01367
|
|
14
|
+
S1 Winter Upper TRAIN 20.958 30.75 0.01549 0.00983 0.02095 0.00763 0.01452 0.01698 0.00998 0.0109 0.01518 0.00895 0.00901
|
|
15
|
+
S2 Spring Middle TRAIN 6.917 31.84 0.02301 0.02538 0.00532 0.00733 0.00701 0.00735 0.00613 0.01277 0.02495 0.00473 0.01706
|
|
16
|
+
S3 Summer Lower TEST 19.054 22.79 0.01742 0.02856 0.02246 0.02869 0.02776 0.01671 0.00877 0.02708 0.01667 0.02329 0.01661
|
|
17
|
+
S4 Autumn Upper TEST 16.489 26.43 0.01369 0.01025 0.0095 0.02202 0.02843 0.00809 0.02572 0.0292 0.01136 0.01325 0.01352
|
|
18
|
+
S5 Winter Middle TEST 23.33 19.14 0.01249 0.02704 0.00807 0.002 0.00491 0.00342 0.02983 0.0158 0.02437 0.0271 0.01125
|
|
19
|
+
S6 Spring Lower TEST 7.334 27.33 0.00164 0.01079 0.00766 0.02947 0.02709 0.02293 0.01175 0.02121 0.0064 0.01528 0.01671
|
|
20
|
+
S1 Summer Upper TEST 20.38 21.81 0.01461 0.02455 0.01175 0.00125 0.00947 0.00867 0.01439 0.0192 0.02679 0.00123 0.01208
|
|
21
|
+
S2 Autumn Middle TEST 13.918 30.24 0.01908 0.02417 0.01053 0.00868 0.00472 0.0101 0.01071 0.02596 0.02053 0.00574 0.02359
|
|
22
|
+
S3 Winter Lower INDEPENDENT 7.326 20.78 0.02844 0.02 0.01877 0.0276 0.00442 0.02966 0.0116 0.00413 0.02865 0.00388 0.00545
|
|
23
|
+
S4 Spring Upper INDEPENDENT 5.816 23.0 0.01362 0.00763 0.0168 0.00333 0.02302 0.01262 0.02093 0.01729 0.00709 0.00626 0.02189
|
|
24
|
+
S5 Summer Middle INDEPENDENT 13.449 18.23 0.01508 0.00499 0.02274 0.02579 0.00356 0.0138 0.02232 0.02444 0.00775 0.00885 0.00705
|
|
25
|
+
S6 Autumn Lower INDEPENDENT 15.403 18.52 0.01605 0.01331 0.00955 0.0052 0.02313 0.0277 0.01019 0.02345 0.01424 0.00319 0.02803
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "RSATSIModel"
|
|
7
|
+
version = "2.5.0"
|
|
8
|
+
description = "Python package for computing ATSI and RS-ATSI using CHL, SAL, and Sentinel-3 OLCI-style data."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Dr Md Galal Uddin", email = "jalaluddinbd1987@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"pandas>=1.5.0",
|
|
17
|
+
"numpy>=1.23.0",
|
|
18
|
+
"matplotlib>=3.6.0",
|
|
19
|
+
"openpyxl>=3.1.0",
|
|
20
|
+
"scikit-learn>=1.2.0"
|
|
21
|
+
]
|