1use crate::bitsets::{BitCollection, Bitset, BitsetInit};
2use crate::data::{Dataset, Feature};
3
4#[derive(Clone, Debug)]
7pub struct HeuristicValues {
8 gini_per_split: Vec<Vec<usize>>,
10 best_gini_per_feature: Vec<(f64, usize)>,
13}
14
15impl HeuristicValues {
16 pub fn new(num_features: usize) -> Self {
18 Self {
19 gini_per_split: vec![Vec::new(); num_features],
20 best_gini_per_feature: (0..num_features).map(|i| (1.0, i)).collect(),
21 }
22 }
23
24 pub fn set_feature_ginis(&mut self, feature: usize, split_indices: Vec<usize>, best_gini: f64) {
27 if feature < self.gini_per_split.len() {
28 self.gini_per_split[feature] = split_indices;
29 self.best_gini_per_feature[feature] = (best_gini, feature);
30 }
31 }
32
33 pub fn sort_by_gini(&mut self) {
35 self.best_gini_per_feature
36 .sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
37 }
38}
39
40pub struct DataView<'a> {
46 pub dataset: &'a Dataset,
48 pub total_instances: usize,
50 pub feature_columns: Vec<Vec<usize>>,
53 pub possible_split_indices: Vec<Vec<usize>>,
56 pub label_freq: Vec<usize>,
58 pub sort_by_heuristic: bool,
60 pub heuristic_values: HeuristicValues,
62 pub bitset: Bitset,
64}
65
66impl<'a> DataView<'a> {
67 pub fn root(dataset: &'a Dataset, sort_by_heuristic: bool) -> Self {
69 let total_instances = dataset.count();
70 let mut label_freq = vec![0; dataset.num_labels()];
71 let mut feature_columns = Vec::new();
72 let mut possible_split_indices = Vec::new();
73
74 for (feature_idx, feature) in dataset.into_iter().enumerate() {
75 let feature_len = feature.len();
76
77 let mut splits = Vec::new();
78 let mut last_unique_index: Option<usize> = None;
79
80 for pos in 0..feature_len {
81 let el = &feature[pos];
82
83 if feature_idx == 0 {
84 label_freq[el.label as usize] += 1;
85 }
86
87 if let Some(last) = last_unique_index {
88 if el.unique_value_idx != last {
89 splits.push(pos);
90 }
91 }
92 last_unique_index = Some(el.unique_value_idx);
93 }
94 possible_split_indices.push(splits);
95 feature_columns.push((0..feature_len).collect::<Vec<usize>>());
96 }
97
98 let mut heuristic_values = HeuristicValues::new(dataset.num_features());
99
100 if sort_by_heuristic {
101 for feature_idx in 0..dataset.num_features() {
102 let feature = &dataset[feature_idx];
103 let idx = &feature_columns[feature_idx];
104 let (ordered_index, gini) = Self::compute_gini_for_all_splits(
105 feature,
106 idx,
107 &possible_split_indices[feature_idx],
108 &label_freq,
109 dataset.num_labels(),
110 );
111
112 heuristic_values.set_feature_ginis(feature_idx, ordered_index, gini);
113 }
114 heuristic_values.sort_by_gini();
115 }
116
117 let mut bitset = Bitset::new(BitsetInit::Full(total_instances));
118 bitset.save_count();
119
120 Self {
121 dataset,
122 total_instances,
123 feature_columns,
124 possible_split_indices,
125 label_freq,
126 sort_by_heuristic,
127 heuristic_values,
128 bitset,
129 }
130 }
131
132 pub fn get_dataset_size(&self) -> usize {
134 self.feature_columns[0].len()
135 }
136
137 pub fn get_feature_number(&self) -> usize {
139 self.dataset.num_features()
140 }
141
142 pub fn get_sorted_feature(&self, f: usize) -> &Feature {
144 &self.dataset[f]
145 }
146
147 pub fn get_feature_indices(&self, f: usize) -> &[usize] {
150 &self.feature_columns[f]
151 }
152
153 pub fn get_labels_freqs(&self) -> &[usize] {
155 &self.label_freq
156 }
157
158 pub fn get_num_labels(&self) -> usize {
160 self.dataset.num_labels()
161 }
162
163 pub fn get_possible_split_indices(&self, f: usize) -> &[usize] {
165 &self.possible_split_indices[f]
166 }
167
168 pub fn get_max_splits(&self) -> usize {
170 self.possible_split_indices
171 .iter()
172 .map(|x| x.len())
173 .max()
174 .unwrap_or(0)
175 }
176
177 pub fn ordered_possible_splits(&self, feature: usize) -> &[usize] {
180 &self.heuristic_values.gini_per_split[feature]
181 }
182
183 pub fn features_best_score(&self) -> &[(f64, usize)] {
186 &self.heuristic_values.best_gini_per_feature
187 }
188
189 #[inline]
190 fn compute_split_indices_for(col: &Feature, idxs: &[usize]) -> Vec<usize> {
191 if idxs.is_empty() {
192 return Vec::new();
193 }
194 let mut out = Vec::with_capacity(idxs.len() / 10);
195 let mut last = None::<usize>;
196
197 for (i, &pos) in idxs.iter().enumerate() {
198 let cur = col[pos].unique_value_idx;
199 if i > 0 && Some(cur) != last {
200 out.push(i);
201 }
202 last = Some(cur);
203 }
204
205 out.shrink_to_fit();
206 out
207 }
208
209 pub fn initialize_split_parameters(
212 &self,
213 feature_index: usize,
214 split_point: usize,
215 left_freq: &mut [usize],
216 right_freq: &mut [usize],
217 ) {
218 let num_labels = self.dataset.num_labels();
219
220 let total_size = self.get_dataset_size();
221 let feature_ids = self.get_feature_indices(feature_index);
222 let feature = self.get_sorted_feature(feature_index);
223
224 if 2 * split_point < total_size {
226 for i in 0..split_point {
227 let data_point = &feature[feature_ids[i]];
228 left_freq[data_point.label as usize] += 1;
229 }
230 for label in 0..num_labels {
231 right_freq[label] = self.label_freq[label] - left_freq[label];
232 }
233 } else {
234 for i in split_point..total_size {
235 let data_point = &feature[feature_ids[i]];
236 right_freq[data_point.label as usize] += 1;
237 }
238 for label in 0..num_labels {
239 left_freq[label] = self.label_freq[label] - right_freq[label];
240 }
241 }
242 }
243
244 fn compute_gini_for_all_splits(
249 feature: &Feature,
250 idx: &[usize],
251 possible_splits: &[usize],
252 label_freq: &[usize],
253 num_labels: usize,
254 ) -> (Vec<usize>, f64) {
255 if feature.is_empty() || possible_splits.is_empty() {
256 return (Vec::new(), 1.0);
257 }
258
259 let mut gini_values = Vec::with_capacity(possible_splits.len());
260 let mut left_label_freq = vec![0usize; num_labels];
261 let mut right_label_freq = label_freq.to_vec();
262 let mut best_gini = 1.0;
263
264 let mut last_pos = 0;
265
266 for (split_idx, &split_pos) in possible_splits.iter().enumerate() {
267 for &data_idx in &idx[last_pos..split_pos] {
269 let label = feature[data_idx].label as usize;
270 right_label_freq[label] -= 1;
271 left_label_freq[label] += 1;
272 }
273
274 let left_count = split_pos;
275 let right_count = idx.len() - left_count;
276
277 let gini = Self::compute_gini_from_frequencies(
278 &left_label_freq,
279 &right_label_freq,
280 left_count,
281 right_count,
282 );
283
284 if gini < best_gini {
285 best_gini = gini;
286 }
287
288 gini_values.push((split_idx, gini));
289 last_pos = split_pos;
290 }
291
292 gini_values.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
293
294 let sorted_indices: Vec<usize> = gini_values.into_iter().map(|(idx, _)| idx).collect();
295
296 (sorted_indices, best_gini)
297 }
298
299 #[inline]
302 fn compute_gini_from_frequencies(
303 left_freq: &[usize],
304 right_freq: &[usize],
305 left_count: usize,
306 right_count: usize,
307 ) -> f64 {
308 let mut left_gini = 1.0;
309 let mut right_gini = 1.0;
310
311 if left_count > 0 {
312 for &freq in left_freq {
313 let prob = freq as f64 / left_count as f64;
314 left_gini -= prob * prob;
315 }
316 }
317
318 if right_count > 0 {
319 for &freq in right_freq {
320 let prob = freq as f64 / right_count as f64;
321 right_gini -= prob * prob;
322 }
323 }
324
325 let total = left_count + right_count;
326 if total > 0 {
327 (left_gini * left_count as f64 + right_gini * right_count as f64) / total as f64
328 } else {
329 1.0
330 }
331 }
332
333 pub fn split(&self, sf: usize, split_point: usize) -> (Self, Self) {
336 #[cfg(feature = "profiling")]
337 coz::scope!("Split view");
338 let col = &self.dataset[sf];
339 let sf_idxs = &self.feature_columns[sf];
340
341 let (sf_left_idxs, sf_right_idxs) = sf_idxs.split_at(split_point);
342
343 let mut left_bitset = Bitset::new(BitsetInit::Empty(self.total_instances));
344 let mut left_label_freq = vec![0; self.dataset.num_labels()];
345
346 for &pos in sf_left_idxs {
347 let el = &col[pos];
348 left_bitset.set(el.tid);
349 left_label_freq[el.label as usize] += 1;
350 }
351 left_bitset.save_count();
352
353 let mut right_label_freq = vec![0; self.dataset.num_labels()];
354 for label in 0..self.dataset.num_labels() {
355 right_label_freq[label] = self.label_freq[label] - left_label_freq[label];
356 }
357
358 let num_features = self.get_feature_number();
359 let left_size_estimate = split_point;
360 let right_size_estimate = self.get_dataset_size() - split_point;
361
362 let mut left_pfi: Vec<Vec<usize>> = (0..num_features)
363 .map(|_| Vec::with_capacity(left_size_estimate))
364 .collect();
365 let mut right_pfi: Vec<Vec<usize>> = (0..num_features)
366 .map(|_| Vec::with_capacity(right_size_estimate))
367 .collect();
368
369 let mut left_split_indices: Vec<Vec<usize>> = (0..num_features)
370 .map(|_| Vec::with_capacity(left_size_estimate / 10))
371 .collect();
372 let mut right_split_indices: Vec<Vec<usize>> = (0..num_features)
373 .map(|_| Vec::with_capacity(right_size_estimate / 10))
374 .collect();
375
376 let mut left_heuristics = HeuristicValues::new(num_features);
377 let mut right_heuristics = HeuristicValues::new(num_features);
378
379 let num_labels = self.dataset.num_labels();
382 let heuristic_width = if self.sort_by_heuristic {
383 num_labels
384 } else {
385 0
386 };
387 let mut left_label_running = vec![0usize; heuristic_width];
388 let mut right_label_running = vec![0usize; heuristic_width];
389 let mut left_label_remaining = vec![0usize; heuristic_width];
390 let mut right_label_remaining = vec![0usize; heuristic_width];
391 let mut left_gini_values: Vec<(usize, f64)> = Vec::new();
392 let mut right_gini_values: Vec<(usize, f64)> = Vec::new();
393
394 for f in 0..num_features {
395 if f == sf {
396 left_pfi[f] = sf_left_idxs.to_vec();
398 right_pfi[f] = sf_right_idxs.to_vec();
399
400 left_split_indices[f] =
401 Self::compute_split_indices_for(&self.dataset[f], sf_left_idxs);
402 right_split_indices[f] =
403 Self::compute_split_indices_for(&self.dataset[f], sf_right_idxs);
404
405 if self.sort_by_heuristic {
406 let (ordered_splits, best_gini) = Self::compute_gini_for_all_splits(
407 &self.dataset[f],
408 &left_pfi[f],
409 &left_split_indices[f],
410 &left_label_freq,
411 self.dataset.num_labels(),
412 );
413 left_heuristics.set_feature_ginis(f, ordered_splits, best_gini);
414
415 let (ordered_splits, best_gini) = Self::compute_gini_for_all_splits(
416 &self.dataset[f],
417 &right_pfi[f],
418 &right_split_indices[f],
419 &right_label_freq,
420 self.dataset.num_labels(),
421 );
422 right_heuristics.set_feature_ginis(f, ordered_splits, best_gini);
423 }
424 continue;
425 }
426
427 let fcol = &self.dataset[f];
428 let parent_idxs = &self.feature_columns[f];
429
430 let mut left_last_unique: Option<usize> = None;
431 let mut right_last_unique: Option<usize> = None;
432 let mut left_counter = 0;
433 let mut right_counter = 0;
434
435 let mut left_best_gini = 1.0;
436 let mut right_best_gini = 1.0;
437
438 if self.sort_by_heuristic {
439 left_gini_values.clear();
440 right_gini_values.clear();
441 left_label_running.fill(0);
442 right_label_running.fill(0);
443 left_label_remaining.copy_from_slice(&left_label_freq);
444 right_label_remaining.copy_from_slice(&right_label_freq);
445 }
446
447 for &pos in parent_idxs {
448 let el = &fcol[pos];
449 let row = el.tid;
450 let label = el.label as usize;
451
452 if left_bitset.contains(row) {
453 left_pfi[f].push(pos);
454
455 if let Some(last) = left_last_unique {
456 if el.unique_value_idx != last {
457 left_split_indices[f].push(left_counter);
458
459 if self.sort_by_heuristic {
460 let gini = Self::compute_gini_from_frequencies(
461 &left_label_running,
462 &left_label_remaining,
463 left_counter,
464 left_size_estimate - left_counter,
465 );
466 if gini < left_best_gini {
467 left_best_gini = gini;
468 }
469 left_gini_values.push((left_split_indices[f].len() - 1, gini));
470 }
471 }
472 }
473 left_last_unique = Some(el.unique_value_idx);
474 left_counter += 1;
475
476 if self.sort_by_heuristic {
477 left_label_remaining[label] -= 1;
478 left_label_running[label] += 1;
479 }
480 } else {
481 right_pfi[f].push(pos);
482
483 if let Some(last) = right_last_unique {
484 if el.unique_value_idx != last {
485 right_split_indices[f].push(right_counter);
486
487 if self.sort_by_heuristic {
488 let gini = Self::compute_gini_from_frequencies(
489 &right_label_running,
490 &right_label_remaining,
491 right_counter,
492 right_size_estimate - right_counter,
493 );
494 if gini < right_best_gini {
495 right_best_gini = gini;
496 }
497 right_gini_values.push((right_split_indices[f].len() - 1, gini));
498 }
499 }
500 }
501 right_last_unique = Some(el.unique_value_idx);
502 right_counter += 1;
503
504 if self.sort_by_heuristic {
505 right_label_remaining[label] -= 1;
506 right_label_running[label] += 1;
507 }
508 }
509 }
510
511 if self.sort_by_heuristic {
512 left_gini_values
513 .sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
514 right_gini_values
515 .sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
516
517 let left_sorted_indices: Vec<usize> =
518 left_gini_values.iter().map(|&(idx, _)| idx).collect();
519 let right_sorted_indices: Vec<usize> =
520 right_gini_values.iter().map(|&(idx, _)| idx).collect();
521
522 left_heuristics.set_feature_ginis(f, left_sorted_indices, left_best_gini);
523 right_heuristics.set_feature_ginis(f, right_sorted_indices, right_best_gini);
524 }
525
526 }
528
529 let mut right_bitset = self.bitset.intersect_with(&left_bitset, true);
530 right_bitset.save_count();
531
532 if self.sort_by_heuristic {
533 left_heuristics.sort_by_gini();
534 right_heuristics.sort_by_gini();
535 }
536
537 let left = Self {
538 dataset: self.dataset,
539 total_instances: self.total_instances,
540 feature_columns: left_pfi,
541 possible_split_indices: left_split_indices,
542 label_freq: left_label_freq,
543 sort_by_heuristic: self.sort_by_heuristic,
544 heuristic_values: left_heuristics,
545 bitset: left_bitset,
546 };
547
548 let right = Self {
549 dataset: self.dataset,
550 total_instances: self.total_instances,
551 feature_columns: right_pfi,
552 possible_split_indices: right_split_indices,
553 label_freq: right_label_freq,
554 sort_by_heuristic: self.sort_by_heuristic,
555 heuristic_values: right_heuristics,
556 bitset: right_bitset,
557 };
558
559 (left, right)
560 }
561
562 pub fn len(&self) -> usize {
564 debug_assert!(
565 self.bitset.count() == self.feature_columns[0].len(),
566 "Mismatched size in bitset and feature columns"
567 );
568 self.feature_columns[0].len()
569 }
570
571 pub fn is_empty(&self) -> bool {
573 self.len() == 0
574 }
575}
576
577#[cfg(test)]
578mod data_view_tests {
579 use crate::bitsets::BitCollection;
580 use crate::data::view::DataView;
581 use crate::reader::data_reader::DataReader;
582 use crate::reader::DataReaderError;
583 use crate::tests::fixture;
584
585 #[test]
586 fn root_view_covers_the_whole_dataset() -> Result<(), DataReaderError> {
587 let reader = DataReader::default();
588 let mut dataset = reader.read_file(&fixture("avila_1k.txt"))?;
589 dataset.sort_features();
590 dataset.compute_unique_feature_values();
591
592 let view = DataView::root(&dataset, true);
593
594 assert_eq!(view.len(), dataset.count());
595 assert_eq!(view.bitset.count(), dataset.count());
596 assert_eq!(view.get_feature_number(), dataset.num_features());
597 assert_eq!(
598 view.get_labels_freqs().iter().sum::<usize>(),
599 dataset.count(),
600 "the root label histogram must account for every instance"
601 );
602
603 for f in 0..dataset.num_features() {
606 let splits = view.get_possible_split_indices(f);
607 assert!(splits.windows(2).all(|w| w[0] < w[1]));
608 assert!(splits.iter().all(|&i| i > 0 && i < dataset.count()));
609 }
610
611 Ok(())
612 }
613
614 #[test]
615 fn split_partitions_the_view_without_loss() -> Result<(), DataReaderError> {
616 let reader = DataReader::default();
617 let mut dataset = reader.read_file(&fixture("avila_1k.txt"))?;
618 dataset.sort_features();
619 dataset.compute_unique_feature_values();
620
621 let view = DataView::root(&dataset, false);
622 let feature = 4;
623 let split = view.get_possible_split_indices(feature)[3];
624 let (left, right) = view.split(feature, split);
625
626 assert_eq!(left.len() + right.len(), view.len());
627 assert_eq!(left.bitset.count(), left.len());
628 assert_eq!(right.bitset.count(), right.len());
629 for label in 0..dataset.num_labels() {
630 assert_eq!(
631 left.get_labels_freqs()[label] + right.get_labels_freqs()[label],
632 view.get_labels_freqs()[label]
633 );
634 }
635
636 Ok(())
637 }
638}