@@ -24,11 +24,11 @@ impl<X, Y> From<(X, Y)> for Point<X, Y> {
24
24
///
25
25
/// Points on a [`ScaleKind::Text`] are treated categorically with all duplicates removed and in an arbitary order. Points on other [`ScaleKind`] are treated numerically as a range
26
26
#[ derive( Debug , Clone , Copy , PartialEq ) ]
27
- pub enum ScaleKind {
27
+ pub ( crate ) enum ScaleKind {
28
28
Number ,
29
29
Integer ,
30
30
Float ,
31
- Text ,
31
+ Categorical ,
32
32
}
33
33
34
34
impl From < ColumnType > for ScaleKind {
@@ -37,7 +37,7 @@ impl From<ColumnType> for ScaleKind {
37
37
ColumnType :: Number => ScaleKind :: Number ,
38
38
ColumnType :: Integer => ScaleKind :: Integer ,
39
39
ColumnType :: Float => ScaleKind :: Float ,
40
- _ => ScaleKind :: Text ,
40
+ _ => ScaleKind :: Categorical ,
41
41
}
42
42
}
43
43
}
@@ -59,13 +59,19 @@ enum ScaleValues {
59
59
end : f32 ,
60
60
step : f32 ,
61
61
} ,
62
- Text ( Vec < String > ) ,
62
+ Categorical ( Vec < Data > ) ,
63
63
}
64
64
65
65
#[ derive( Debug , Clone , PartialEq ) ]
66
66
pub struct Scale {
67
- pub kind : ScaleKind ,
67
+ /// The type of scale
68
+ pub ( crate ) kind : ScaleKind ,
69
+ /// The values within the scale
68
70
values : ScaleValues ,
71
+ /// The number of points on the scale.
72
+ ///
73
+ /// For non-categorical data this is at most one more than the number of
74
+ /// points used to generate the scale
69
75
pub length : usize ,
70
76
}
71
77
@@ -76,13 +82,11 @@ impl Scale {
76
82
pub ( crate ) fn new ( points : impl IntoIterator < Item = impl Into < Data > > , kind : ScaleKind ) -> Self {
77
83
let points = points. into_iter ( ) . map ( Into :: into) ;
78
84
match kind {
79
- ScaleKind :: Text => {
80
- let values = points
81
- . map ( |point| point. to_string ( ) )
82
- . collect :: < HashSet < String > > ( ) ;
83
- let values = values. into_iter ( ) . collect :: < Vec < String > > ( ) ;
85
+ ScaleKind :: Categorical => {
86
+ let values = points. collect :: < HashSet < Data > > ( ) ;
87
+ let values = values. into_iter ( ) . collect :: < Vec < Data > > ( ) ;
84
88
let length = values. len ( ) ;
85
- let values = ScaleValues :: Text ( values) ;
89
+ let values = ScaleValues :: Categorical ( values) ;
86
90
87
91
Self {
88
92
kind,
@@ -100,7 +104,7 @@ impl Scale {
100
104
valid. insert ( num) ;
101
105
}
102
106
other => {
103
- invalid. insert ( other. to_string ( ) ) ;
107
+ invalid. insert ( other) ;
104
108
}
105
109
} ;
106
110
}
@@ -117,15 +121,15 @@ impl Scale {
117
121
}
118
122
} else if !invalid. is_empty ( ) {
119
123
for point in valid. into_iter ( ) {
120
- invalid. insert ( point. to_string ( ) ) ;
124
+ invalid. insert ( point. into ( ) ) ;
121
125
}
122
126
123
- let invalid = invalid. into_iter ( ) . collect :: < Vec < String > > ( ) ;
127
+ let invalid = invalid. into_iter ( ) . collect :: < Vec < Data > > ( ) ;
124
128
let length = invalid. len ( ) ;
125
129
126
130
Self {
127
- kind : ScaleKind :: Text ,
128
- values : ScaleValues :: Text ( invalid) ,
131
+ kind : ScaleKind :: Categorical ,
132
+ values : ScaleValues :: Categorical ( invalid) ,
129
133
length,
130
134
}
131
135
} else {
@@ -142,7 +146,7 @@ impl Scale {
142
146
valid. insert ( num) ;
143
147
}
144
148
other => {
145
- invalid. insert ( other. to_string ( ) ) ;
149
+ invalid. insert ( other) ;
146
150
}
147
151
}
148
152
}
@@ -159,15 +163,15 @@ impl Scale {
159
163
}
160
164
} else if !invalid. is_empty ( ) {
161
165
for point in valid. into_iter ( ) {
162
- invalid. insert ( point. to_string ( ) ) ;
166
+ invalid. insert ( point. into ( ) ) ;
163
167
}
164
168
165
- let invalid = invalid. into_iter ( ) . collect :: < Vec < String > > ( ) ;
169
+ let invalid = invalid. into_iter ( ) . collect :: < Vec < Data > > ( ) ;
166
170
let length = invalid. len ( ) ;
167
171
168
172
Self {
169
- kind : ScaleKind :: Text ,
170
- values : ScaleValues :: Text ( invalid) ,
173
+ kind : ScaleKind :: Categorical ,
174
+ values : ScaleValues :: Categorical ( invalid) ,
171
175
length,
172
176
}
173
177
} else {
@@ -187,7 +191,7 @@ impl Scale {
187
191
}
188
192
}
189
193
other => {
190
- invalid. insert ( other. to_string ( ) ) ;
194
+ invalid. insert ( other) ;
191
195
}
192
196
}
193
197
}
@@ -204,15 +208,15 @@ impl Scale {
204
208
}
205
209
} else if !invalid. is_empty ( ) {
206
210
for point in valid. into_iter ( ) {
207
- invalid. insert ( point. to_string ( ) ) ;
211
+ invalid. insert ( point. into ( ) ) ;
208
212
}
209
213
210
- let invalid = invalid. into_iter ( ) . collect :: < Vec < String > > ( ) ;
214
+ let invalid = invalid. into_iter ( ) . collect :: < Vec < Data > > ( ) ;
211
215
let length = invalid. len ( ) ;
212
216
213
217
Self {
214
- kind : ScaleKind :: Text ,
215
- values : ScaleValues :: Text ( invalid) ,
218
+ kind : ScaleKind :: Categorical ,
219
+ values : ScaleValues :: Categorical ( invalid) ,
216
220
length,
217
221
}
218
222
} else {
@@ -222,9 +226,14 @@ impl Scale {
222
226
}
223
227
}
224
228
229
+ /// Returns the points on the scale.
230
+ ///
231
+ /// Categorical scales return all points used to generate the scale.
232
+ ///
233
+ /// Non-Categorical scales return a ordered generated range, guaranteed to contain all initial points.
225
234
pub fn points ( & self ) -> Vec < Data > {
226
235
match & self . values {
227
- ScaleValues :: Text ( values) => values. iter ( ) . cloned ( ) . map ( Data :: Text ) . collect ( ) ,
236
+ ScaleValues :: Categorical ( values) => values. clone ( ) ,
228
237
ScaleValues :: Number { start, step, .. } => {
229
238
let mut output = Vec :: default ( ) ;
230
239
let n = self . length as isize ;
@@ -261,9 +270,55 @@ impl Scale {
261
270
}
262
271
}
263
272
273
+ /// Returns the successive points on the scale. For categorical and floating
274
+ /// point scales, this is the same as [`Scale::points`]
275
+ ///
276
+ /// # Example
277
+ ///
278
+ /// ```
279
+ /// use modav_core::{repr::Data, models::Scale};
280
+ ///
281
+ /// let scale = Scale::from(vec![1,2,9,10]);
282
+ /// assert_eq!(scale.ranged(), (1..=10).map(From::from).collect::<Vec<Data>>())
283
+ ///
284
+ /// ```
285
+ pub fn ranged ( & self ) -> Vec < Data > {
286
+ match & self . values {
287
+ ScaleValues :: Integer { start, end, .. } => {
288
+ let range = * start..=* end;
289
+ range. map ( From :: from) . collect ( )
290
+ }
291
+ ScaleValues :: Number { start, end, .. } => {
292
+ let range = * start..=* end;
293
+ range. map ( From :: from) . collect ( )
294
+ }
295
+ _ => self . points ( ) ,
296
+ }
297
+ }
298
+
299
+ /// Returns true if the scale contains the given [`Data`].
300
+ ///
301
+ /// For non-categorical scales, true is returned if a valid data value falls
302
+ /// the range min(Scale::points), max(Scale::points).
303
+ ///
304
+ /// # Example
305
+ ///
306
+ /// ```
307
+ /// use modav_core::{repr::Data, models::Scale};
308
+ ///
309
+ /// let scale = Scale::from(vec![1,3,4,5]);
310
+ /// assert!(scale.contains(&Data::Integer(3)));
311
+ ///
312
+ /// /// Returns true, even though 2 was not in original scale points
313
+ /// assert!(scale.contains(&Data::Integer(2)));
314
+ ///
315
+ /// /// scale doesn't contain [`Data::Number`]
316
+ /// assert!(!scale.contains(&Data::Number(3)));
317
+ ///
318
+ /// ```
264
319
pub fn contains ( & self , value : & Data ) -> bool {
265
320
match ( & self . values , value) {
266
- ( ScaleValues :: Text ( values) , Data :: Text ( val ) ) => values. contains ( val ) ,
321
+ ( ScaleValues :: Categorical ( values) , data ) => values. contains ( data ) ,
267
322
( ScaleValues :: Number { start, step, .. } , Data :: Number ( num) ) => {
268
323
let end = start + ( * step * ( self . length - 1 ) as isize ) ;
269
324
start <= num && num <= & end
@@ -280,6 +335,11 @@ impl Scale {
280
335
}
281
336
}
282
337
338
+ /// Returns true if the scale is categorical
339
+ pub fn is_categorical ( & self ) -> bool {
340
+ self . kind == ScaleKind :: Categorical
341
+ }
342
+
283
343
/// Assumes points is not empty
284
344
fn from_i32 ( points : impl Iterator < Item = i32 > ) -> Self {
285
345
let deduped = points. collect :: < HashSet < i32 > > ( ) ;
@@ -470,7 +530,7 @@ impl Scale {
470
530
}
471
531
472
532
pub fn sort ( & mut self ) {
473
- if let ScaleValues :: Text ( values) = & mut self . values {
533
+ if let ScaleValues :: Categorical ( values) = & mut self . values {
474
534
values. sort ( ) ;
475
535
}
476
536
}
@@ -569,18 +629,18 @@ mod tests {
569
629
assert ! ( !scale. contains( & Data :: Float ( 0.99 ) ) ) ;
570
630
571
631
let pnts: Vec < isize > = vec ! [ 1 , 12 , 12 , 6 , 4 , 1 , 25 ] ;
572
- let mut scale = Scale :: new ( pnts, ScaleKind :: Text ) ;
632
+ let mut scale = Scale :: new ( pnts, ScaleKind :: Categorical ) ;
573
633
scale. sort ( ) ;
574
634
575
635
assert_eq ! ( scale. length, 5 ) ;
576
636
assert_eq ! (
577
637
scale. points( ) ,
578
638
vec![
579
- Data :: Text ( "1" . into ( ) ) ,
580
- Data :: Text ( "12" . into ( ) ) ,
581
- Data :: Text ( "25" . into ( ) ) ,
582
- Data :: Text ( "4" . into ( ) ) ,
583
- Data :: Text ( "6" . into ( ) ) ,
639
+ Data :: Number ( 1 ) ,
640
+ Data :: Number ( 4 ) ,
641
+ Data :: Number ( 6 ) ,
642
+ Data :: Number ( 12 ) ,
643
+ Data :: Number ( 25 ) ,
584
644
]
585
645
) ;
586
646
@@ -597,17 +657,16 @@ mod tests {
597
657
assert_eq ! (
598
658
scale. points( ) ,
599
659
vec![
600
- Data :: Text ( "4" . into ( ) ) ,
601
- Data :: Text ( "44" . into ( ) ) ,
602
- Data :: Text ( "<None>" . into ( ) ) ,
660
+ Data :: None ,
661
+ Data :: Integer ( 4 ) ,
662
+ Data :: Integer ( 44 ) ,
603
663
Data :: Text ( "Test" . into( ) ) ,
604
664
]
605
665
) ;
606
- assert ! ( scale. contains( & Data :: Text ( "44" . into( ) ) ) ) ;
607
- assert ! ( ! scale. contains( & Data :: Integer ( 44 ) ) ) ;
608
- assert ! ( ! scale. contains( & Data :: None ) ) ;
666
+ assert ! ( ! scale. contains( & Data :: Text ( "44" . into( ) ) ) ) ;
667
+ assert ! ( scale. contains( & Data :: Integer ( 44 ) ) ) ;
668
+ assert ! ( scale. contains( & Data :: None ) ) ;
609
669
assert ! ( scale. contains( & Data :: Text ( "Test" . into( ) ) ) ) ;
610
- assert ! ( scale. contains( & Data :: Text ( "<None>" . into( ) ) ) ) ;
611
670
}
612
671
613
672
#[ test]
@@ -679,4 +738,47 @@ mod tests {
679
738
assert_eq ! ( scale. points( ) , vec![ Data :: Integer ( 0 ) ] ) ;
680
739
assert ! ( scale. contains( & Data :: Integer ( 0 ) ) ) ;
681
740
}
741
+
742
+ #[ test]
743
+ fn test_scale_ranged ( ) {
744
+ let pnts = vec ! [ 1 , 2 , 9 , 10 ] ;
745
+ let scale = Scale :: new ( pnts, ScaleKind :: Integer ) ;
746
+ let rng = scale. ranged ( ) ;
747
+
748
+ assert_eq ! (
749
+ rng,
750
+ vec![
751
+ Data :: Integer ( 1 ) ,
752
+ Data :: Integer ( 2 ) ,
753
+ Data :: Integer ( 3 ) ,
754
+ Data :: Integer ( 4 ) ,
755
+ Data :: Integer ( 5 ) ,
756
+ Data :: Integer ( 6 ) ,
757
+ Data :: Integer ( 7 ) ,
758
+ Data :: Integer ( 8 ) ,
759
+ Data :: Integer ( 9 ) ,
760
+ Data :: Integer ( 10 ) ,
761
+ ]
762
+ ) ;
763
+
764
+ let pnts: Vec < isize > = vec ! [ 1 , 2 , 9 , 10 ] ;
765
+ let scale = Scale :: new ( pnts, ScaleKind :: Number ) ;
766
+ let rng = scale. ranged ( ) ;
767
+
768
+ assert_eq ! (
769
+ rng,
770
+ vec![
771
+ Data :: Number ( 1 ) ,
772
+ Data :: Number ( 2 ) ,
773
+ Data :: Number ( 3 ) ,
774
+ Data :: Number ( 4 ) ,
775
+ Data :: Number ( 5 ) ,
776
+ Data :: Number ( 6 ) ,
777
+ Data :: Number ( 7 ) ,
778
+ Data :: Number ( 8 ) ,
779
+ Data :: Number ( 9 ) ,
780
+ Data :: Number ( 10 ) ,
781
+ ]
782
+ )
783
+ }
682
784
}
0 commit comments