aspect-data 0.0.1__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.
- aspect/__init__.py +5 -0
- aspect/checkpoint_utils.py +95 -0
- aspect/cli_module/__init__.py +0 -0
- aspect/cli_module/cli.py +129 -0
- aspect/cli_module/featurize.py +187 -0
- aspect/cli_module/io.py +52 -0
- aspect/data.py +577 -0
- aspect/io.py +189 -0
- aspect/package_data.py +35 -0
- aspect/transform/__init__.py +9 -0
- aspect/transform/base.py +182 -0
- aspect/transform/deep_functions.py +143 -0
- aspect/transform/functions.py +333 -0
- aspect/transform/registry.py +17 -0
- aspect/typing.py +31 -0
- aspect_data-0.0.1.dist-info/METADATA +917 -0
- aspect_data-0.0.1.dist-info/RECORD +21 -0
- aspect_data-0.0.1.dist-info/WHEEL +5 -0
- aspect_data-0.0.1.dist-info/entry_points.txt +2 -0
- aspect_data-0.0.1.dist-info/licenses/LICENSE +21 -0
- aspect_data-0.0.1.dist-info/top_level.txt +1 -0
aspect/data.py
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
"""Data pipeline class."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Mapping, Tuple, Optional, Union
|
|
4
|
+
|
|
5
|
+
from functools import cached_property, partial
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from carabiner import cast, print_err
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from datasets import Dataset, DatasetDict, IterableDataset
|
|
12
|
+
else:
|
|
13
|
+
Dataset, DatasetDict, IterableDataset = Any, Any, Any
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from numpy.typing import ArrayLike
|
|
17
|
+
|
|
18
|
+
from . import app_name, __version__
|
|
19
|
+
from .checkpoint_utils import load_checkpoint_file, save_json
|
|
20
|
+
from .io import AutoDataset
|
|
21
|
+
from .package_data import CACHE_DIR
|
|
22
|
+
from .transform.base import ColumnTransform
|
|
23
|
+
from .typing import DataLike, StrOrIterableOfStr
|
|
24
|
+
|
|
25
|
+
DEFAULT_BATCH_SIZE: int = 1024
|
|
26
|
+
DEFAULT_FORMAT: str = "numpy"
|
|
27
|
+
|
|
28
|
+
def _check_column_presence(
|
|
29
|
+
features: StrOrIterableOfStr,
|
|
30
|
+
data: Dataset
|
|
31
|
+
) -> Iterable[str]:
|
|
32
|
+
columns = cast(features, to=list)
|
|
33
|
+
data_cols = data.column_names
|
|
34
|
+
absent_columns = [col for col in columns if col not in data_cols]
|
|
35
|
+
if absent_columns:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"""
|
|
38
|
+
Requested columns ({', '.join(columns)}) not present in
|
|
39
|
+
{type(data)}: {', '.join(absent_columns)}.
|
|
40
|
+
"""
|
|
41
|
+
)
|
|
42
|
+
return columns
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _check_is_calculated(
|
|
46
|
+
x: Dataset,
|
|
47
|
+
column_transform: ColumnTransform
|
|
48
|
+
) -> Tuple[str, bool]:
|
|
49
|
+
"""Check named column is in dataset.
|
|
50
|
+
|
|
51
|
+
Examples
|
|
52
|
+
========
|
|
53
|
+
_check_column_presence passes when columns present:
|
|
54
|
+
|
|
55
|
+
>>> from unittest.mock import MagicMock
|
|
56
|
+
>>> ds = MagicMock()
|
|
57
|
+
>>> ds.column_names = ["smiles", "mic"]
|
|
58
|
+
>>> _check_column_presence(["smiles", "mic"], ds)
|
|
59
|
+
['smiles', 'mic']
|
|
60
|
+
|
|
61
|
+
_check_column_presence raises on absent columns:
|
|
62
|
+
|
|
63
|
+
>>> _check_column_presence(["smiles", "missing"], ds)
|
|
64
|
+
Traceback (most recent call last):
|
|
65
|
+
...
|
|
66
|
+
ValueError: ...missing...
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
out_column = column_transform.output_column
|
|
70
|
+
return out_column, out_column in x.column_names
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _fill_na(
|
|
74
|
+
x: Mapping[str, Any],
|
|
75
|
+
types: Mapping[str, Any]
|
|
76
|
+
) -> Dict[str, Any]:
|
|
77
|
+
"""Fill missing values with typed missing.
|
|
78
|
+
|
|
79
|
+
For example, numeric filled with zeros, and strings filled with `""`.
|
|
80
|
+
Examples
|
|
81
|
+
========
|
|
82
|
+
_fill_na fills by dtype:
|
|
83
|
+
|
|
84
|
+
>>> _fill_na(
|
|
85
|
+
... {"a": [1, None, 3], "b": ["x", None, "z"], "c": [1.0, None, 3.0]},
|
|
86
|
+
... {"a": "int64", "b": "string", "c": "float32"}
|
|
87
|
+
... )
|
|
88
|
+
{'a': [1, 0, 3], 'b': ['x', '', 'z'], 'c': [1.0, 0.0, 3.0]}
|
|
89
|
+
|
|
90
|
+
_fill_na unknown dtype fills None:
|
|
91
|
+
|
|
92
|
+
>>> _fill_na({"a": [1, None]}, {"a": "bool"})
|
|
93
|
+
{'a': [1, None]}
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
for key in x:
|
|
97
|
+
this_type = types[key]
|
|
98
|
+
if this_type.startswith(("int", "uint")):
|
|
99
|
+
fill_value = 0
|
|
100
|
+
elif this_type.startswith("float"):
|
|
101
|
+
fill_value = 0.
|
|
102
|
+
elif this_type in ("string", "large_string"):
|
|
103
|
+
fill_value = ""
|
|
104
|
+
else:
|
|
105
|
+
fill_value = None
|
|
106
|
+
|
|
107
|
+
x[key] = [fill_value if v is None else v for v in x[key]]
|
|
108
|
+
return x
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class DataPipeline:
|
|
112
|
+
"""Data processing pipeline.
|
|
113
|
+
|
|
114
|
+
Examples
|
|
115
|
+
========
|
|
116
|
+
Construction from dict spec:
|
|
117
|
+
|
|
118
|
+
>>> p = DataPipeline({"log_affinity": ("affinity", "log")})
|
|
119
|
+
>>> "log_affinity" in p.column_transforms
|
|
120
|
+
True
|
|
121
|
+
>>> len(p.column_transforms["log_affinity"])
|
|
122
|
+
1
|
|
123
|
+
>>> p.column_transforms["log_affinity"][0].input_column
|
|
124
|
+
'affinity'
|
|
125
|
+
|
|
126
|
+
Construction from list of 2-tuples:
|
|
127
|
+
|
|
128
|
+
>>> p2 = DataPipeline([("affinity", "log"), ("assay", ["hash"])])
|
|
129
|
+
>>> sorted(p2.column_transforms.keys())
|
|
130
|
+
['col_00', 'col_01']
|
|
131
|
+
|
|
132
|
+
Chained transforms propagate input → output columns:
|
|
133
|
+
|
|
134
|
+
>>> p3 = DataPipeline({"feat": ("assay", ["hash", "identity"])})
|
|
135
|
+
>>> chain = p3.column_transforms["feat"]
|
|
136
|
+
>>> chain[1].input_column == chain[0].output_column
|
|
137
|
+
True
|
|
138
|
+
|
|
139
|
+
serialize_transforms produces JSON-compatible dicts:
|
|
140
|
+
|
|
141
|
+
>>> serialized = p.serialize_transforms(p.column_transforms)
|
|
142
|
+
>>> isinstance(serialized["log_affinity"], tuple)
|
|
143
|
+
True
|
|
144
|
+
>>> isinstance(serialized["log_affinity"][0], dict)
|
|
145
|
+
True
|
|
146
|
+
>>> serialized["log_affinity"][0]["name"]
|
|
147
|
+
'log'
|
|
148
|
+
|
|
149
|
+
Invalid column spec raises:
|
|
150
|
+
|
|
151
|
+
>>> DataPipeline({"bad": ("affinity", "log", "extra")})
|
|
152
|
+
Traceback (most recent call last):
|
|
153
|
+
...
|
|
154
|
+
ValueError: Column transforms must be 2-tuples...
|
|
155
|
+
|
|
156
|
+
Non-string first element raises:
|
|
157
|
+
|
|
158
|
+
>>> DataPipeline({"bad": (123, "log")})
|
|
159
|
+
Traceback (most recent call last):
|
|
160
|
+
...
|
|
161
|
+
ValueError: First item must be input_column name, or dict with input_column key.
|
|
162
|
+
|
|
163
|
+
__call__ produces output columns:
|
|
164
|
+
|
|
165
|
+
>>> import numpy as np
|
|
166
|
+
>>> p = DataPipeline({"log_affinity": ("affinity", "log")})
|
|
167
|
+
>>> data = {"affinity": [1.0, 10.0, 100.0]}
|
|
168
|
+
>>> out = p(data)
|
|
169
|
+
>>> "log_affinity" in out.column_names
|
|
170
|
+
True
|
|
171
|
+
>>> np.allclose(out["log_affinity"], np.log([1., 10., 100.])[:,None])
|
|
172
|
+
True
|
|
173
|
+
|
|
174
|
+
drop_unused_columns removes input columns:
|
|
175
|
+
|
|
176
|
+
>>> out2 = p(data, drop_unused_columns=True)
|
|
177
|
+
>>> "affinity" not in out2.column_names
|
|
178
|
+
True
|
|
179
|
+
>>> "log_affinity" in out2.column_names
|
|
180
|
+
True
|
|
181
|
+
|
|
182
|
+
keep_extra_columns preserved when dropping:
|
|
183
|
+
|
|
184
|
+
>>> data2 = {"affinity": [1.0, 10.0], "label": [0, 1]}
|
|
185
|
+
>>> out3 = p(data2, drop_unused_columns=True, keep_extra_columns=["label"])
|
|
186
|
+
>>> "label" in out3.column_names
|
|
187
|
+
True
|
|
188
|
+
|
|
189
|
+
Missing input column raises:
|
|
190
|
+
|
|
191
|
+
>>> p({"wrong_col": [1.0, 2.0]})
|
|
192
|
+
Traceback (most recent call last):
|
|
193
|
+
...
|
|
194
|
+
ValueError: ...affinity...
|
|
195
|
+
|
|
196
|
+
"""
|
|
197
|
+
def __init__(
|
|
198
|
+
self,
|
|
199
|
+
column_transforms: Optional[Iterable[Union[str, ColumnTransform]]] = None,
|
|
200
|
+
columns_to_keep: Optional[Iterable[Union[str, ColumnTransform]]] = None,
|
|
201
|
+
output_format: str = DEFAULT_FORMAT,
|
|
202
|
+
output_format_opts: Optional[Mapping[str, Any]] = None,
|
|
203
|
+
cache_dir: Optional[str] = None,
|
|
204
|
+
_version: str = __version__,
|
|
205
|
+
_app: str = app_name
|
|
206
|
+
):
|
|
207
|
+
self._column_transforms = column_transforms or []
|
|
208
|
+
columns_to_keep = columns_to_keep or []
|
|
209
|
+
if isinstance(columns_to_keep, str):
|
|
210
|
+
columns_to_keep = [columns_to_keep]
|
|
211
|
+
self.columns_to_keep = columns_to_keep
|
|
212
|
+
self._version = _version
|
|
213
|
+
self._app = _app
|
|
214
|
+
self.column_transforms = self.canonicalize_transforms(self._column_transforms)
|
|
215
|
+
self._column_transforms_serialized = self.serialize_transforms(self.column_transforms)
|
|
216
|
+
self.output_format = output_format
|
|
217
|
+
self.output_format_opts = output_format_opts or {}
|
|
218
|
+
self.data_in = None
|
|
219
|
+
self.data_out = None
|
|
220
|
+
self.data_out_example = None
|
|
221
|
+
self.data_out_shape = None
|
|
222
|
+
self.cache_dir = cache_dir or CACHE_DIR
|
|
223
|
+
self._data_in_filename = "data-in.hf"
|
|
224
|
+
self._data_out_filename = "data-out.hf"
|
|
225
|
+
self._data_out_example_filename = "data-out-example.hf"
|
|
226
|
+
self._data_loaded = False
|
|
227
|
+
self._config_filename = "config.json"
|
|
228
|
+
|
|
229
|
+
self._inspect_data_out()
|
|
230
|
+
|
|
231
|
+
def __eq__(self, other):
|
|
232
|
+
if hasattr(other, "column_transforms_serialized"):
|
|
233
|
+
return all([
|
|
234
|
+
self.column_transforms_serialized == other.column_transforms_serialized,
|
|
235
|
+
self.columns_to_keep == other.columns_to_keep,
|
|
236
|
+
])
|
|
237
|
+
else:
|
|
238
|
+
raise ValueError(f"Cannot compare {type(self)} with {type(other)}.")
|
|
239
|
+
|
|
240
|
+
@cached_property
|
|
241
|
+
def column_transforms_serialized(self):
|
|
242
|
+
self.serialize_transforms(self.column_transforms)
|
|
243
|
+
|
|
244
|
+
def _canonicalize_transforms(
|
|
245
|
+
self,
|
|
246
|
+
column_transforms: Iterable[Union[str, Mapping, ColumnTransform]],
|
|
247
|
+
input_column: Optional[str] = None
|
|
248
|
+
) -> Tuple[ColumnTransform]:
|
|
249
|
+
if isinstance(column_transforms, (str, dict, ColumnTransform)):
|
|
250
|
+
column_transforms = [column_transforms]
|
|
251
|
+
out = []
|
|
252
|
+
prev_transform = None
|
|
253
|
+
for i, candidate in enumerate(column_transforms):
|
|
254
|
+
if prev_transform is not None:
|
|
255
|
+
input_column = prev_transform.output_column
|
|
256
|
+
elif input_column is None:
|
|
257
|
+
if isinstance(candidate, ColumnTransform):
|
|
258
|
+
input_column = candidate.input_column
|
|
259
|
+
elif isinstance(candidate, dict):
|
|
260
|
+
input_column = candidate["input_column"]
|
|
261
|
+
else:
|
|
262
|
+
raise ValueError(
|
|
263
|
+
"Supply an input_column or a first ColumnTransform object."
|
|
264
|
+
)
|
|
265
|
+
elif not isinstance(input_column, str):
|
|
266
|
+
raise ValueError(
|
|
267
|
+
f"Supplied an input_column must be a str, but was {type(input_column)}: {input_column}."
|
|
268
|
+
)
|
|
269
|
+
if isinstance(candidate, ColumnTransform):
|
|
270
|
+
kwargs = candidate.to_dict()
|
|
271
|
+
elif isinstance(candidate, dict):
|
|
272
|
+
kwargs = candidate
|
|
273
|
+
elif isinstance(candidate, str):
|
|
274
|
+
kwargs = {"name": candidate}
|
|
275
|
+
else:
|
|
276
|
+
raise ValueError(
|
|
277
|
+
"Transform must be a ColumnTransform, dict, or str. "
|
|
278
|
+
f"It was {type(candidate)}: {candidate}"
|
|
279
|
+
)
|
|
280
|
+
transform = ColumnTransform(**({
|
|
281
|
+
"_version": self._version,
|
|
282
|
+
"_app": self._app,
|
|
283
|
+
} | kwargs | {
|
|
284
|
+
"input_column": input_column,
|
|
285
|
+
}))
|
|
286
|
+
out.append(transform)
|
|
287
|
+
prev_transform = transform
|
|
288
|
+
return tuple(out)
|
|
289
|
+
|
|
290
|
+
def canonicalize_transforms(
|
|
291
|
+
self,
|
|
292
|
+
column_transforms: Union[Mapping, Iterable],
|
|
293
|
+
input_column: Optional[str] = None
|
|
294
|
+
) -> Dict[str, ColumnTransform]:
|
|
295
|
+
if isinstance(column_transforms, (list, tuple)):
|
|
296
|
+
if len(column_transforms) == 0:
|
|
297
|
+
return {}
|
|
298
|
+
elif isinstance(column_transforms[0], str):
|
|
299
|
+
if isinstance(column_transforms[1], (list, tuple, dict, str, ColumnTransform)):
|
|
300
|
+
column_transforms = [[column_transforms]]
|
|
301
|
+
else:
|
|
302
|
+
raise ValueError(
|
|
303
|
+
"Column transforms should be a list or dict of (input, [transforms...])"
|
|
304
|
+
)
|
|
305
|
+
if not isinstance(column_transforms, dict):
|
|
306
|
+
column_transforms = {
|
|
307
|
+
f"col_{i:02d}": v
|
|
308
|
+
for i, v in enumerate(column_transforms)
|
|
309
|
+
}
|
|
310
|
+
for key in column_transforms:
|
|
311
|
+
first_item = column_transforms[key][0]
|
|
312
|
+
if isinstance(first_item, dict) and "input_column" in first_item:
|
|
313
|
+
column_transforms[key] = (first_item["input_column"], tuple(column_transforms[key]))
|
|
314
|
+
elif isinstance(first_item, str):
|
|
315
|
+
pass
|
|
316
|
+
else:
|
|
317
|
+
raise ValueError(f"First item must be input_column name, or dict with input_column key.")
|
|
318
|
+
wrong_lengths = {k: len(v) for k, v in column_transforms.items() if len(v) != 2}
|
|
319
|
+
if wrong_lengths:
|
|
320
|
+
raise ValueError(
|
|
321
|
+
"Column transforms must be 2-tuples. "
|
|
322
|
+
f"These were not: {wrong_lengths}; {column_transforms}"
|
|
323
|
+
)
|
|
324
|
+
no_names = {k: v for k, v in column_transforms.items() if not isinstance(v[0], str)}
|
|
325
|
+
if no_names:
|
|
326
|
+
raise ValueError(
|
|
327
|
+
"First item of column transform tuple must be string column name. "
|
|
328
|
+
f"These were not: {no_names}"
|
|
329
|
+
)
|
|
330
|
+
out = {}
|
|
331
|
+
for name, (input_column, subpipeline) in column_transforms.items():
|
|
332
|
+
out[name] = self._canonicalize_transforms(
|
|
333
|
+
subpipeline,
|
|
334
|
+
input_column=input_column,
|
|
335
|
+
)
|
|
336
|
+
return out
|
|
337
|
+
|
|
338
|
+
def serialize_transforms(
|
|
339
|
+
self,
|
|
340
|
+
column_transforms: Mapping[str, ColumnTransform]
|
|
341
|
+
) -> Dict[str, Tuple[dict]]:
|
|
342
|
+
return {k: tuple(t.to_dict() for t in v) for k, v in column_transforms.items()}
|
|
343
|
+
|
|
344
|
+
def _inspect_data_out(self) -> None:
|
|
345
|
+
if self.data_out is not None:
|
|
346
|
+
self.data_out = self.data_out.with_format(
|
|
347
|
+
self.output_format,
|
|
348
|
+
**self.output_format_opts,
|
|
349
|
+
)
|
|
350
|
+
self.data_out_example = (
|
|
351
|
+
self.data_out
|
|
352
|
+
.take(1)
|
|
353
|
+
)
|
|
354
|
+
first_item = self.data_out_example.with_format("numpy")[:1]
|
|
355
|
+
self.data_out_shape = {
|
|
356
|
+
col: first_item[col].shape[1:]
|
|
357
|
+
if not isinstance(first_item[col], dict)
|
|
358
|
+
else {
|
|
359
|
+
k: v.shape[1:] if v is not None else None
|
|
360
|
+
for k, v in first_item[col].items()
|
|
361
|
+
}
|
|
362
|
+
for col in self.data_out_example.column_names
|
|
363
|
+
}
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
def _resolve_data(
|
|
367
|
+
self,
|
|
368
|
+
data: DataLike,
|
|
369
|
+
cache_dir: Optional[str] = None
|
|
370
|
+
) -> Union[Dataset, IterableDataset]:
|
|
371
|
+
return AutoDataset.load(data, cache=cache_dir or self.cache_dir)._dataset
|
|
372
|
+
|
|
373
|
+
@staticmethod
|
|
374
|
+
def _featurize(
|
|
375
|
+
x: Mapping[str, ArrayLike],
|
|
376
|
+
column_transforms: Mapping[str, dict]
|
|
377
|
+
) -> Dict[str, np.ndarray]:
|
|
378
|
+
|
|
379
|
+
column_transforms = {
|
|
380
|
+
k: [ColumnTransform(**d) for d in v]
|
|
381
|
+
for k, v in column_transforms.items()
|
|
382
|
+
}
|
|
383
|
+
for name, transforms in column_transforms.items():
|
|
384
|
+
if name in x:
|
|
385
|
+
raise ValueError(
|
|
386
|
+
f"Output column name {name} already in data: {','.join(x)}. "
|
|
387
|
+
"Change transform name to avoid overwriting."
|
|
388
|
+
)
|
|
389
|
+
for name, transforms in column_transforms.items():
|
|
390
|
+
prev_transform = None
|
|
391
|
+
for i, transform in enumerate(transforms):
|
|
392
|
+
if i == 0:
|
|
393
|
+
input_column = transform.input_column
|
|
394
|
+
else:
|
|
395
|
+
input_column = prev_transform.output_column
|
|
396
|
+
x = transform(x)
|
|
397
|
+
prev_transform = transform
|
|
398
|
+
x[name] = x[transform.output_column]
|
|
399
|
+
return x
|
|
400
|
+
|
|
401
|
+
@staticmethod
|
|
402
|
+
def _unsqueeze(
|
|
403
|
+
x: Mapping[str, ArrayLike]
|
|
404
|
+
) -> Dict[str, np.ndarray]:
|
|
405
|
+
for key in x:
|
|
406
|
+
vals = np.asarray(x[key])
|
|
407
|
+
if vals.ndim == 1:
|
|
408
|
+
x[key] = vals[:, None]
|
|
409
|
+
return x
|
|
410
|
+
|
|
411
|
+
def __call__(
|
|
412
|
+
self,
|
|
413
|
+
dataset: DataLike,
|
|
414
|
+
batch_size: int = DEFAULT_BATCH_SIZE,
|
|
415
|
+
drop_unused_columns: bool = False,
|
|
416
|
+
keep_extra_columns: Optional[Iterable[str]] = None
|
|
417
|
+
):
|
|
418
|
+
data_in = self._resolve_data(dataset)
|
|
419
|
+
input_columns = sorted(set(
|
|
420
|
+
seq[0].input_column for k, seq in self.column_transforms.items()
|
|
421
|
+
)) # get only the input column for each branch
|
|
422
|
+
output_columns = sorted(set(self.column_transforms))
|
|
423
|
+
|
|
424
|
+
if len(input_columns) == 0:
|
|
425
|
+
raise AttributeError("No input columns specified.")
|
|
426
|
+
|
|
427
|
+
_check_column_presence(
|
|
428
|
+
input_columns,
|
|
429
|
+
data_in,
|
|
430
|
+
)
|
|
431
|
+
if drop_unused_columns:
|
|
432
|
+
if keep_extra_columns is None:
|
|
433
|
+
extra_cols = []
|
|
434
|
+
else:
|
|
435
|
+
extra_cols = list(keep_extra_columns)
|
|
436
|
+
extra_cols = list(set(extra_cols + self.columns_to_keep).intersection(data_in.column_names))
|
|
437
|
+
all_input_columns = list(set(input_columns + extra_cols))
|
|
438
|
+
data_in = (
|
|
439
|
+
data_in
|
|
440
|
+
.select_columns(all_input_columns)
|
|
441
|
+
)
|
|
442
|
+
all_output_columns = output_columns + extra_cols
|
|
443
|
+
else:
|
|
444
|
+
all_input_columns = list(data_in.column_names)
|
|
445
|
+
all_output_columns = all_input_columns + output_columns
|
|
446
|
+
|
|
447
|
+
data_out = (
|
|
448
|
+
data_in
|
|
449
|
+
.map(
|
|
450
|
+
_fill_na,
|
|
451
|
+
fn_kwargs={
|
|
452
|
+
"types": {
|
|
453
|
+
key: f.dtype
|
|
454
|
+
for key, f in data_in.info.features.items()
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
batched=True,
|
|
458
|
+
batch_size=batch_size,
|
|
459
|
+
desc="Filling NaN values",
|
|
460
|
+
)
|
|
461
|
+
.map(
|
|
462
|
+
self._featurize,
|
|
463
|
+
fn_kwargs={
|
|
464
|
+
"column_transforms": self._column_transforms_serialized,
|
|
465
|
+
},
|
|
466
|
+
batched=True,
|
|
467
|
+
batch_size=batch_size,
|
|
468
|
+
desc="Featurizing",
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
data_out = (
|
|
472
|
+
data_out
|
|
473
|
+
.with_format(None) # guard against tensors
|
|
474
|
+
.select_columns(all_output_columns)
|
|
475
|
+
.map(
|
|
476
|
+
self._unsqueeze,
|
|
477
|
+
batched=True,
|
|
478
|
+
batch_size=batch_size,
|
|
479
|
+
desc="Unsqueezing",
|
|
480
|
+
)
|
|
481
|
+
)
|
|
482
|
+
self.data_in = data_in
|
|
483
|
+
self.data_out = data_out
|
|
484
|
+
self._data_loaded = True
|
|
485
|
+
self._inspect_data_out()
|
|
486
|
+
return self.data_out
|
|
487
|
+
|
|
488
|
+
def save_checkpoint(
|
|
489
|
+
self,
|
|
490
|
+
checkpoint_dir: str,
|
|
491
|
+
skip_data_in: bool = False,
|
|
492
|
+
skip_data_out: bool = False
|
|
493
|
+
):
|
|
494
|
+
keys = {
|
|
495
|
+
"kwargs": (
|
|
496
|
+
("_column_transforms_serialized", "column_transforms"),
|
|
497
|
+
"columns_to_keep",
|
|
498
|
+
"output_format",
|
|
499
|
+
"output_format_opts",
|
|
500
|
+
),
|
|
501
|
+
"state": (
|
|
502
|
+
"_column_transforms",
|
|
503
|
+
"data_out_shape",
|
|
504
|
+
"_data_in_filename",
|
|
505
|
+
"_data_out_filename",
|
|
506
|
+
"_data_out_example_filename",
|
|
507
|
+
"_data_loaded",
|
|
508
|
+
"_config_filename",
|
|
509
|
+
"_version",
|
|
510
|
+
"_app",
|
|
511
|
+
),
|
|
512
|
+
}
|
|
513
|
+
data_config = {
|
|
514
|
+
section: {
|
|
515
|
+
(key if isinstance(key, str) else key[1]): getattr(
|
|
516
|
+
self,
|
|
517
|
+
(key if isinstance(key, str) else key[0]),
|
|
518
|
+
)
|
|
519
|
+
for key in section_keys
|
|
520
|
+
}
|
|
521
|
+
for section, section_keys in keys.items()
|
|
522
|
+
}
|
|
523
|
+
os.makedirs(checkpoint_dir, exist_ok=True)
|
|
524
|
+
if self.data_in is not None and not skip_data_in:
|
|
525
|
+
self.data_in.save_to_disk(
|
|
526
|
+
os.path.join(checkpoint_dir, self._data_in_filename),
|
|
527
|
+
)
|
|
528
|
+
if self.data_out is not None and not skip_data_out:
|
|
529
|
+
(
|
|
530
|
+
self.data_out
|
|
531
|
+
.save_to_disk(os.path.join(checkpoint_dir, self._data_out_filename)),
|
|
532
|
+
)
|
|
533
|
+
if self.data_out_example is not None:
|
|
534
|
+
(
|
|
535
|
+
self.data_out_example
|
|
536
|
+
.save_to_disk(os.path.join(checkpoint_dir, self._data_out_example_filename)),
|
|
537
|
+
)
|
|
538
|
+
save_json(data_config, os.path.join(checkpoint_dir, self._config_filename))
|
|
539
|
+
return None
|
|
540
|
+
|
|
541
|
+
def load_checkpoint(
|
|
542
|
+
self,
|
|
543
|
+
checkpoint: str,
|
|
544
|
+
skip_data_in: bool = False,
|
|
545
|
+
skip_data_out: bool = False,
|
|
546
|
+
cache_dir: Optional[str] = None
|
|
547
|
+
):
|
|
548
|
+
cache_dir = cache_dir or self.cache_dir
|
|
549
|
+
data_config = load_checkpoint_file(
|
|
550
|
+
checkpoint,
|
|
551
|
+
filename=self._config_filename,
|
|
552
|
+
callback="json",
|
|
553
|
+
none_on_error=False,
|
|
554
|
+
cache_dir=cache_dir,
|
|
555
|
+
)
|
|
556
|
+
self.__init__(**data_config["kwargs"], cache_dir=cache_dir)
|
|
557
|
+
for key, val in data_config["state"].items():
|
|
558
|
+
setattr(self, key, val)
|
|
559
|
+
if self._data_loaded:
|
|
560
|
+
if not skip_data_in and self._data_loaded:
|
|
561
|
+
self.data_in = load_checkpoint_file(
|
|
562
|
+
checkpoint,
|
|
563
|
+
filename=self._data_in_filename,
|
|
564
|
+
callback="hf-dataset",
|
|
565
|
+
none_on_error=True,
|
|
566
|
+
cache_dir=cache_dir,
|
|
567
|
+
)
|
|
568
|
+
if not skip_data_out:
|
|
569
|
+
self.data_out = load_checkpoint_file(
|
|
570
|
+
checkpoint,
|
|
571
|
+
filename=self._data_out_filename,
|
|
572
|
+
callback="hf-dataset",
|
|
573
|
+
none_on_error=True,
|
|
574
|
+
cache_dir=cache_dir,
|
|
575
|
+
)
|
|
576
|
+
self._inspect_data_out()
|
|
577
|
+
return self
|