solarpandas 0.1.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.
- solarpandas-0.1.0/PKG-INFO +335 -0
- solarpandas-0.1.0/README.md +298 -0
- solarpandas-0.1.0/pyproject.toml +81 -0
- solarpandas-0.1.0/src/solarpandas/__init__.py +33 -0
- solarpandas-0.1.0/src/solarpandas/accessors/__init__.py +36 -0
- solarpandas-0.1.0/src/solarpandas/accessors/clearsky.py +265 -0
- solarpandas-0.1.0/src/solarpandas/accessors/param.py +105 -0
- solarpandas-0.1.0/src/solarpandas/accessors/qcflag.py +170 -0
- solarpandas-0.1.0/src/solarpandas/accessors/qcontrol.py +437 -0
- solarpandas-0.1.0/src/solarpandas/accessors/solarplot.py +624 -0
- solarpandas-0.1.0/src/solarpandas/accessors/solpos.py +375 -0
- solarpandas-0.1.0/src/solarpandas/base.py +463 -0
- solarpandas-0.1.0/src/solarpandas/config.py +263 -0
- solarpandas-0.1.0/src/solarpandas/helpers.py +83 -0
- solarpandas-0.1.0/src/solarpandas/iohelpers.py +53 -0
- solarpandas-0.1.0/src/solarpandas/logtools.py +103 -0
- solarpandas-0.1.0/src/solarpandas/mplstyles/__init__.py +28 -0
- solarpandas-0.1.0/src/solarpandas/mplstyles/dtmap.mplstyle +5 -0
- solarpandas-0.1.0/src/solarpandas/mplstyles/qc.mplstyle +9 -0
- solarpandas-0.1.0/src/solarpandas/origin/__init__.py +1 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/__init__.py +6 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/cf-metadata.json +425 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/core.py +905 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/helpers.py +203 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/lr_parsers.py +637 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/tables.py +124 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/types.py +12 -0
- solarpandas-0.1.0/src/solarpandas/origin/bsrn/utils.py +68 -0
- solarpandas-0.1.0/src/solarpandas/py.typed +0 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/Kspace.py +303 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/__init__.py +7 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/closure.py +116 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/erl.py +170 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/helpers.py +83 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/ppl.py +227 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/qcrad.py +23 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/timeshift.py +96 -0
- solarpandas-0.1.0/src/solarpandas/qcontrol/tracker.py +112 -0
- solarpandas-0.1.0/src/solarpandas/sample_data/__init__.py +35 -0
- solarpandas-0.1.0/src/solarpandas/sample_data/car_bsrn_2016.parquet +0 -0
- solarpandas-0.1.0/src/solarpandas/types/__init__.py +7 -0
- solarpandas-0.1.0/src/solarpandas/types/annotated.py +79 -0
- solarpandas-0.1.0/src/solarpandas/types/qcflag.py +268 -0
- solarpandas-0.1.0/src/solarpandas/validate.py +334 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: solarpandas
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Analysis of solar irradiance time series with pandas
|
|
5
|
+
Keywords: solar,irradiance,timeseries,pandas,bsrn,photovoltaics
|
|
6
|
+
Author: Jose A Ruiz Arias
|
|
7
|
+
Author-email: Jose A Ruiz Arias <jararias@uma.es>
|
|
8
|
+
License: CC-BY-NC-SA-4.0
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Natural Language :: English
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
18
|
+
Classifier: License :: Free To Use But Restricted
|
|
19
|
+
Requires-Dist: colorcet>=3.2.1
|
|
20
|
+
Requires-Dist: datashader>=0.19.1
|
|
21
|
+
Requires-Dist: loguru>=0.7.3
|
|
22
|
+
Requires-Dist: lxml>=6.1.1
|
|
23
|
+
Requires-Dist: matplotlib>=3.10.9
|
|
24
|
+
Requires-Dist: numpy>=2.4.4
|
|
25
|
+
Requires-Dist: pandas>=3.0.2
|
|
26
|
+
Requires-Dist: platformdirs>=4.9.6
|
|
27
|
+
Requires-Dist: pyarrow>=24.0.0
|
|
28
|
+
Requires-Dist: sparta-solar>=0.1.0
|
|
29
|
+
Requires-Dist: sunwhere>=1.4.1
|
|
30
|
+
Requires-Dist: tomlkit>=0.15.0
|
|
31
|
+
Requires-Python: >=3.13
|
|
32
|
+
Project-URL: Documentation, https://jararias.github.io/solarpandas
|
|
33
|
+
Project-URL: Homepage, https://github.com/jararias/solarpandas
|
|
34
|
+
Project-URL: Issues, https://github.com/jararias/solarpandas/issues
|
|
35
|
+
Project-URL: Repository, https://github.com/jararias/solarpandas
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/logo_solarpandas_azul_fondo_transparente.png" alt="logo" width="25%">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
# solarpandas: pandas for solar resource assessment
|
|
44
|
+
|
|
45
|
+

|
|
46
|
+

|
|
47
|
+

|
|
48
|
+
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
|
49
|
+
|
|
50
|
+
𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is a personal project that I have been developing and using for my own research for years. It integrates under a common framework both standard methods in solar resource modeling and libraries and models I have developed myself. The incomparable extensibility of pandas makes it the perfect framework for this. The result is an advanced, modern, and sophisticated library that combines the unique power and versatility of pandas with the most widely used methods in solar resource modeling.
|
|
51
|
+
|
|
52
|
+
## Main features
|
|
53
|
+
|
|
54
|
+
- **𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 subclasses pandas Series and DataFrame** to embed site location metadata (latitude, longitude and elevation) and optional general-purpose custom metadata. This approach frees the user from having to pass location metadata to every individual routine, as it is automatically propagated across objects and only needs to be specified once, while retaining the rich API of pandas in the SolarSeries and SolarDataFrame objects.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
>>> import solarpandas as sp
|
|
58
|
+
|
|
59
|
+
# pandas class solarpandas class
|
|
60
|
+
# ------------ -----------------
|
|
61
|
+
# Series SolarSeries
|
|
62
|
+
# DataFrame SolarDataFrame
|
|
63
|
+
|
|
64
|
+
>>> sdf = sp.SolarSeries(
|
|
65
|
+
... data=np.linspace(500, 550, 6), # as in pandas Series
|
|
66
|
+
... index=pd.date_range("2026-06-01 10", periods=6, freq="30min"), # a sequence of datetimes, as required by pandas Series
|
|
67
|
+
... name="ghi",
|
|
68
|
+
... # metadata...
|
|
69
|
+
... latitude=36.949, # mandatory in solarpandas
|
|
70
|
+
... longitude=-3.823, # mandatory in solarpandas
|
|
71
|
+
... elevation=914, # if not providad, set to 0 meters above mean sea level
|
|
72
|
+
... custom_metadata={ # optional, following json standard rules
|
|
73
|
+
... "site": "Jayena",
|
|
74
|
+
... "network": "my-network",
|
|
75
|
+
... }
|
|
76
|
+
... )
|
|
77
|
+
>>> sdf
|
|
78
|
+
2026-06-01 10:00:00 500.0
|
|
79
|
+
2026-06-01 10:30:00 510.0
|
|
80
|
+
2026-06-01 11:00:00 520.0
|
|
81
|
+
2026-06-01 11:30:00 530.0
|
|
82
|
+
2026-06-01 12:00:00 540.0
|
|
83
|
+
2026-06-01 12:30:00 550.0
|
|
84
|
+
Freq: 30min, Name: ghi, dtype: float64
|
|
85
|
+
[site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
|
|
86
|
+
|
|
87
|
+
>>> sdf_hourly = sdf.resample("h").mean()
|
|
88
|
+
>>> sdf_hourly
|
|
89
|
+
2026-06-01 10:00:00 505.0
|
|
90
|
+
2026-06-01 11:00:00 525.0
|
|
91
|
+
2026-06-01 12:00:00 545.0
|
|
92
|
+
Freq: h, dtype: float64
|
|
93
|
+
[site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- SolarDataFrame instances **can be serialized and de-serialized** to and from `parquet` or `csv` files **keeping the original metadata**. This opens the door to standardized metadata for solar time series following cf-compliant rules.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
>>> sdf = sp.sample_data.load_carpentras_data()
|
|
100
|
+
>>> sdf.custom_metadata
|
|
101
|
+
{'station': 'CAR',
|
|
102
|
+
'location': 'Carpentras, France',
|
|
103
|
+
'network': 'BSRN',
|
|
104
|
+
'source': 'BSRN FTP server via solarpandas',
|
|
105
|
+
'institution': 'Jose A Ruiz-Arias (solarpandas dev) and BSRN data providers',
|
|
106
|
+
'contact': 'xxx@xxx.xxx',
|
|
107
|
+
'timestamp_alignment': 'center',
|
|
108
|
+
'surface_type': 'cultivated',
|
|
109
|
+
'topography_type': 'hilly, rural',
|
|
110
|
+
...
|
|
111
|
+
'variables': {
|
|
112
|
+
'ghi': {
|
|
113
|
+
'standard_name': 'surface_downwelling_shortwave_flux_in_air',
|
|
114
|
+
'long_name': 'global horizontal irradiance',
|
|
115
|
+
'short_name': 'ghi',
|
|
116
|
+
'units': 'W m-2',
|
|
117
|
+
'cell_methods': 'time: mean (interval: 1 minute)',
|
|
118
|
+
'bsrn_name': 'global_horizontal_avg'
|
|
119
|
+
},
|
|
120
|
+
...
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 provides **fast memory-cached accessors for** key aspects of solar resource modeling, such as the calculation of **solar position** (via [sunwhere](https://github.com/jararias/sunwhere)) **and clear-sky irradiance** (via [sparta-solar](https://github.com/jararias/sparta-solar)). These parameters are not stored as columns of the dataframe, keeping it clean and compact, but are instead exposed as virtual columns through the accessors.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
>>> sdf = sp.sample_data.load_carpentras_data()
|
|
129
|
+
>>> sdf
|
|
130
|
+
ghi dni dif
|
|
131
|
+
time
|
|
132
|
+
2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
|
|
133
|
+
2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
|
|
134
|
+
2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
|
|
135
|
+
... ... ... ...
|
|
136
|
+
2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
|
|
137
|
+
2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
|
|
138
|
+
2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
|
|
139
|
+
[527040 rows x 3 columns]
|
|
140
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
141
|
+
|
|
142
|
+
>>> sdf.solpos.zenith # solar zenith angle
|
|
143
|
+
time
|
|
144
|
+
2016-01-01 00:00:30+00:00 158.666033
|
|
145
|
+
2016-01-01 00:01:30+00:00 158.630072
|
|
146
|
+
2016-01-01 00:02:30+00:00 158.592202
|
|
147
|
+
...
|
|
148
|
+
2016-12-31 23:57:30+00:00 158.713107
|
|
149
|
+
2016-12-31 23:58:30+00:00 158.683687
|
|
150
|
+
2016-12-31 23:59:30+00:00 158.652329
|
|
151
|
+
Length: 527040, dtype: float64
|
|
152
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
153
|
+
|
|
154
|
+
>>> sdf.solpos.sunrise(units="utc") # sunrise time, UTC
|
|
155
|
+
time
|
|
156
|
+
2016-01-01 00:00:30+00:00 2016-01-01 07:37:23.580818129
|
|
157
|
+
2016-01-01 00:01:30+00:00 2016-01-01 07:37:23.564837855
|
|
158
|
+
2016-01-01 00:02:30+00:00 2016-01-01 07:37:23.548856487
|
|
159
|
+
...
|
|
160
|
+
2016-12-31 23:57:30+00:00 2017-01-01 07:37:05.570849828
|
|
161
|
+
2016-12-31 23:58:30+00:00 2017-01-01 07:37:05.553684227
|
|
162
|
+
2016-12-31 23:59:30+00:00 2017-01-01 07:37:05.536517540
|
|
163
|
+
Length: 527040, dtype: datetime64[ns]
|
|
164
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
165
|
+
|
|
166
|
+
>>> sdf.lta.ghi # clear-sky ghi assuming a long-term average clear-sky atmosphere
|
|
167
|
+
time
|
|
168
|
+
2016-01-01 00:00:30+00:00 0.0
|
|
169
|
+
2016-01-01 00:01:30+00:00 0.0
|
|
170
|
+
2016-01-01 00:02:30+00:00 0.0
|
|
171
|
+
...
|
|
172
|
+
2016-12-31 23:57:30+00:00 0.0
|
|
173
|
+
2016-12-31 23:58:30+00:00 0.0
|
|
174
|
+
2016-12-31 23:59:30+00:00 0.0
|
|
175
|
+
Length: 527040, dtype: float64
|
|
176
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
177
|
+
|
|
178
|
+
>>> sdf.cda.ghi # idem, but for a clean and dry clear-sky atmosphere
|
|
179
|
+
time
|
|
180
|
+
2016-01-01 00:00:30+00:00 0.0
|
|
181
|
+
2016-01-01 00:01:30+00:00 0.0
|
|
182
|
+
2016-01-01 00:02:30+00:00 0.0
|
|
183
|
+
...
|
|
184
|
+
2016-12-31 23:57:30+00:00 0.0
|
|
185
|
+
2016-12-31 23:58:30+00:00 0.0
|
|
186
|
+
2016-12-31 23:59:30+00:00 0.0
|
|
187
|
+
Length: 527040, dtype: float64
|
|
188
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
189
|
+
|
|
190
|
+
>>> sdf.clearsky.ghi # idem, but using a preset clear-sky atmosphere from sparta-solar
|
|
191
|
+
time
|
|
192
|
+
2016-01-01 00:00:30+00:00 0.0
|
|
193
|
+
2016-01-01 00:01:30+00:00 0.0
|
|
194
|
+
2016-01-01 00:02:30+00:00 0.0
|
|
195
|
+
...
|
|
196
|
+
2016-12-31 23:57:30+00:00 0.0
|
|
197
|
+
2016-12-31 23:58:30+00:00 0.0
|
|
198
|
+
2016-12-31 23:59:30+00:00 0.0
|
|
199
|
+
Name: ghi, Length: 527040, dtype: float64
|
|
200
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
201
|
+
|
|
202
|
+
>>> sdf.clearsky.compute( # ad-hoc non-cached calculation
|
|
203
|
+
... atmosphere="crs_soda",
|
|
204
|
+
... model="SPARTA")
|
|
205
|
+
dni dhi dif ghi csi
|
|
206
|
+
time
|
|
207
|
+
2016-01-01 00:00:30 0.0 0.0 0.0 0.0 0.0
|
|
208
|
+
2016-01-01 00:01:30 0.0 0.0 0.0 0.0 0.0
|
|
209
|
+
2016-01-01 00:02:30 0.0 0.0 0.0 0.0 0.0
|
|
210
|
+
... ... ... ... ... ...
|
|
211
|
+
2016-12-31 23:57:30 0.0 0.0 0.0 0.0 0.0
|
|
212
|
+
2016-12-31 23:58:30 0.0 0.0 0.0 0.0 0.0
|
|
213
|
+
2016-12-31 23:59:30 0.0 0.0 0.0 0.0 0.0
|
|
214
|
+
[527040 rows x 5 columns]
|
|
215
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is **shipped with BSRN high-level data retrieval** and parsing utilities. When BSRN data is requested for the first time, it is downloaded, parsed, and archived locally in `parquet` format for fast subsequent access.
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
>>> from solarpandas.origin import bsrn
|
|
222
|
+
|
|
223
|
+
>>> year_table = bsrn.data_availability(update="auto", as_year_table=True)
|
|
224
|
+
>>> print(year_table)
|
|
225
|
+
site | 9 0 0 1 1 2 2
|
|
226
|
+
| 5 0 5 0 5 0 5
|
|
227
|
+
-----+------------------------------------
|
|
228
|
+
abs | ######
|
|
229
|
+
aes |
|
|
230
|
+
ale | ###########
|
|
231
|
+
asp | ##########################
|
|
232
|
+
bar | ###############################
|
|
233
|
+
ber | ###################### ## #
|
|
234
|
+
bil | ###########################
|
|
235
|
+
... ...
|
|
236
|
+
|
|
237
|
+
# 2) load station metadata (cached locally)
|
|
238
|
+
>>> meta = bsrn.load_metadata(update="auto")
|
|
239
|
+
|
|
240
|
+
# 3) load BSRN measurements for one station/year
|
|
241
|
+
>>> sdf = bsrn.load_data(
|
|
242
|
+
... site="car",
|
|
243
|
+
... years=2016,
|
|
244
|
+
... logical_record="LR0100",
|
|
245
|
+
... group="essential")
|
|
246
|
+
ghi dni dif
|
|
247
|
+
time
|
|
248
|
+
2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
|
|
249
|
+
2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
|
|
250
|
+
2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
|
|
251
|
+
... ... ... ...
|
|
252
|
+
2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
|
|
253
|
+
2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
|
|
254
|
+
2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
|
|
255
|
+
[527040 rows x 3 columns]
|
|
256
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- It has **built-in quality-control workflows** enhanced with a tailored qc-specific ExtensionDType, `qcflag`. The QC workflow is memory-cached and the 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴's `qcflag` dtype provides direct access to QC-specific methods via the `.flag` accessor.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
>>> sdf = sp.sample_data.load_carpentras_data()
|
|
263
|
+
>>> sdf.qc.tests # perform the tests and return them
|
|
264
|
+
ghi_ppl dif_ppl ... closure trackeroff
|
|
265
|
+
time ...
|
|
266
|
+
2016-01-01 00:00:30+00:00 0 0 ... 0 0
|
|
267
|
+
2016-01-01 00:01:30+00:00 0 0 ... 0 0
|
|
268
|
+
2016-01-01 00:02:30+00:00 0 0 ... 0 0
|
|
269
|
+
... ... ... ... ... ...
|
|
270
|
+
2016-12-31 23:57:30+00:00 0 0 ... 0 0
|
|
271
|
+
2016-12-31 23:58:30+00:00 0 0 ... 0 0
|
|
272
|
+
2016-12-31 23:59:30+00:00 0 0 ... 0 0
|
|
273
|
+
[527040 rows x 13 columns]
|
|
274
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
|
|
275
|
+
|
|
276
|
+
>>> sdf.qc.ghi_ppl # access individual tests
|
|
277
|
+
time
|
|
278
|
+
2016-01-01 00:00:30+00:00 0
|
|
279
|
+
2016-01-01 00:01:30+00:00 0
|
|
280
|
+
2016-01-01 00:02:30+00:00 0
|
|
281
|
+
..
|
|
282
|
+
2016-12-31 23:57:30+00:00 0
|
|
283
|
+
2016-12-31 23:58:30+00:00 0
|
|
284
|
+
2016-12-31 23:59:30+00:00 0
|
|
285
|
+
Name: ghi_ppl, Length: 527040, dtype: qcflag
|
|
286
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
|
|
287
|
+
|
|
288
|
+
>>> sdf.qc.ghi_ppl.dtype # tests data have a special dtype `qcflag`
|
|
289
|
+
QCFlagDType()
|
|
290
|
+
|
|
291
|
+
# the type `qcflag` provides specific functionalities throught the `.flag` accessor
|
|
292
|
+
>>> sdf.qc.ghi_ppl.flag.counts() # all data points in this dataset pass this test (by default, night time is excluded)
|
|
293
|
+
PASSED 265417
|
|
294
|
+
NOT_VERIFIABLE 1653
|
|
295
|
+
Name: count, dtype: int64
|
|
296
|
+
|
|
297
|
+
# and additional plotting methods:
|
|
298
|
+
>>> sdf.qc.ghi_ppl.flag.pieplot()
|
|
299
|
+
>>> sdf.qc.ghi_ppl.flag.heatmap()
|
|
300
|
+
>>> sdf.qc.ghi_ppl.flag.plot(sdf)
|
|
301
|
+
|
|
302
|
+
# 4) bolean masks from sets of individual tests
|
|
303
|
+
>>> failed_ghi = sdf.qc.failed(component="ghi")
|
|
304
|
+
>>> passed_all = sdf.qc.passed()
|
|
305
|
+
|
|
306
|
+
# 5) mask failed data points
|
|
307
|
+
>>> sdf_masked = sdf.qc.mask_failed(component="ghi")
|
|
308
|
+
>>> sdf.qc.heatmap(component="ghi")
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
- It provides specialized plotting helpers for solar datasets through the ``.solarplot`` accessor.
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
# diurnal line plot
|
|
315
|
+
>>> fig1 = sdf.solarplot.diurnal(column="ghi")
|
|
316
|
+
|
|
317
|
+
# date-time heatmap
|
|
318
|
+
>>> fig2 = sdf.solarplot.heatmap(column="ghi", time_ref="tst", twilight_line=True)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Installation
|
|
322
|
+
|
|
323
|
+
With pip:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
pip install solarpandas
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
and with [uv](https://docs.astral.sh/uv/):
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
uv add solarpandas
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Find further details in the [documentation](https://jararias.github.io/solarpandas).
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/jararias/solarpandas/main/docs/images/logo_solarpandas_azul_fondo_transparente.png" alt="logo" width="25%">
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
# solarpandas: pandas for solar resource assessment
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
|
12
|
+
|
|
13
|
+
𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is a personal project that I have been developing and using for my own research for years. It integrates under a common framework both standard methods in solar resource modeling and libraries and models I have developed myself. The incomparable extensibility of pandas makes it the perfect framework for this. The result is an advanced, modern, and sophisticated library that combines the unique power and versatility of pandas with the most widely used methods in solar resource modeling.
|
|
14
|
+
|
|
15
|
+
## Main features
|
|
16
|
+
|
|
17
|
+
- **𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 subclasses pandas Series and DataFrame** to embed site location metadata (latitude, longitude and elevation) and optional general-purpose custom metadata. This approach frees the user from having to pass location metadata to every individual routine, as it is automatically propagated across objects and only needs to be specified once, while retaining the rich API of pandas in the SolarSeries and SolarDataFrame objects.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
>>> import solarpandas as sp
|
|
21
|
+
|
|
22
|
+
# pandas class solarpandas class
|
|
23
|
+
# ------------ -----------------
|
|
24
|
+
# Series SolarSeries
|
|
25
|
+
# DataFrame SolarDataFrame
|
|
26
|
+
|
|
27
|
+
>>> sdf = sp.SolarSeries(
|
|
28
|
+
... data=np.linspace(500, 550, 6), # as in pandas Series
|
|
29
|
+
... index=pd.date_range("2026-06-01 10", periods=6, freq="30min"), # a sequence of datetimes, as required by pandas Series
|
|
30
|
+
... name="ghi",
|
|
31
|
+
... # metadata...
|
|
32
|
+
... latitude=36.949, # mandatory in solarpandas
|
|
33
|
+
... longitude=-3.823, # mandatory in solarpandas
|
|
34
|
+
... elevation=914, # if not providad, set to 0 meters above mean sea level
|
|
35
|
+
... custom_metadata={ # optional, following json standard rules
|
|
36
|
+
... "site": "Jayena",
|
|
37
|
+
... "network": "my-network",
|
|
38
|
+
... }
|
|
39
|
+
... )
|
|
40
|
+
>>> sdf
|
|
41
|
+
2026-06-01 10:00:00 500.0
|
|
42
|
+
2026-06-01 10:30:00 510.0
|
|
43
|
+
2026-06-01 11:00:00 520.0
|
|
44
|
+
2026-06-01 11:30:00 530.0
|
|
45
|
+
2026-06-01 12:00:00 540.0
|
|
46
|
+
2026-06-01 12:30:00 550.0
|
|
47
|
+
Freq: 30min, Name: ghi, dtype: float64
|
|
48
|
+
[site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
|
|
49
|
+
|
|
50
|
+
>>> sdf_hourly = sdf.resample("h").mean()
|
|
51
|
+
>>> sdf_hourly
|
|
52
|
+
2026-06-01 10:00:00 505.0
|
|
53
|
+
2026-06-01 11:00:00 525.0
|
|
54
|
+
2026-06-01 12:00:00 545.0
|
|
55
|
+
Freq: h, dtype: float64
|
|
56
|
+
[site=Jayena/my-network latitude=36.9490° longitude=-3.8230° elevation=914.0 m]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- SolarDataFrame instances **can be serialized and de-serialized** to and from `parquet` or `csv` files **keeping the original metadata**. This opens the door to standardized metadata for solar time series following cf-compliant rules.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
>>> sdf = sp.sample_data.load_carpentras_data()
|
|
63
|
+
>>> sdf.custom_metadata
|
|
64
|
+
{'station': 'CAR',
|
|
65
|
+
'location': 'Carpentras, France',
|
|
66
|
+
'network': 'BSRN',
|
|
67
|
+
'source': 'BSRN FTP server via solarpandas',
|
|
68
|
+
'institution': 'Jose A Ruiz-Arias (solarpandas dev) and BSRN data providers',
|
|
69
|
+
'contact': 'xxx@xxx.xxx',
|
|
70
|
+
'timestamp_alignment': 'center',
|
|
71
|
+
'surface_type': 'cultivated',
|
|
72
|
+
'topography_type': 'hilly, rural',
|
|
73
|
+
...
|
|
74
|
+
'variables': {
|
|
75
|
+
'ghi': {
|
|
76
|
+
'standard_name': 'surface_downwelling_shortwave_flux_in_air',
|
|
77
|
+
'long_name': 'global horizontal irradiance',
|
|
78
|
+
'short_name': 'ghi',
|
|
79
|
+
'units': 'W m-2',
|
|
80
|
+
'cell_methods': 'time: mean (interval: 1 minute)',
|
|
81
|
+
'bsrn_name': 'global_horizontal_avg'
|
|
82
|
+
},
|
|
83
|
+
...
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 provides **fast memory-cached accessors for** key aspects of solar resource modeling, such as the calculation of **solar position** (via [sunwhere](https://github.com/jararias/sunwhere)) **and clear-sky irradiance** (via [sparta-solar](https://github.com/jararias/sparta-solar)). These parameters are not stored as columns of the dataframe, keeping it clean and compact, but are instead exposed as virtual columns through the accessors.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
>>> sdf = sp.sample_data.load_carpentras_data()
|
|
92
|
+
>>> sdf
|
|
93
|
+
ghi dni dif
|
|
94
|
+
time
|
|
95
|
+
2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
|
|
96
|
+
2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
|
|
97
|
+
2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
|
|
98
|
+
... ... ... ...
|
|
99
|
+
2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
|
|
100
|
+
2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
|
|
101
|
+
2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
|
|
102
|
+
[527040 rows x 3 columns]
|
|
103
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
104
|
+
|
|
105
|
+
>>> sdf.solpos.zenith # solar zenith angle
|
|
106
|
+
time
|
|
107
|
+
2016-01-01 00:00:30+00:00 158.666033
|
|
108
|
+
2016-01-01 00:01:30+00:00 158.630072
|
|
109
|
+
2016-01-01 00:02:30+00:00 158.592202
|
|
110
|
+
...
|
|
111
|
+
2016-12-31 23:57:30+00:00 158.713107
|
|
112
|
+
2016-12-31 23:58:30+00:00 158.683687
|
|
113
|
+
2016-12-31 23:59:30+00:00 158.652329
|
|
114
|
+
Length: 527040, dtype: float64
|
|
115
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
116
|
+
|
|
117
|
+
>>> sdf.solpos.sunrise(units="utc") # sunrise time, UTC
|
|
118
|
+
time
|
|
119
|
+
2016-01-01 00:00:30+00:00 2016-01-01 07:37:23.580818129
|
|
120
|
+
2016-01-01 00:01:30+00:00 2016-01-01 07:37:23.564837855
|
|
121
|
+
2016-01-01 00:02:30+00:00 2016-01-01 07:37:23.548856487
|
|
122
|
+
...
|
|
123
|
+
2016-12-31 23:57:30+00:00 2017-01-01 07:37:05.570849828
|
|
124
|
+
2016-12-31 23:58:30+00:00 2017-01-01 07:37:05.553684227
|
|
125
|
+
2016-12-31 23:59:30+00:00 2017-01-01 07:37:05.536517540
|
|
126
|
+
Length: 527040, dtype: datetime64[ns]
|
|
127
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
128
|
+
|
|
129
|
+
>>> sdf.lta.ghi # clear-sky ghi assuming a long-term average clear-sky atmosphere
|
|
130
|
+
time
|
|
131
|
+
2016-01-01 00:00:30+00:00 0.0
|
|
132
|
+
2016-01-01 00:01:30+00:00 0.0
|
|
133
|
+
2016-01-01 00:02:30+00:00 0.0
|
|
134
|
+
...
|
|
135
|
+
2016-12-31 23:57:30+00:00 0.0
|
|
136
|
+
2016-12-31 23:58:30+00:00 0.0
|
|
137
|
+
2016-12-31 23:59:30+00:00 0.0
|
|
138
|
+
Length: 527040, dtype: float64
|
|
139
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
140
|
+
|
|
141
|
+
>>> sdf.cda.ghi # idem, but for a clean and dry clear-sky atmosphere
|
|
142
|
+
time
|
|
143
|
+
2016-01-01 00:00:30+00:00 0.0
|
|
144
|
+
2016-01-01 00:01:30+00:00 0.0
|
|
145
|
+
2016-01-01 00:02:30+00:00 0.0
|
|
146
|
+
...
|
|
147
|
+
2016-12-31 23:57:30+00:00 0.0
|
|
148
|
+
2016-12-31 23:58:30+00:00 0.0
|
|
149
|
+
2016-12-31 23:59:30+00:00 0.0
|
|
150
|
+
Length: 527040, dtype: float64
|
|
151
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
152
|
+
|
|
153
|
+
>>> sdf.clearsky.ghi # idem, but using a preset clear-sky atmosphere from sparta-solar
|
|
154
|
+
time
|
|
155
|
+
2016-01-01 00:00:30+00:00 0.0
|
|
156
|
+
2016-01-01 00:01:30+00:00 0.0
|
|
157
|
+
2016-01-01 00:02:30+00:00 0.0
|
|
158
|
+
...
|
|
159
|
+
2016-12-31 23:57:30+00:00 0.0
|
|
160
|
+
2016-12-31 23:58:30+00:00 0.0
|
|
161
|
+
2016-12-31 23:59:30+00:00 0.0
|
|
162
|
+
Name: ghi, Length: 527040, dtype: float64
|
|
163
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
164
|
+
|
|
165
|
+
>>> sdf.clearsky.compute( # ad-hoc non-cached calculation
|
|
166
|
+
... atmosphere="crs_soda",
|
|
167
|
+
... model="SPARTA")
|
|
168
|
+
dni dhi dif ghi csi
|
|
169
|
+
time
|
|
170
|
+
2016-01-01 00:00:30 0.0 0.0 0.0 0.0 0.0
|
|
171
|
+
2016-01-01 00:01:30 0.0 0.0 0.0 0.0 0.0
|
|
172
|
+
2016-01-01 00:02:30 0.0 0.0 0.0 0.0 0.0
|
|
173
|
+
... ... ... ... ... ...
|
|
174
|
+
2016-12-31 23:57:30 0.0 0.0 0.0 0.0 0.0
|
|
175
|
+
2016-12-31 23:58:30 0.0 0.0 0.0 0.0 0.0
|
|
176
|
+
2016-12-31 23:59:30 0.0 0.0 0.0 0.0 0.0
|
|
177
|
+
[527040 rows x 5 columns]
|
|
178
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴 is **shipped with BSRN high-level data retrieval** and parsing utilities. When BSRN data is requested for the first time, it is downloaded, parsed, and archived locally in `parquet` format for fast subsequent access.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
>>> from solarpandas.origin import bsrn
|
|
185
|
+
|
|
186
|
+
>>> year_table = bsrn.data_availability(update="auto", as_year_table=True)
|
|
187
|
+
>>> print(year_table)
|
|
188
|
+
site | 9 0 0 1 1 2 2
|
|
189
|
+
| 5 0 5 0 5 0 5
|
|
190
|
+
-----+------------------------------------
|
|
191
|
+
abs | ######
|
|
192
|
+
aes |
|
|
193
|
+
ale | ###########
|
|
194
|
+
asp | ##########################
|
|
195
|
+
bar | ###############################
|
|
196
|
+
ber | ###################### ## #
|
|
197
|
+
bil | ###########################
|
|
198
|
+
... ...
|
|
199
|
+
|
|
200
|
+
# 2) load station metadata (cached locally)
|
|
201
|
+
>>> meta = bsrn.load_metadata(update="auto")
|
|
202
|
+
|
|
203
|
+
# 3) load BSRN measurements for one station/year
|
|
204
|
+
>>> sdf = bsrn.load_data(
|
|
205
|
+
... site="car",
|
|
206
|
+
... years=2016,
|
|
207
|
+
... logical_record="LR0100",
|
|
208
|
+
... group="essential")
|
|
209
|
+
ghi dni dif
|
|
210
|
+
time
|
|
211
|
+
2016-01-01 00:00:30+00:00 -1.0 0.0 -1.0
|
|
212
|
+
2016-01-01 00:01:30+00:00 -1.0 0.0 -1.0
|
|
213
|
+
2016-01-01 00:02:30+00:00 -1.0 0.0 -1.0
|
|
214
|
+
... ... ... ...
|
|
215
|
+
2016-12-31 23:57:30+00:00 -2.0 -1.0 -2.0
|
|
216
|
+
2016-12-31 23:58:30+00:00 -2.0 -1.0 -2.0
|
|
217
|
+
2016-12-31 23:59:30+00:00 -2.0 -1.0 -2.0
|
|
218
|
+
[527040 rows x 3 columns]
|
|
219
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=100.0 m]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- It has **built-in quality-control workflows** enhanced with a tailored qc-specific ExtensionDType, `qcflag`. The QC workflow is memory-cached and the 𝘴𝘰𝘭𝘢𝘳𝘱𝘢𝘯𝘥𝘢𝘴's `qcflag` dtype provides direct access to QC-specific methods via the `.flag` accessor.
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
>>> sdf = sp.sample_data.load_carpentras_data()
|
|
226
|
+
>>> sdf.qc.tests # perform the tests and return them
|
|
227
|
+
ghi_ppl dif_ppl ... closure trackeroff
|
|
228
|
+
time ...
|
|
229
|
+
2016-01-01 00:00:30+00:00 0 0 ... 0 0
|
|
230
|
+
2016-01-01 00:01:30+00:00 0 0 ... 0 0
|
|
231
|
+
2016-01-01 00:02:30+00:00 0 0 ... 0 0
|
|
232
|
+
... ... ... ... ... ...
|
|
233
|
+
2016-12-31 23:57:30+00:00 0 0 ... 0 0
|
|
234
|
+
2016-12-31 23:58:30+00:00 0 0 ... 0 0
|
|
235
|
+
2016-12-31 23:59:30+00:00 0 0 ... 0 0
|
|
236
|
+
[527040 rows x 13 columns]
|
|
237
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
|
|
238
|
+
|
|
239
|
+
>>> sdf.qc.ghi_ppl # access individual tests
|
|
240
|
+
time
|
|
241
|
+
2016-01-01 00:00:30+00:00 0
|
|
242
|
+
2016-01-01 00:01:30+00:00 0
|
|
243
|
+
2016-01-01 00:02:30+00:00 0
|
|
244
|
+
..
|
|
245
|
+
2016-12-31 23:57:30+00:00 0
|
|
246
|
+
2016-12-31 23:58:30+00:00 0
|
|
247
|
+
2016-12-31 23:59:30+00:00 0
|
|
248
|
+
Name: ghi_ppl, Length: 527040, dtype: qcflag
|
|
249
|
+
[site=CAR/BSRN latitude=44.0830° longitude=5.0590° elevation=0.0 m]
|
|
250
|
+
|
|
251
|
+
>>> sdf.qc.ghi_ppl.dtype # tests data have a special dtype `qcflag`
|
|
252
|
+
QCFlagDType()
|
|
253
|
+
|
|
254
|
+
# the type `qcflag` provides specific functionalities throught the `.flag` accessor
|
|
255
|
+
>>> sdf.qc.ghi_ppl.flag.counts() # all data points in this dataset pass this test (by default, night time is excluded)
|
|
256
|
+
PASSED 265417
|
|
257
|
+
NOT_VERIFIABLE 1653
|
|
258
|
+
Name: count, dtype: int64
|
|
259
|
+
|
|
260
|
+
# and additional plotting methods:
|
|
261
|
+
>>> sdf.qc.ghi_ppl.flag.pieplot()
|
|
262
|
+
>>> sdf.qc.ghi_ppl.flag.heatmap()
|
|
263
|
+
>>> sdf.qc.ghi_ppl.flag.plot(sdf)
|
|
264
|
+
|
|
265
|
+
# 4) bolean masks from sets of individual tests
|
|
266
|
+
>>> failed_ghi = sdf.qc.failed(component="ghi")
|
|
267
|
+
>>> passed_all = sdf.qc.passed()
|
|
268
|
+
|
|
269
|
+
# 5) mask failed data points
|
|
270
|
+
>>> sdf_masked = sdf.qc.mask_failed(component="ghi")
|
|
271
|
+
>>> sdf.qc.heatmap(component="ghi")
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- It provides specialized plotting helpers for solar datasets through the ``.solarplot`` accessor.
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
# diurnal line plot
|
|
278
|
+
>>> fig1 = sdf.solarplot.diurnal(column="ghi")
|
|
279
|
+
|
|
280
|
+
# date-time heatmap
|
|
281
|
+
>>> fig2 = sdf.solarplot.heatmap(column="ghi", time_ref="tst", twilight_line=True)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Installation
|
|
285
|
+
|
|
286
|
+
With pip:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
pip install solarpandas
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
and with [uv](https://docs.astral.sh/uv/):
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
uv add solarpandas
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Find further details in the [documentation](https://jararias.github.io/solarpandas).
|