kalfa 3.0.0.dev0__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 (148) hide show
  1. kalfa/__init__.py +19 -0
  2. kalfa/api.py +284 -0
  3. kalfa/board/__init__.py +1 -0
  4. kalfa/board/answers.py +419 -0
  5. kalfa/board/server.py +536 -0
  6. kalfa/board/static/api.js +176 -0
  7. kalfa/board/static/app.js +122 -0
  8. kalfa/board/static/board.css +228 -0
  9. kalfa/board/static/charts.css +46 -0
  10. kalfa/board/static/components/badge.js +14 -0
  11. kalfa/board/static/components/chart.js +204 -0
  12. kalfa/board/static/components/diagram.js +857 -0
  13. kalfa/board/static/components/error.js +17 -0
  14. kalfa/board/static/components/loading.js +7 -0
  15. kalfa/board/static/components/progress.js +25 -0
  16. kalfa/board/static/components/settings.js +418 -0
  17. kalfa/board/static/components/spark.js +43 -0
  18. kalfa/board/static/components/table.js +156 -0
  19. kalfa/board/static/diagram.css +76 -0
  20. kalfa/board/static/format.js +138 -0
  21. kalfa/board/static/index.html +34 -0
  22. kalfa/board/static/plot.js +594 -0
  23. kalfa/board/static/predictions.css +20 -0
  24. kalfa/board/static/store.js +215 -0
  25. kalfa/board/static/views/compare.js +185 -0
  26. kalfa/board/static/views/home.js +90 -0
  27. kalfa/board/static/views/record/config.js +81 -0
  28. kalfa/board/static/views/record/curves.js +137 -0
  29. kalfa/board/static/views/record/data.js +378 -0
  30. kalfa/board/static/views/record/files.js +128 -0
  31. kalfa/board/static/views/record/log.js +164 -0
  32. kalfa/board/static/views/record/model.js +286 -0
  33. kalfa/board/static/views/record/overview.js +216 -0
  34. kalfa/board/static/views/record/plots.js +103 -0
  35. kalfa/board/static/views/record/predictions.js +834 -0
  36. kalfa/board/static/views/record.js +179 -0
  37. kalfa/board/static/views/runs.js +124 -0
  38. kalfa/board/static/views/sweep.js +282 -0
  39. kalfa/board/watch.py +87 -0
  40. kalfa/cli.py +371 -0
  41. kalfa/commands/__init__.py +0 -0
  42. kalfa/commands/common.py +8 -0
  43. kalfa/commands/export.py +117 -0
  44. kalfa/commands/generate.py +31 -0
  45. kalfa/commands/guide/__init__.py +101 -0
  46. kalfa/commands/guide/about.py +93 -0
  47. kalfa/commands/guide/calls.py +102 -0
  48. kalfa/commands/guide/sections.py +186 -0
  49. kalfa/commands/guide/seen.py +307 -0
  50. kalfa/commands/guide/show.py +106 -0
  51. kalfa/commands/guide/training.py +333 -0
  52. kalfa/commands/guide/words.py +97 -0
  53. kalfa/commands/legos.py +508 -0
  54. kalfa/commands/plots.py +27 -0
  55. kalfa/commands/report/__init__.py +64 -0
  56. kalfa/commands/report/page.py +112 -0
  57. kalfa/commands/report/predictions.py +216 -0
  58. kalfa/commands/report/sections.py +356 -0
  59. kalfa/commands/stop.py +60 -0
  60. kalfa/commands/sweep/__init__.py +202 -0
  61. kalfa/commands/sweep/checks.py +98 -0
  62. kalfa/commands/sweep/plan.py +183 -0
  63. kalfa/commands/sweep/points.py +123 -0
  64. kalfa/commands/sweep/runner.py +100 -0
  65. kalfa/commands/sweep/space.py +67 -0
  66. kalfa/commands/sweep/strategies.py +228 -0
  67. kalfa/core/__init__.py +0 -0
  68. kalfa/core/aliases.py +229 -0
  69. kalfa/core/build.py +727 -0
  70. kalfa/core/check.py +550 -0
  71. kalfa/core/coerce.py +36 -0
  72. kalfa/core/context.py +149 -0
  73. kalfa/core/device.py +63 -0
  74. kalfa/core/errors.py +45 -0
  75. kalfa/core/graph.py +304 -0
  76. kalfa/core/history.py +70 -0
  77. kalfa/core/loop.py +710 -0
  78. kalfa/core/make.py +121 -0
  79. kalfa/core/plan.py +156 -0
  80. kalfa/core/predict.py +290 -0
  81. kalfa/core/protocols.py +122 -0
  82. kalfa/core/record.py +347 -0
  83. kalfa/core/refs.py +88 -0
  84. kalfa/core/rng.py +100 -0
  85. kalfa/core/rules.py +513 -0
  86. kalfa/core/run.py +568 -0
  87. kalfa/core/spec.py +413 -0
  88. kalfa/core/state.py +100 -0
  89. kalfa/core/wrap.py +671 -0
  90. kalfa/records/__init__.py +24 -0
  91. kalfa/records/compare.py +86 -0
  92. kalfa/records/reader.py +561 -0
  93. kalfa/records/stats/__init__.py +36 -0
  94. kalfa/records/stats/choices.py +282 -0
  95. kalfa/records/stats/classes.py +236 -0
  96. kalfa/records/stats/measures.py +144 -0
  97. kalfa/records/stats/pairs.py +225 -0
  98. kalfa/records/stats/tables.py +153 -0
  99. kalfa/records/sweep.py +244 -0
  100. kalfa/std/__init__.py +17 -0
  101. kalfa/std/actions.py +35 -0
  102. kalfa/std/common.py +56 -0
  103. kalfa/std/data/__init__.py +1 -0
  104. kalfa/std/data/base.py +45 -0
  105. kalfa/std/data/columns.py +190 -0
  106. kalfa/std/data/flow.py +317 -0
  107. kalfa/std/data/images.py +443 -0
  108. kalfa/std/data/options.py +147 -0
  109. kalfa/std/data/pre.py +84 -0
  110. kalfa/std/data/splits.py +140 -0
  111. kalfa/std/data/stream.py +294 -0
  112. kalfa/std/data/table.py +642 -0
  113. kalfa/std/data/text.py +436 -0
  114. kalfa/std/data/transforms.py +327 -0
  115. kalfa/std/data/weights.py +113 -0
  116. kalfa/std/data/window.py +135 -0
  117. kalfa/std/figure.py +37 -0
  118. kalfa/std/init.py +72 -0
  119. kalfa/std/loggers.py +105 -0
  120. kalfa/std/losses.py +161 -0
  121. kalfa/std/metrics.py +463 -0
  122. kalfa/std/models.py +122 -0
  123. kalfa/std/notes.py +1553 -0
  124. kalfa/std/objectives.py +360 -0
  125. kalfa/std/plots/__init__.py +75 -0
  126. kalfa/std/plots/classification.py +207 -0
  127. kalfa/std/plots/common.py +44 -0
  128. kalfa/std/plots/curves.py +56 -0
  129. kalfa/std/plots/images.py +188 -0
  130. kalfa/std/plots/pairs.py +66 -0
  131. kalfa/std/plots/predictions.py +154 -0
  132. kalfa/std/samplers.py +261 -0
  133. kalfa/std/samples.py +72 -0
  134. kalfa/std/schedules.py +74 -0
  135. kalfa/std/steps.py +133 -0
  136. kalfa/std/triggers.py +135 -0
  137. kalfa/summary.py +296 -0
  138. kalfa/tui/__init__.py +4 -0
  139. kalfa/tui/app.py +107 -0
  140. kalfa/tui/config.py +61 -0
  141. kalfa/tui/guide.py +100 -0
  142. kalfa/tui/jobs.py +97 -0
  143. kalfa/tui/legos.py +62 -0
  144. kalfa/tui/records.py +439 -0
  145. kalfa-3.0.0.dev0.dist-info/METADATA +261 -0
  146. kalfa-3.0.0.dev0.dist-info/RECORD +148 -0
  147. kalfa-3.0.0.dev0.dist-info/WHEEL +4 -0
  148. kalfa-3.0.0.dev0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,140 @@
1
+ import numpy
2
+ import pandas
3
+
4
+ from kalfa.core.errors import BuildError
5
+
6
+
7
+ SET_NAMES = ("train", "valid", "test")
8
+
9
+
10
+ def ratios_of(ratios):
11
+ if isinstance(ratios, (str, bytes, dict, bool)):
12
+ raise BuildError(f"split ratios are up to three shares (train, valid, test), got {ratios!r}")
13
+ if isinstance(ratios, (int, float)):
14
+ ratios = [ratios]
15
+ try:
16
+ values = [float(part) for part in ratios]
17
+ except (TypeError, ValueError):
18
+ raise BuildError(f"split ratios must be numbers, got {ratios!r}") from None
19
+ if not 1 <= len(values) <= 3:
20
+ raise BuildError(f"split ratios are up to three shares (train, valid, test), got {len(values)} entries")
21
+ values += [0.0] * (3 - len(values))
22
+ if any(part < 0.0 for part in values) or abs(sum(values) - 1.0) > 1e-6:
23
+ raise BuildError(f"split ratios must be non negative and sum to 1, got {ratios!r}")
24
+ return values
25
+
26
+
27
+ def sizes_of(count, ratios):
28
+ active = [position for position, share in enumerate(ratios) if share > 0.0]
29
+ sizes = [0, 0, 0]
30
+ used = 0
31
+ for position in active[:-1]:
32
+ sizes[position] = int(count * ratios[position] + 1e-9)
33
+ used += sizes[position]
34
+ sizes[active[-1]] = max(count - used, 0)
35
+ return sizes
36
+
37
+
38
+ def pieces_of(order, ratios):
39
+ pieces = {}
40
+ start = 0
41
+ for name, size in zip(SET_NAMES, sizes_of(len(order), ratios)):
42
+ pieces[name] = order[start:start + size]
43
+ start += size
44
+ return pieces
45
+
46
+
47
+ def sets_of(frame, pieces):
48
+ return {name: frame.iloc[numpy.sort(piece)] for name, piece in pieces.items() if len(piece)}
49
+
50
+
51
+ def permutation(count, seed):
52
+ return numpy.random.default_rng(seed).permutation(count)
53
+
54
+
55
+ def fold_bounds(count, folds):
56
+ sizes = [count // folds + (1 if position < count % folds else 0) for position in range(folds)]
57
+ bounds = [0]
58
+ for size in sizes:
59
+ bounds.append(bounds[-1] + size)
60
+ return bounds
61
+
62
+
63
+ def whole(value, what, low, high=None):
64
+ if isinstance(value, bool) or not isinstance(value, int) or value < low or (high is not None and value > high):
65
+ span = f"at least {low}" if high is None else f"in [{low}, {high}]"
66
+ raise BuildError(f"{what} must be an integer {span}, got {value!r}")
67
+ return value
68
+
69
+
70
+ class RandomSplit:
71
+ def __init__(self, ratios, seed=None):
72
+ self.ratios = ratios_of(ratios)
73
+ self.seed = seed
74
+
75
+ def split(self, frame):
76
+ return sets_of(frame, pieces_of(permutation(len(frame), self.seed), self.ratios))
77
+
78
+
79
+ class SequentialSplit:
80
+ def __init__(self, ratios, group=None):
81
+ self.ratios = ratios_of(ratios)
82
+ self.group = group
83
+
84
+ def split(self, frame):
85
+ if self.group is None:
86
+ return sets_of(frame, pieces_of(numpy.arange(len(frame)), self.ratios))
87
+ if self.group not in frame.columns:
88
+ raise BuildError(f"sequential split: the group column {self.group!r} is not in the table")
89
+ codes, _ = pandas.factorize(frame[self.group], sort=False, use_na_sentinel=False)
90
+ collected = {name: [] for name in SET_NAMES}
91
+ for code in range(int(codes.max()) + 1 if len(codes) else 0):
92
+ members = numpy.flatnonzero(codes == code)
93
+ for name, piece in pieces_of(members, self.ratios).items():
94
+ collected[name].append(piece)
95
+ pieces = {name: numpy.concatenate(parts) if parts else numpy.zeros(0, dtype="int64")
96
+ for name, parts in collected.items()}
97
+ return sets_of(frame, pieces)
98
+
99
+
100
+ class KFoldSplit:
101
+ def __init__(self, folds, fold, seed=None, valid_ratio=0.0):
102
+ self.folds = whole(folds, "kfold: folds", 2)
103
+ self.fold = whole(fold, "kfold: fold", 0, self.folds - 1)
104
+ self.seed = seed
105
+ self.valid_ratio = float(valid_ratio or 0.0)
106
+ if not 0.0 <= self.valid_ratio < 1.0:
107
+ raise BuildError(f"kfold: valid_ratio is a share of the train rows in [0, 1), got {valid_ratio!r}")
108
+
109
+ def split(self, frame):
110
+ order = permutation(len(frame), self.seed)
111
+ bounds = fold_bounds(len(order), self.folds)
112
+ test = order[bounds[self.fold]:bounds[self.fold + 1]]
113
+ rest = numpy.concatenate([order[:bounds[self.fold]], order[bounds[self.fold + 1]:]])
114
+ carve = int(round(len(rest) * self.valid_ratio))
115
+ return sets_of(frame, {"train": rest[carve:], "valid": rest[:carve], "test": test})
116
+
117
+
118
+ class GivenSplit:
119
+ def __init__(self, column, train="train", valid="valid", test="test"):
120
+ self.column = column
121
+ self.train = train
122
+ self.valid = valid
123
+ self.test = test
124
+
125
+ def split(self, frame):
126
+ if self.column not in frame.columns:
127
+ raise BuildError(f"given split: the column {self.column!r} is not in the table")
128
+ values = frame[self.column]
129
+ labels = {"train": self.train, "valid": self.valid, "test": self.test}
130
+ out = {}
131
+ for name, label in labels.items():
132
+ if label is None:
133
+ continue
134
+ mask = (values == label).to_numpy()
135
+ if mask.any():
136
+ out[name] = frame[mask]
137
+ if not out:
138
+ wanted = [label for label in labels.values() if label is not None]
139
+ raise BuildError(f"given split: the column {self.column!r} holds none of {wanted}")
140
+ return out
@@ -0,0 +1,294 @@
1
+ from contextlib import closing
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+
5
+ import pandas
6
+ import torch
7
+ from torch.utils.data import IterableDataset, get_worker_info
8
+
9
+ from kalfa.core.errors import BuildError
10
+ from kalfa.std.common import positive_int
11
+ from kalfa.std.data import splits
12
+ from kalfa.std.data.base import config_directories
13
+ from kalfa.std.data.options import STREAM_SPLIT, locate, query
14
+
15
+
16
+ DEFAULTS = {"chunk": 65536, "fit_rows": 100000}
17
+ PARQUET = (".parquet", ".pq")
18
+ SEPARATORS = {".csv": ",", ".tsv": "\t"}
19
+ INDEX_PREFIX = "__index_level_"
20
+
21
+
22
+ def joined(pieces):
23
+ return pieces[0] if len(pieces) == 1 else pandas.concat(pieces)
24
+
25
+
26
+ @dataclass
27
+ class StreamPart:
28
+ name: str
29
+ start: int
30
+ stop: int
31
+ where: str | None = None
32
+
33
+
34
+ class ChunkReader:
35
+ def __init__(self, file, chunk=DEFAULTS["chunk"]):
36
+ self.file = file
37
+ self.chunk = int(chunk)
38
+ if self.chunk < 1:
39
+ raise BuildError(f"stream.chunk must be a positive integer, got {chunk!r}")
40
+ self.path = Path(str(file))
41
+ self.suffix = self.path.suffix.lower()
42
+ if self.suffix not in PARQUET and self.suffix not in SEPARATORS:
43
+ raise BuildError(f"{self.path}: the table format {self.suffix!r} is unknown; use .parquet or .csv")
44
+ self.names = None
45
+ self.total = None
46
+
47
+ def is_parquet(self):
48
+ return self.suffix in PARQUET
49
+
50
+ def header(self):
51
+ if self.names is None:
52
+ try:
53
+ if self.is_parquet():
54
+ import pyarrow.parquet
55
+
56
+ with pyarrow.parquet.ParquetFile(str(self.path)) as handle:
57
+ names = list(handle.schema_arrow.names)
58
+ else:
59
+ names = list(pandas.read_csv(self.path, nrows=0, sep=SEPARATORS[self.suffix]).columns)
60
+ except (OSError, ValueError) as error:
61
+ raise BuildError(f"{self.path}: cannot read the table header ({error})") from error
62
+ self.names = [name for name in names if not str(name).startswith(INDEX_PREFIX)]
63
+ return list(self.names)
64
+
65
+ def count(self):
66
+ if self.total is None:
67
+ try:
68
+ if self.is_parquet():
69
+ import pyarrow.parquet
70
+
71
+ with pyarrow.parquet.ParquetFile(str(self.path)) as handle:
72
+ self.total = int(handle.metadata.num_rows)
73
+ else:
74
+ with pandas.read_csv(self.path, sep=SEPARATORS[self.suffix], usecols=[0],
75
+ chunksize=self.chunk) as reader:
76
+ self.total = sum(len(frame) for frame in reader)
77
+ except (OSError, ValueError) as error:
78
+ raise BuildError(f"{self.path}: cannot count the table rows ({error})") from error
79
+ return self.total
80
+
81
+ def parquet_pieces(self, columns, start, stop):
82
+ import pyarrow.parquet
83
+
84
+ with pyarrow.parquet.ParquetFile(str(self.path)) as handle:
85
+ meta = handle.metadata
86
+ groups, first, offset = [], None, 0
87
+ for index in range(meta.num_row_groups):
88
+ size = meta.row_group(index).num_rows
89
+ if size and offset + size > start and (stop is None or offset < stop):
90
+ first = offset if first is None else first
91
+ groups.append(index)
92
+ offset += size
93
+ if not groups:
94
+ return
95
+ position = first
96
+ selected = self.header() if columns is None else columns
97
+ for batch in handle.iter_batches(batch_size=self.chunk, row_groups=groups, columns=selected):
98
+ yield position, batch.num_rows, batch
99
+ position += batch.num_rows
100
+
101
+ def csv_pieces(self, columns):
102
+ with pandas.read_csv(self.path, sep=SEPARATORS[self.suffix], usecols=columns,
103
+ chunksize=self.chunk) as reader:
104
+ position = 0
105
+ for frame in reader:
106
+ yield position, len(frame), frame
107
+ position += len(frame)
108
+
109
+ def pieces(self, columns, start, stop):
110
+ if self.is_parquet():
111
+ return self.parquet_pieces(columns, start, stop)
112
+ return self.csv_pieces(columns)
113
+
114
+ def chunks(self, columns=None, start=0, stop=None, shard=0, shards=1):
115
+ start = max(int(start or 0), 0)
116
+ stop = None if stop is None else int(stop)
117
+ if stop is not None and stop <= start:
118
+ return
119
+ position = 0
120
+ with closing(self.pieces(None if columns is None else list(columns), start, stop)) as pieces:
121
+ for first, size, piece in pieces:
122
+ last = first + size
123
+ if not size or last <= start:
124
+ continue
125
+ if stop is not None and first >= stop:
126
+ break
127
+ owned = position % shards == shard
128
+ position += 1
129
+ if not owned:
130
+ continue
131
+ frame = piece if isinstance(piece, pandas.DataFrame) else piece.to_pandas()
132
+ frame.index = pandas.RangeIndex(first, last)
133
+ low = max(start - first, 0)
134
+ high = size if stop is None else min(stop - first, size)
135
+ yield frame.iloc[low:high] if low or high < size else frame
136
+
137
+
138
+ class StreamDataset(IterableDataset):
139
+ rows = None
140
+
141
+ def __init__(self, engine, part, set_name):
142
+ self.engine = engine
143
+ self.part = part
144
+ self.set_name = set_name
145
+ self.count = engine.count(part)
146
+
147
+ def generator(self):
148
+ table = self.engine.table
149
+ if self.set_name != "train" or table.rng is None or not table.batch.get("shuffle", True):
150
+ return None
151
+ return table.rng.stream("loader")
152
+
153
+ def __iter__(self):
154
+ table = self.engine.table
155
+ info = get_worker_info()
156
+ shard, shards = (0, 1) if info is None else (int(info.id), int(info.num_workers))
157
+ generator = self.generator()
158
+ with closing(self.engine.frames(self.part, shard=shard, shards=shards)) as frames:
159
+ for frame in frames:
160
+ if not len(frame):
161
+ continue
162
+ x = table.features_of(frame)
163
+ targets = {target.name: table.target_tensor(target, frame) for target in table.fitted_targets
164
+ if target.name in frame.columns}
165
+ targets.update(table.weight_tensors(frame))
166
+ if generator is None:
167
+ order = range(len(frame))
168
+ else:
169
+ order = torch.randperm(len(frame), generator=generator).tolist()
170
+ for position in order:
171
+ item = {"x": x[position]}
172
+ for name, values in targets.items():
173
+ item[name] = values[position]
174
+ yield item
175
+
176
+
177
+ def options_of(options):
178
+ if options is True:
179
+ return dict(DEFAULTS)
180
+ if not isinstance(options, dict):
181
+ raise BuildError(f"stream is true or a mapping of {list(DEFAULTS)}, got {options!r}")
182
+ found = dict(DEFAULTS, **options)
183
+ unknown = sorted(set(found) - set(DEFAULTS))
184
+ if unknown:
185
+ raise BuildError(f"stream: unknown keys {unknown}; the keys are {list(DEFAULTS)}")
186
+ for key in DEFAULTS:
187
+ positive_int(found[key], f"stream.{key}")
188
+ return found
189
+
190
+
191
+ class StreamEngine:
192
+ def __init__(self, table, options):
193
+ self.table = table
194
+ self.options = options_of(options)
195
+ self.reader = None
196
+ self.known = {}
197
+
198
+ def sets_of(self, ctx):
199
+ table = self.table
200
+ split = table.split
201
+ if not isinstance(split, (splits.SequentialSplit, splits.GivenSplit)):
202
+ raise BuildError(f"data.split: {STREAM_SPLIT}")
203
+ if getattr(split, "group", None) is not None:
204
+ raise BuildError("data.split: a stream cuts a sequential split over the whole file and takes no group")
205
+ if table.feed is not None:
206
+ raise BuildError("data.stream: a stream yields single rows and takes no feed; drop stream or feed")
207
+ self.reader = ChunkReader(locate(table.file, config_directories(ctx)), self.options["chunk"])
208
+ self.known = {}
209
+ parts = self.parts_of(split)
210
+ train = parts.pop("train", None)
211
+ if train is None or not self.count(train):
212
+ raise BuildError("the split leaves no train rows")
213
+ return {"train": train, **self.materialized(list(parts.values()))}
214
+
215
+ def parts_of(self, split):
216
+ count = self.reader.count()
217
+ if isinstance(split, splits.SequentialSplit):
218
+ parts, start = {}, 0
219
+ for name, size in zip(splits.SET_NAMES, splits.sizes_of(count, split.ratios)):
220
+ if size:
221
+ parts[name] = StreamPart(name, start, start + size)
222
+ start += size
223
+ return parts
224
+ if split.column not in self.reader.header():
225
+ raise BuildError(f"given split: the column {split.column!r} is not in the table")
226
+ labels = {"train": split.train, "valid": split.valid, "test": split.test}
227
+ return {name: StreamPart(name, 0, count, label) for name, label in labels.items() if label is not None}
228
+
229
+ def selected(self, frame, part, weigh=True):
230
+ table = self.table
231
+ rows = frame.index
232
+ if len(rows) and (rows[0] < part.start or rows[-1] >= part.stop):
233
+ frame = frame[(rows >= part.start) & (rows < part.stop)]
234
+ if part.where is not None:
235
+ frame = frame[(frame[table.split.column] == part.where).to_numpy()]
236
+ if table.all_filter:
237
+ frame = query(frame, table.all_filter, "filter")
238
+ text = table.set_filters.get(part.name)
239
+ if text:
240
+ frame = query(frame, text, f"filter.{part.name}")
241
+ return table.dropped(table.weighed(frame) if weigh else frame)
242
+
243
+ def frames(self, part, columns=None, shard=0, shards=1, weigh=True):
244
+ with closing(self.reader.chunks(columns, part.start, part.stop, shard, shards)) as chunks:
245
+ for frame in chunks:
246
+ yield self.selected(frame, part, weigh)
247
+
248
+ def materialized(self, parts):
249
+ if not parts:
250
+ return {}
251
+ collected = {part.name: [] for part in parts}
252
+ start = min(part.start for part in parts)
253
+ stop = max(part.stop for part in parts)
254
+ with closing(self.reader.chunks(None, start, stop)) as chunks:
255
+ for frame in chunks:
256
+ for part in parts:
257
+ piece = self.selected(frame, part)
258
+ if len(piece):
259
+ collected[part.name].append(piece)
260
+ return {name: joined(pieces) for name, pieces in collected.items() if pieces}
261
+
262
+ def fit_sample(self, sets):
263
+ part = sets["train"]
264
+ limit = self.options["fit_rows"]
265
+ pieces, taken = [], 0
266
+ with closing(self.frames(part)) as frames:
267
+ for frame in frames:
268
+ if not len(frame):
269
+ continue
270
+ piece = frame.iloc[:limit - taken]
271
+ pieces.append(piece)
272
+ taken += len(piece)
273
+ if taken >= limit:
274
+ break
275
+ if not pieces:
276
+ raise BuildError("the split leaves no train rows")
277
+ return joined(pieces)
278
+
279
+ def dataset(self, part, set_name):
280
+ return StreamDataset(self, part, set_name)
281
+
282
+ def count(self, part):
283
+ if isinstance(part, pandas.DataFrame):
284
+ return len(part)
285
+ key = (part.name, part.start, part.stop, part.where)
286
+ if key not in self.known:
287
+ filtered = bool(self.table.all_filter or self.table.set_filters.get(part.name))
288
+ if part.where is None and not filtered:
289
+ self.known[key] = max(part.stop - part.start, 0)
290
+ else:
291
+ columns = None if filtered else [self.table.split.column]
292
+ with closing(self.frames(part, columns, weigh=False)) as frames:
293
+ self.known[key] = sum(len(frame) for frame in frames)
294
+ return self.known[key]