python-esios 2.4.1__py3-none-any.whl → 2.4.2__py3-none-any.whl

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.
esios/processing/i90.py CHANGED
@@ -200,18 +200,29 @@ class I90Sheet:
200
200
  arr[arr == ""] = np.nan
201
201
  return arr
202
202
 
203
- def _normalize_datetime_columns(self, columns: np.ndarray) -> np.ndarray:
203
+ def _normalize_datetime_columns(
204
+ self, columns: np.ndarray, n_measures: int = 1
205
+ ) -> np.ndarray:
204
206
  """Normalize time column headers to integer period indices.
205
207
 
206
- Handles four column formats found in I90 files:
208
+ Handles five column formats found in I90 files:
207
209
 
208
210
  1. Sequential integers: 1–24 (hourly) or 1–96 (quarterly)
209
211
  2. H-Q format: "1-1", "1-2", "1-3", "1-4", "2-1", …
210
- 3. NaN-filler format: [1, NaN, NaN, NaN, 2, …]
212
+ 3. NaN-filler format: [1, NaN, NaN, NaN, 2, …] — one period number per
213
+ hour, three NaN fillers for the remaining quarters (single measure).
211
214
  4. Range format (DST days): "00-01", "01-02", "02-03a", "02-03b", …
212
215
  where the first number is the start hour and a/b suffix marks
213
216
  the repeated hour on fall-back days. Detected by the first
214
217
  column starting with "0" (e.g. "00-01").
218
+ 5. Multi-measure format: [1, NaN, 2, NaN, …] — each period number labels
219
+ a block of `n_measures` sub-columns (e.g. MW + €/MWh paired per
220
+ quarter-hour in offer sheets), the NaN fillers being the extra
221
+ measures. The period number is on every n_measures-th column.
222
+
223
+ `n_measures` is the count of distinct measure sub-columns per period
224
+ (1 for single-measure sheets). It disambiguates format 3 from format 5,
225
+ which share the same NaN-filler shape but mean opposite things.
215
226
  """
216
227
  if any(pd.isna(columns)):
217
228
  self._n_columns_totals = 3
@@ -240,6 +251,10 @@ class I90Sheet:
240
251
  )
241
252
  return ((hours - 1) * 4 + quarters).values
242
253
 
254
+ # Multi-measure format: each period number labels a block of n_measures sub-columns (e.g. MW + €/MWh). The ffilled value already IS the period index and the duplication is the measures — keep it so every measure of a period shares one datetime (paired), instead of spreading them across sequential slots.
255
+ if n_measures > 1:
256
+ return hours.values
257
+
243
258
  # NaN-filler quarterly format: after ffill the same hour number repeats
244
259
  # four times (quarters share the hour label). Assign sequential indices.
245
260
  if hours.duplicated().any():
@@ -257,13 +272,18 @@ class I90Sheet:
257
272
  if idx_col_start == -1:
258
273
  return pd.DataFrame()
259
274
 
260
- columns_date = self._normalize_datetime_columns(columns_prior[idx_col_start:])
275
+ columns_variable = columns[idx_col_start:]
276
+ # Distinct measure labels per period (e.g. {MW, €/MWh} -> 2). Disambiguates the NaN-filler time header (single measure, expand to sequential quarters) from the multi-measure paired layout (keep the period index so measures pair).
277
+ measure_labels = pd.Series(columns_variable).dropna()
278
+ n_measures = int(measure_labels.nunique()) if len(measure_labels) else 1
279
+ columns_date = self._normalize_datetime_columns(
280
+ columns_prior[idx_col_start:], n_measures=n_measures
281
+ )
261
282
  # _normalize_datetime_columns sets _n_columns_totals from the time-block
262
283
  # content (NaN-filler vs sequential). Override with a header-label count
263
284
  # so the index slice survives REE format revisions that add or drop a
264
285
  # "Total" column without touching the time-axis encoding.
265
286
  self._n_columns_totals = _count_header_separators(columns_prior, idx_col_start)
266
- columns_variable = columns[idx_col_start:]
267
287
  columns_index = columns[: idx_col_start - self._n_columns_totals]
268
288
 
269
289
  return columns, columns_index, columns_date, columns_variable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-esios
3
- Version: 2.4.1
3
+ Version: 2.4.2
4
4
  Summary: A Python wrapper for the ESIOS API (Spanish electricity market)
5
5
  Project-URL: Homepage, https://github.com/datons/python-esios
6
6
  Project-URL: Repository, https://github.com/datons/python-esios
@@ -32,10 +32,10 @@ esios/models/indicator.py,sha256=u1AJyEA3YeOqQFjV08_lzyMaofuCiMoLPjvosls9gfE,111
32
32
  esios/models/offer_indicator.py,sha256=nA80Y7Yp0utDaDOdZ-ObcWTsAdhvuXlfJjJBpdVQ7Lo,758
33
33
  esios/processing/__init__.py,sha256=1kLt_gO_wDhXM1BbY0zTyfAYo-CjYKW1ljgRRDZ7USM,278
34
34
  esios/processing/dataframes.py,sha256=OitzBvAerssGP2VXNC-sSO48XsHdIB2nKTUgByN5eYQ,2524
35
- esios/processing/i90.py,sha256=UZEI6f0pG6mbMaVTh_L3KtUp9PuvxvfXYaG9-ePNIxg,13622
35
+ esios/processing/i90.py,sha256=oo7CWKiHvCJjRIifr-hdXsqEH4JfFoFzY-LWVfw-brE,15107
36
36
  esios/processing/zip.py,sha256=12LbFHJTdX_h3JG-clEgQ4Haj-kw0UjfopGLlCRXfGM,1913
37
- python_esios-2.4.1.dist-info/METADATA,sha256=_qFX68pzna9JIBW1zm57X8WNxZ70BO8zaxC4g1nbmTM,3194
38
- python_esios-2.4.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
39
- python_esios-2.4.1.dist-info/entry_points.txt,sha256=7ngseyIyvJ4buTHFL9htaZ4tTFHpG4zzJNkc8B5Jr8U,40
40
- python_esios-2.4.1.dist-info/licenses/LICENSE,sha256=LorLs1-VeBW70Wo9fLAtLJN7nNd6Poy0xzvqdWVqFlE,35128
41
- python_esios-2.4.1.dist-info/RECORD,,
37
+ python_esios-2.4.2.dist-info/METADATA,sha256=KeSOrUKqPGOOx0LfWfovkaAahwtMFEAReh_C96OKyJo,3194
38
+ python_esios-2.4.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
39
+ python_esios-2.4.2.dist-info/entry_points.txt,sha256=7ngseyIyvJ4buTHFL9htaZ4tTFHpG4zzJNkc8B5Jr8U,40
40
+ python_esios-2.4.2.dist-info/licenses/LICENSE,sha256=LorLs1-VeBW70Wo9fLAtLJN7nNd6Poy0xzvqdWVqFlE,35128
41
+ python_esios-2.4.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.30.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any