anemoi-datasets 0.5.16__py3-none-any.whl → 0.5.18__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.
Files changed (155) hide show
  1. anemoi/datasets/__init__.py +4 -1
  2. anemoi/datasets/__main__.py +12 -2
  3. anemoi/datasets/_version.py +9 -4
  4. anemoi/datasets/commands/cleanup.py +17 -2
  5. anemoi/datasets/commands/compare.py +18 -2
  6. anemoi/datasets/commands/copy.py +196 -14
  7. anemoi/datasets/commands/create.py +50 -7
  8. anemoi/datasets/commands/finalise-additions.py +17 -2
  9. anemoi/datasets/commands/finalise.py +17 -2
  10. anemoi/datasets/commands/init-additions.py +17 -2
  11. anemoi/datasets/commands/init.py +16 -2
  12. anemoi/datasets/commands/inspect.py +283 -62
  13. anemoi/datasets/commands/load-additions.py +16 -2
  14. anemoi/datasets/commands/load.py +16 -2
  15. anemoi/datasets/commands/patch.py +17 -2
  16. anemoi/datasets/commands/publish.py +17 -2
  17. anemoi/datasets/commands/scan.py +31 -3
  18. anemoi/datasets/compute/recentre.py +47 -11
  19. anemoi/datasets/create/__init__.py +612 -85
  20. anemoi/datasets/create/check.py +142 -20
  21. anemoi/datasets/create/chunks.py +64 -4
  22. anemoi/datasets/create/config.py +185 -21
  23. anemoi/datasets/create/filter.py +50 -0
  24. anemoi/datasets/create/filters/__init__.py +33 -0
  25. anemoi/datasets/create/filters/empty.py +37 -0
  26. anemoi/datasets/create/filters/legacy.py +93 -0
  27. anemoi/datasets/create/filters/noop.py +37 -0
  28. anemoi/datasets/create/filters/orog_to_z.py +58 -0
  29. anemoi/datasets/create/{functions/filters → filters}/pressure_level_relative_humidity_to_specific_humidity.py +33 -10
  30. anemoi/datasets/create/{functions/filters → filters}/pressure_level_specific_humidity_to_relative_humidity.py +32 -8
  31. anemoi/datasets/create/filters/rename.py +205 -0
  32. anemoi/datasets/create/{functions/filters → filters}/rotate_winds.py +43 -28
  33. anemoi/datasets/create/{functions/filters → filters}/single_level_dewpoint_to_relative_humidity.py +32 -9
  34. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_dewpoint.py +33 -9
  35. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_specific_humidity.py +55 -7
  36. anemoi/datasets/create/{functions/filters → filters}/single_level_specific_humidity_to_relative_humidity.py +98 -37
  37. anemoi/datasets/create/filters/speeddir_to_uv.py +95 -0
  38. anemoi/datasets/create/{functions/filters → filters}/sum.py +24 -27
  39. anemoi/datasets/create/filters/transform.py +53 -0
  40. anemoi/datasets/create/{functions/filters → filters}/unrotate_winds.py +27 -18
  41. anemoi/datasets/create/filters/uv_to_speeddir.py +94 -0
  42. anemoi/datasets/create/{functions/filters → filters}/wz_to_w.py +51 -33
  43. anemoi/datasets/create/input/__init__.py +76 -5
  44. anemoi/datasets/create/input/action.py +149 -13
  45. anemoi/datasets/create/input/concat.py +81 -10
  46. anemoi/datasets/create/input/context.py +39 -4
  47. anemoi/datasets/create/input/data_sources.py +72 -6
  48. anemoi/datasets/create/input/empty.py +21 -3
  49. anemoi/datasets/create/input/filter.py +60 -12
  50. anemoi/datasets/create/input/function.py +154 -37
  51. anemoi/datasets/create/input/join.py +86 -14
  52. anemoi/datasets/create/input/misc.py +67 -17
  53. anemoi/datasets/create/input/pipe.py +33 -6
  54. anemoi/datasets/create/input/repeated_dates.py +189 -41
  55. anemoi/datasets/create/input/result.py +202 -87
  56. anemoi/datasets/create/input/step.py +119 -22
  57. anemoi/datasets/create/input/template.py +100 -13
  58. anemoi/datasets/create/input/trace.py +62 -7
  59. anemoi/datasets/create/patch.py +52 -4
  60. anemoi/datasets/create/persistent.py +134 -17
  61. anemoi/datasets/create/size.py +15 -1
  62. anemoi/datasets/create/source.py +51 -0
  63. anemoi/datasets/create/sources/__init__.py +36 -0
  64. anemoi/datasets/create/{functions/sources → sources}/accumulations.py +296 -30
  65. anemoi/datasets/create/{functions/sources → sources}/constants.py +27 -2
  66. anemoi/datasets/create/{functions/sources → sources}/eccc_fstd.py +7 -3
  67. anemoi/datasets/create/sources/empty.py +37 -0
  68. anemoi/datasets/create/{functions/sources → sources}/forcings.py +25 -1
  69. anemoi/datasets/create/sources/grib.py +297 -0
  70. anemoi/datasets/create/{functions/sources → sources}/hindcasts.py +38 -4
  71. anemoi/datasets/create/sources/legacy.py +93 -0
  72. anemoi/datasets/create/{functions/sources → sources}/mars.py +168 -20
  73. anemoi/datasets/create/sources/netcdf.py +42 -0
  74. anemoi/datasets/create/sources/opendap.py +43 -0
  75. anemoi/datasets/create/{functions/sources/__init__.py → sources/patterns.py} +35 -4
  76. anemoi/datasets/create/sources/recentre.py +150 -0
  77. anemoi/datasets/create/{functions/sources → sources}/source.py +27 -5
  78. anemoi/datasets/create/{functions/sources → sources}/tendencies.py +64 -7
  79. anemoi/datasets/create/sources/xarray.py +92 -0
  80. anemoi/datasets/create/sources/xarray_kerchunk.py +36 -0
  81. anemoi/datasets/create/sources/xarray_support/README.md +1 -0
  82. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/__init__.py +109 -8
  83. anemoi/datasets/create/sources/xarray_support/coordinates.py +442 -0
  84. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/field.py +94 -16
  85. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/fieldlist.py +90 -25
  86. anemoi/datasets/create/sources/xarray_support/flavour.py +1036 -0
  87. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/grid.py +92 -31
  88. anemoi/datasets/create/sources/xarray_support/metadata.py +395 -0
  89. anemoi/datasets/create/sources/xarray_support/patch.py +91 -0
  90. anemoi/datasets/create/sources/xarray_support/time.py +391 -0
  91. anemoi/datasets/create/sources/xarray_support/variable.py +331 -0
  92. anemoi/datasets/create/sources/xarray_zarr.py +41 -0
  93. anemoi/datasets/create/{functions/sources → sources}/zenodo.py +34 -5
  94. anemoi/datasets/create/statistics/__init__.py +233 -44
  95. anemoi/datasets/create/statistics/summary.py +52 -6
  96. anemoi/datasets/create/testing.py +76 -0
  97. anemoi/datasets/create/{functions/filters/noop.py → typing.py} +6 -3
  98. anemoi/datasets/create/utils.py +97 -6
  99. anemoi/datasets/create/writer.py +26 -4
  100. anemoi/datasets/create/zarr.py +170 -23
  101. anemoi/datasets/data/__init__.py +51 -4
  102. anemoi/datasets/data/complement.py +191 -40
  103. anemoi/datasets/data/concat.py +141 -16
  104. anemoi/datasets/data/dataset.py +558 -62
  105. anemoi/datasets/data/debug.py +197 -26
  106. anemoi/datasets/data/ensemble.py +93 -8
  107. anemoi/datasets/data/fill_missing.py +165 -18
  108. anemoi/datasets/data/forwards.py +428 -56
  109. anemoi/datasets/data/grids.py +323 -97
  110. anemoi/datasets/data/indexing.py +112 -19
  111. anemoi/datasets/data/interpolate.py +92 -12
  112. anemoi/datasets/data/join.py +158 -19
  113. anemoi/datasets/data/masked.py +129 -15
  114. anemoi/datasets/data/merge.py +137 -23
  115. anemoi/datasets/data/misc.py +172 -16
  116. anemoi/datasets/data/missing.py +233 -29
  117. anemoi/datasets/data/rescale.py +111 -10
  118. anemoi/datasets/data/select.py +168 -26
  119. anemoi/datasets/data/statistics.py +67 -6
  120. anemoi/datasets/data/stores.py +149 -64
  121. anemoi/datasets/data/subset.py +159 -25
  122. anemoi/datasets/data/unchecked.py +168 -57
  123. anemoi/datasets/data/xy.py +168 -25
  124. anemoi/datasets/dates/__init__.py +191 -16
  125. anemoi/datasets/dates/groups.py +189 -47
  126. anemoi/datasets/grids.py +270 -31
  127. anemoi/datasets/testing.py +28 -1
  128. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/METADATA +9 -6
  129. anemoi_datasets-0.5.18.dist-info/RECORD +137 -0
  130. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/WHEEL +1 -1
  131. anemoi/datasets/create/functions/__init__.py +0 -66
  132. anemoi/datasets/create/functions/filters/__init__.py +0 -9
  133. anemoi/datasets/create/functions/filters/empty.py +0 -17
  134. anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
  135. anemoi/datasets/create/functions/filters/rename.py +0 -79
  136. anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
  137. anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
  138. anemoi/datasets/create/functions/sources/empty.py +0 -15
  139. anemoi/datasets/create/functions/sources/grib.py +0 -150
  140. anemoi/datasets/create/functions/sources/netcdf.py +0 -15
  141. anemoi/datasets/create/functions/sources/opendap.py +0 -15
  142. anemoi/datasets/create/functions/sources/recentre.py +0 -60
  143. anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
  144. anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
  145. anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
  146. anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
  147. anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
  148. anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
  149. anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
  150. anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
  151. anemoi/datasets/utils/fields.py +0 -47
  152. anemoi_datasets-0.5.16.dist-info/RECORD +0 -129
  153. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/entry_points.txt +0 -0
  154. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info/licenses}/LICENSE +0 -0
  155. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/top_level.txt +0 -0
@@ -8,14 +8,31 @@
8
8
  # nor does it submit to any jurisdiction.
9
9
 
10
10
 
11
+ import datetime
11
12
  import itertools
13
+ from abc import ABC
14
+ from abc import abstractmethod
12
15
  from functools import cached_property
16
+ from typing import Any
17
+ from typing import Callable
18
+ from typing import Iterator
19
+ from typing import List
20
+ from typing import Tuple
21
+ from typing import Union
13
22
 
14
23
  from anemoi.datasets.dates import DatesProvider
15
24
  from anemoi.datasets.dates import as_datetime
16
25
 
17
26
 
18
- def _shorten(dates):
27
+ def _shorten(dates: Union[List[datetime.datetime], Tuple[datetime.datetime, ...]]) -> Union[str, List[str]]:
28
+ """Shorten the list of dates for display.
29
+
30
+ Args:
31
+ dates (Union[List[datetime.datetime], Tuple[datetime.datetime, ...]]): The list of dates.
32
+
33
+ Returns:
34
+ Union[str, List[str]]: The shortened list of dates.
35
+ """
19
36
  if isinstance(dates, (list, tuple)):
20
37
  dates = [d.isoformat() for d in dates]
21
38
  if len(dates) > 5:
@@ -24,7 +41,9 @@ def _shorten(dates):
24
41
 
25
42
 
26
43
  class GroupOfDates:
27
- def __init__(self, dates, provider, partial_ok=False):
44
+ """A class to represent a group of dates."""
45
+
46
+ def __init__(self, dates: List[datetime.datetime], provider: DatesProvider, partial_ok: bool = False) -> None:
28
47
  assert isinstance(provider, DatesProvider), type(provider)
29
48
  assert isinstance(dates, list)
30
49
 
@@ -32,68 +51,115 @@ class GroupOfDates:
32
51
  self.provider = provider
33
52
  self.partial_ok = partial_ok
34
53
 
35
- def __len__(self):
54
+ def __len__(self) -> int:
55
+ """Return the number of dates in the group.
56
+
57
+ Returns:
58
+ int: The number of dates.
59
+ """
36
60
  return len(self.dates)
37
61
 
38
- def __iter__(self):
62
+ def __iter__(self) -> Iterator[datetime.datetime]:
63
+ """Return an iterator over the dates in the group.
64
+
65
+ Returns:
66
+ Iterator[datetime.datetime]: The iterator over the dates.
67
+ """
39
68
  return iter(self.dates)
40
69
 
41
70
  def __repr__(self) -> str:
71
+ """Return a string representation of the group of dates.
72
+
73
+ Returns:
74
+ str: The string representation.
75
+ """
42
76
  return f"GroupOfDates(dates={_shorten(self.dates)})"
43
77
 
44
78
  def __eq__(self, other: object) -> bool:
79
+ """Check if two groups of dates are equal.
80
+
81
+ Args:
82
+ other (object): The other group of dates.
83
+
84
+ Returns:
85
+ bool: True if the groups are equal, False otherwise.
86
+ """
45
87
  return isinstance(other, GroupOfDates) and self.dates == other.dates
46
88
 
47
89
 
48
90
  class Groups:
49
- """>>> list(Groups(group_by="daily", start="2023-01-01 00:00", end="2023-01-05 00:00", frequency=12))[0]
50
- [datetime.datetime(2023, 1, 1, 0, 0), datetime.datetime(2023, 1, 1, 12, 0)]
51
-
52
- >>> list(Groups(group_by="daily", start="2023-01-01 00:00", end="2023-01-05 00:00", frequency=12))[1]
53
- [datetime.datetime(2023, 1, 2, 0, 0), datetime.datetime(2023, 1, 2, 12, 0)]
54
-
55
- >>> g = Groups(group_by=3, start="2023-01-01 00:00", end="2023-01-05 00:00", frequency=24)
56
- >>> len(list(g))
57
- 2
58
- >>> len(list(g)[0])
59
- 3
60
- >>> len(list(g)[1])
61
- 2
62
- >>> g = Groups(group_by=3,
63
- ... start="2023-01-01 00:00",
64
- ... end="2023-01-05 00:00",
65
- ... frequency=24,
66
- ... missing=["2023-01-02 00:00"])
67
- >>> len(list(g))
68
- 2
69
- >>> len(list(g)[0])
70
- 2
71
- >>> len(list(g)[1])
72
- 2
91
+ """A collection of groups of dates.
92
+
93
+ Examples:
94
+ >>> list(Groups(group_by="daily", start="2023-01-01 00:00", end="2023-01-05 00:00", frequency=12))[0]
95
+ [datetime.datetime(2023, 1, 1, 0, 0), datetime.datetime(2023, 1, 1, 12, 0)]
96
+
97
+ >>> list(Groups(group_by="daily", start="2023-01-01 00:00", end="2023-01-05 00:00", frequency=12))[1]
98
+ [datetime.datetime(2023, 1, 2, 0, 0), datetime.datetime(2023, 1, 2, 12, 0)]
99
+
100
+ >>> g = Groups(group_by=3, start="2023-01-01 00:00", end="2023-01-05 00:00", frequency=24)
101
+ >>> len(list(g))
102
+ 2
103
+ >>> len(list(g)[0])
104
+ 3
105
+ >>> len(list(g)[1])
106
+ 2
107
+ >>> g = Groups(group_by=3,
108
+ ... start="2023-01-01 00:00",
109
+ ... end="2023-01-05 00:00",
110
+ ... frequency=24,
111
+ ... missing=["2023-01-02 00:00"])
112
+ >>> len(list(g))
113
+ 2
114
+ >>> len(list(g)[0])
115
+ 2
116
+ >>> len(list(g)[1])
117
+ 2
73
118
  """
74
119
 
75
- def __init__(self, **kwargs):
120
+ def __init__(self, **kwargs: Any) -> None:
121
+ """Initialize the class with the provided keyword arguments.
122
+
123
+ Parameters
124
+ ----------
125
+ **kwargs : Any : Arbitrary keyword arguments. Expected keys include:
126
+ - group_by: Configuration for the Grouper.
127
+ - Other keys for DatesProvider configuration.
128
+ """
129
+
76
130
  group_by = kwargs.pop("group_by")
77
131
  self._dates = DatesProvider.from_config(**kwargs)
78
132
  self._grouper = Grouper.from_config(group_by)
79
133
  self._filter = Filter(self._dates.missing)
80
134
 
81
135
  @property
82
- def provider(self):
136
+ def provider(self) -> DatesProvider:
137
+ """Return the dates provider."""
83
138
  return self._dates
84
139
 
85
- def __iter__(self):
140
+ def __iter__(self) -> Iterator[GroupOfDates]:
141
+ """Return an iterator over the groups of dates.
142
+
143
+ Returns:
144
+ Iterator[GroupOfDates]: The iterator over the groups of dates.
145
+ """
86
146
  for go in self._grouper(self._dates):
87
147
  dates = self._filter(go.dates)
88
148
  if not dates:
89
149
  continue
90
150
  yield GroupOfDates(dates, go.provider)
91
151
 
92
- def __len__(self):
152
+ def __len__(self) -> int:
153
+ """Return the number of groups of dates.
154
+
155
+ Returns:
156
+ int: The number of groups.
157
+ """
93
158
  return self._len
94
159
 
95
160
  @cached_property
96
- def _len(self):
161
+ def _len(self) -> int:
162
+ """Calculate the number of groups of dates."""
97
163
  n = 0
98
164
  for go in self._grouper(self._dates):
99
165
  dates = self._filter(go.dates)
@@ -102,28 +168,56 @@ class Groups:
102
168
  n += 1
103
169
  return n
104
170
 
105
- def __repr__(self):
171
+ def __repr__(self) -> str:
172
+ """Return a string representation of the groups of dates.
173
+
174
+ Returns:
175
+ str: The string representation.
176
+ """
106
177
  return f"{self.__class__.__name__}(dates={len(self)},{_shorten(self._dates)})"
107
178
 
108
- def describe(self):
109
- return self.dates.summary
179
+ def describe(self) -> str:
180
+ """Return a summary description of the dates.
181
+
182
+ Returns:
183
+ str: The summary description.
184
+ """
185
+ return self._dates.summary
186
+
187
+ def one_date(self) -> GroupOfDates:
188
+ """Return a group containing only one date.
110
189
 
111
- def one_date(self):
190
+ Returns:
191
+ GroupOfDates: The group containing only one date.
192
+ """
112
193
  go = next(iter(self))
113
194
  return GroupOfDates([go.dates[0]], go.provider)
114
195
 
115
196
 
116
197
  class Filter:
117
- def __init__(self, missing):
198
+ """A class to filter out missing dates."""
199
+
200
+ def __init__(self, missing: List[datetime.datetime]) -> None:
118
201
  self.missing = set(as_datetime(m) for m in missing)
119
202
 
120
- def __call__(self, dates):
203
+ def __call__(self, dates: List[datetime.datetime]) -> List[datetime.datetime]:
204
+ """Filter out missing dates from the list of dates.
205
+
206
+ Args:
207
+ dates (List[datetime.datetime]): The list of dates.
208
+
209
+ Returns:
210
+ List[datetime.datetime]: The filtered list of dates.
211
+ """
121
212
  return [d for d in dates if d not in self.missing]
122
213
 
123
214
 
124
- class Grouper:
215
+ class Grouper(ABC):
216
+ """Abstract base class for grouping dates."""
217
+
125
218
  @classmethod
126
- def from_config(cls, group_by):
219
+ def from_config(cls, group_by: Any) -> "Grouper":
220
+ """Create a grouper based on the configuration."""
127
221
 
128
222
  if isinstance(group_by, int) and group_by > 0:
129
223
  return GrouperByFixedSize(group_by)
@@ -142,9 +236,31 @@ class Grouper:
142
236
  }[group_by]
143
237
  return GrouperByKey(key)
144
238
 
239
+ @abstractmethod
240
+ def __call__(self, dates: DatesProvider) -> Iterator[GroupOfDates]:
241
+ """Group dates based on the implementation.
242
+
243
+ Args:
244
+ dates (DatesProvider): The dates provider.
245
+
246
+ Returns:
247
+ Iterator[GroupOfDates]: The iterator over the groups of dates.
248
+ """
249
+ pass
250
+
145
251
 
146
252
  class ReferenceDateGroup(Grouper):
147
- def __call__(self, dates):
253
+ """Group dates by their reference date."""
254
+
255
+ def __call__(self, dates: DatesProvider) -> Iterator[GroupOfDates]:
256
+ """Group dates by their reference date.
257
+
258
+ Args:
259
+ dates (DatesProvider): The dates provider.
260
+
261
+ Returns:
262
+ Iterator[GroupOfDates]: The iterator over the groups of dates.
263
+ """
148
264
  assert isinstance(dates, DatesProvider), type(dates)
149
265
 
150
266
  mapping = dates.mapping
@@ -157,7 +273,17 @@ class ReferenceDateGroup(Grouper):
157
273
 
158
274
 
159
275
  class GrouperOneGroup(Grouper):
160
- def __call__(self, dates):
276
+ """Group all dates into a single group."""
277
+
278
+ def __call__(self, dates: DatesProvider) -> Iterator[GroupOfDates]:
279
+ """Group all dates into a single group.
280
+
281
+ Args:
282
+ dates (DatesProvider): The dates provider.
283
+
284
+ Returns:
285
+ Iterator[GroupOfDates]: The iterator over the groups of dates.
286
+ """
161
287
  assert isinstance(dates, DatesProvider), type(dates)
162
288
 
163
289
  yield GroupOfDates(dates.values, dates)
@@ -166,10 +292,18 @@ class GrouperOneGroup(Grouper):
166
292
  class GrouperByKey(Grouper):
167
293
  """Group dates by a key."""
168
294
 
169
- def __init__(self, key):
295
+ def __init__(self, key: Callable[[datetime.datetime], Any]) -> None:
170
296
  self.key = key
171
297
 
172
- def __call__(self, dates):
298
+ def __call__(self, dates: DatesProvider) -> Iterator[GroupOfDates]:
299
+ """Group dates based on the provided key.
300
+
301
+ Args:
302
+ dates (DatesProvider): The dates provider.
303
+
304
+ Returns:
305
+ Iterator[GroupOfDates]: The iterator over the groups of dates.
306
+ """
173
307
  for _, g in itertools.groupby(sorted(dates, key=self.key), key=self.key):
174
308
  yield GroupOfDates(list(g), dates)
175
309
 
@@ -177,10 +311,18 @@ class GrouperByKey(Grouper):
177
311
  class GrouperByFixedSize(Grouper):
178
312
  """Group dates by a fixed size."""
179
313
 
180
- def __init__(self, size):
314
+ def __init__(self, size: int) -> None:
181
315
  self.size = size
182
316
 
183
- def __call__(self, dates):
317
+ def __call__(self, dates: DatesProvider) -> Iterator[GroupOfDates]:
318
+ """Group dates into fixed-size batches.
319
+
320
+ Args:
321
+ dates (DatesProvider): The dates provider.
322
+
323
+ Returns:
324
+ Iterator[GroupOfDates]: The iterator over the groups of dates.
325
+ """
184
326
  batch = []
185
327
 
186
328
  for d in dates: