Ultra 0.9.4
Loading...
Searching...
No Matches
columns_info.tcc
Go to the documentation of this file.
1
12
13#if !defined(ULTRA_COLUMNS_INFO_H)
14# error "Don't include this file directly, include the specific .h instead"
15#endif
16
17#if !defined(ULTRA_COLUMNS_INFO_TCC)
18#define ULTRA_COLUMNS_INFO_TCC
19
20namespace internal
21{
22
23[[nodiscard]] constexpr std::size_t normalised_column_count(
24 std::size_t raw_count, std::optional<std::size_t> output_index) noexcept
25{
26 return raw_count + (output_index ? 0 : 1);
27}
28
29[[nodiscard]] constexpr std::optional<std::size_t> raw_column_index(
30 std::size_t idx, std::optional<std::size_t> output_index) noexcept
31{
32 if (!output_index)
33 return idx == 0 ? std::nullopt : std::optional<std::size_t>(idx - 1);
34
35 if (idx == 0)
36 return *output_index;
37
38 return idx <= *output_index ? idx - 1 : idx;
39}
40
41template<DataframeRow R>
42class normalised_row_view
43{
44public:
45 using value_type = std::ranges::range_value_t<R>;
46
47 normalised_row_view(const R &r, std::optional<std::size_t> output_index)
48 : r_(&r), output_index_(output_index),
49 logical_size_(normalised_column_count(std::ranges::size(r), output_index))
50 {
51 Expects(!output_index_ || *output_index_ < std::ranges::size(r));
52 }
53
54 [[nodiscard]] std::size_t size() const noexcept { return logical_size_; }
55
56 class iterator
57 {
58 public:
59 using difference_type = std::ptrdiff_t;
60 using value_type = normalised_row_view::value_type;
61 using iterator_concept = std::forward_iterator_tag;
62 using iterator_category = std::forward_iterator_tag;
63
64 iterator() = default;
65
66 iterator(const normalised_row_view *view, std::size_t pos)
67 : view_(view), pos_(pos)
68 {
69 refresh_raw_index();
70
71 if (raw_idx_)
72 {
73 it_ = std::ranges::begin(*view_->r_);
74 std::advance(it_, *raw_idx_);
75 }
76 }
77
78 [[nodiscard]] const value_type &operator*() const
79 {
80 if (!raw_idx_)
81 return view_->empty_;
82
83 return *it_;
84 }
85
86 iterator &operator++()
87 {
88 const auto previous_raw_idx(raw_idx_);
89
90 ++pos_;
91
92 if (pos_ >= view_->logical_size_)
93 {
94 raw_idx_.reset();
95 return *this;
96 }
97
98 refresh_raw_index();
99
100 if (!raw_idx_)
101 return *this;
102
103 if (previous_raw_idx && *raw_idx_ == *previous_raw_idx + 1)
104 ++it_;
105 else
106 {
107 it_ = std::ranges::begin(*view_->r_);
108 std::advance(it_, *raw_idx_);
109 }
110
111 return *this;
112 }
113
114 iterator operator++(int)
115 {
116 auto old(*this);
117 ++*this;
118 return old;
119 }
120
121 [[nodiscard]] bool operator==(const iterator &other) const noexcept
122 {
123 return view_ == other.view_ && pos_ == other.pos_;
124 }
125
126 private:
127 void refresh_raw_index()
128 {
129 raw_idx_ = pos_ < view_->logical_size_
130 ? view_->raw_column(pos_)
131 : std::nullopt;
132 }
133
134 const normalised_row_view *view_ {};
135 std::size_t pos_ {};
136 std::optional<std::size_t> raw_idx_ {};
137 std::ranges::iterator_t<const R> it_ {};
138 };
139
140 [[nodiscard]] iterator begin() const { return iterator(this, 0); }
141 [[nodiscard]] iterator end() const { return iterator(this, logical_size_); }
142
143private:
144 [[nodiscard]] std::optional<std::size_t> raw_column(
145 std::size_t pos) const noexcept
146 {
147 return raw_column_index(pos, output_index_);
148 }
149
150 const R *r_;
151 std::optional<std::size_t> output_index_;
152 std::size_t logical_size_;
153 const value_type empty_ {};
154};
155
156} // namespace internal
157
183template<DataframeMatrix R>
184void columns_info::build(const R &exs, std::optional<std::size_t> output_index)
185{
186 using VT = std::ranges::range_value_t<std::ranges::range_value_t<R>>;
187
188 Expects(!std::ranges::empty(exs));
189 Expects(std::ranges::size(*std::ranges::begin(exs)));
190
191 // ---- Set up column headers (first row must contain the headers) ----
192 const internal::normalised_row_view header_row(*std::ranges::begin(exs),
193 output_index);
194
195 cols_.clear();
196 cols_.reserve(header_row.size());
197
198 for (const auto &name : header_row)
199 if constexpr (std::same_as<VT, std::string>)
200 cols_.emplace_back(*this, trim(name));
201 else
202 cols_.emplace_back(*this, trim(lexical_cast<std::string>(name)));
203
204 const auto update_domain([this](std::size_t idx, const auto &value)
205 {
206 if constexpr (std::same_as<VT, std::string>)
207 {
208 if (trim(value).empty())
209 return;
210 }
211 else
212 {
213 if (!has_value(value))
214 return;
215 }
216
217 switch (cols_[idx].domain())
218 {
219 case d_void:
220 if constexpr (std::same_as<VT, std::string>)
221 {
222 if (is_integer(value))
223 cols_[idx].domain(d_int);
224 else if (is_number(value))
225 cols_[idx].domain(d_double);
226 else
227 cols_[idx].domain(d_string);
228 }
229 else
230 {
231 if (basic_data_type(value))
232 cols_[idx].domain(static_cast<domain_t>(value.index()));
233 }
234 break;
235
236 case d_int:
237 if constexpr (std::same_as<VT, std::string>)
238 {
239 if (is_integer(value))
240 return;
241 }
242 else
243 {
244 if (value.index() == d_int)
245 return;
246 }
247
248 cols_[idx].domain(d_double);
249 [[fallthrough]];
250
251 case d_double:
252 if constexpr (std::same_as<VT, std::string>)
253 {
254 if (is_number(value))
255 return;
256 }
257 else
258 {
259 if (numerical_data_type(value))
260 return;
261 }
262 [[fallthrough]];
263
264 default:
265 if constexpr (std::same_as<VT, std::string>)
266 cols_[idx].domain(d_string);
267 else if (value.index() == d_string)
268 cols_[idx].domain(d_string);
269 }
270 });
271
272 // ---- Domain inference ----
273 constexpr std::size_t max_domain_samples(1000);
274
275 // Reservoir-sample valid rows so malformed records do not consume the
276 // inference budget and row storage stays bounded.
277 std::vector<std::ranges::range_value_t<R>> sample_rows;
278 sample_rows.reserve(max_domain_samples);
279
280 std::minstd_rand rng(0);
281 std::size_t valid_rows(0);
282
283 for (const auto &row : exs | std::views::drop(1))
284 {
285 if (output_index
286 && *output_index >= static_cast<std::size_t>(std::ranges::distance(row)))
287 continue;
288
289 if (const internal::normalised_row_view normalised(row, output_index);
290 normalised.size() != size())
291 continue;
292
293 ++valid_rows;
294
295 if (sample_rows.size() < max_domain_samples)
296 sample_rows.push_back(row);
297 else
298 {
299 std::uniform_int_distribution<std::size_t> dist(0, valid_rows - 1);
300
301 if (const auto idx(dist(rng)); idx < max_domain_samples)
302 sample_rows[idx] = row;
303 }
304 }
305
306 for (const auto &row : sample_rows)
307 {
308 const internal::normalised_row_view normalised(row, output_index);
309
310 for (std::size_t idx(0); const auto &value : normalised)
311 {
312 update_domain(idx, value);
313 ++idx;
314 }
315 }
316
317 settle_task_t();
318}
319
320#endif // include guard
size_type size() const noexcept
Definition columns_info.cc:264
void build(const R &, std::optional< std::size_t >)
Definition columns_info.h:185
bool basic_data_type(domain_t d) noexcept
Definition value.cc:40
bool has_value(const value_t &v) noexcept
Definition value.cc:31
bool numerical_data_type(domain_t d) noexcept
Definition value.cc:65
domain_t
Definition value.h:38