@@ -301,32 +301,33 @@ where
301301{
302302 let y = y. as_single_targets ( ) ;
303303
304- // counts the instances of two distinct class labels
305304 let mut binary_classes = [ None , None ] ;
306- // find binary classes of our target dataset
307305 for class in y {
308306 binary_classes = match binary_classes {
309- // count the first class label
310307 [ None , None ] => [ Some ( ( class, 1 ) ) , None ] ,
311- // if the class has already been counted, increment the count
312308 [ Some ( ( c, count) ) , c2] if c == class => [ Some ( ( class, count + 1 ) ) , c2] ,
313309 [ c1, Some ( ( c, count) ) ] if c == class => [ c1, Some ( ( class, count + 1 ) ) ] ,
314- // count the second class label
315310 [ Some ( c1) , None ] => [ Some ( c1) , Some ( ( class, 1 ) ) ] ,
316-
317- // should not be possible
318311 [ None , Some ( _) ] => unreachable ! ( "impossible binary class array" ) ,
319- // found 3rd distinct class
320312 [ Some ( _) , Some ( _) ] => return Err ( Error :: TooManyClasses ) ,
321313 } ;
322314 }
323315
324- let ( pos_class , neg_class ) = match binary_classes {
316+ let ( class_a , class_b ) = match binary_classes {
325317 [ Some ( a) , Some ( b) ] => ( a, b) ,
326318 _ => return Err ( Error :: TooFewClasses ) ,
327319 } ;
328320
329- let mut target_array = y
321+ // Sort by label value (Ord), not by encounter order or count.
322+ // The smaller label is always negative (-1),
323+ // the larger label is always positive (+1).
324+ let ( neg_class, pos_class) = if class_a. 0 < class_b. 0 {
325+ ( class_a, class_b)
326+ } else {
327+ ( class_b, class_a)
328+ } ;
329+
330+ let target_array = y
330331 . into_iter ( )
331332 . map ( |x| {
332333 if x == pos_class. 0 {
@@ -337,24 +338,14 @@ where
337338 } )
338339 . collect :: < Array1 < _ > > ( ) ;
339340
340- let ( pos_cl, neg_cl) = if pos_class. 1 < neg_class. 1 {
341- // If we found the larger class first, flip the sign in the target
342- // vector, so that -1.0 is always the label for the smaller class
343- // and 1.0 the label for the larger class
344- target_array *= -F :: one ( ) ;
345- ( neg_class. 0 . clone ( ) , pos_class. 0 . clone ( ) )
346- } else {
347- ( pos_class. 0 . clone ( ) , neg_class. 0 . clone ( ) )
348- } ;
349-
350341 Ok ( (
351342 BinaryClassLabels {
352343 pos : ClassLabel {
353- class : pos_cl ,
344+ class : pos_class . 0 . clone ( ) ,
354345 label : F :: POSITIVE_LABEL ,
355346 } ,
356347 neg : ClassLabel {
357- class : neg_cl ,
348+ class : neg_class . 0 . clone ( ) ,
358349 label : F :: NEGATIVE_LABEL ,
359350 } ,
360351 } ,
@@ -989,7 +980,7 @@ mod test {
989980 let dataset = Dataset :: new ( x, y) ;
990981 let res = log_reg. fit ( & dataset) . unwrap ( ) ;
991982 assert_abs_diff_eq ! ( res. intercept( ) , 0.0 ) ;
992- assert ! ( res. params( ) . abs_diff_eq( & array![ - 0.681 ] , 1e-3 ) ) ;
983+ assert ! ( res. params( ) . abs_diff_eq( & array![ 0.681 ] , 1e-3 ) ) ;
993984 assert_eq ! (
994985 & res. predict( dataset. records( ) ) ,
995986 dataset. targets( ) . as_single_targets( )
@@ -1172,7 +1163,7 @@ mod test {
11721163 let dataset = Dataset :: new ( x, y) ;
11731164 let res = log_reg. fit ( & dataset) . unwrap ( ) ;
11741165 assert_abs_diff_eq ! ( res. intercept( ) , 0.0_f32 ) ;
1175- assert ! ( res. params( ) . abs_diff_eq( & array![ - 0.682_f32 ] , 1e-3 ) ) ;
1166+ assert ! ( res. params( ) . abs_diff_eq( & array![ 0.682_f32 ] , 1e-3 ) ) ;
11761167 assert_eq ! (
11771168 & res. predict( dataset. records( ) ) ,
11781169 dataset. targets( ) . as_single_targets( )
@@ -1375,4 +1366,28 @@ mod test {
13751366 }
13761367 ) ) ;
13771368 }
1369+
1370+ #[ test]
1371+ fn label_order_independent ( ) {
1372+ let x1 = array ! [ [ -1.0 ] , [ 1.0 ] , [ -0.5 ] , [ 0.5 ] ] ;
1373+ let y1 = array ! [ "cat" , "dog" , "cat" , "dog" ] ;
1374+
1375+ let x2 = array ! [ [ 1.0 ] , [ -1.0 ] , [ 0.5 ] , [ -0.5 ] ] ;
1376+ let y2 = array ! [ "dog" , "cat" , "dog" , "cat" ] ;
1377+
1378+ let model1 = LogisticRegression :: default ( )
1379+ . fit ( & Dataset :: new ( x1, y1) )
1380+ . unwrap ( ) ;
1381+ let model2 = LogisticRegression :: default ( )
1382+ . fit ( & Dataset :: new ( x2, y2) )
1383+ . unwrap ( ) ;
1384+
1385+ assert_eq ! ( model1. labels( ) . pos. class, "dog" ) ;
1386+ assert_eq ! ( model1. labels( ) . neg. class, "cat" ) ;
1387+ assert_eq ! ( model2. labels( ) . pos. class, "dog" ) ;
1388+ assert_eq ! ( model2. labels( ) . neg. class, "cat" ) ;
1389+
1390+ assert_abs_diff_eq ! ( model1. intercept( ) , model2. intercept( ) ) ;
1391+ assert ! ( model1. params( ) . abs_diff_eq( model2. params( ) , 1e-6 ) ) ;
1392+ }
13781393}
0 commit comments