@@ -30,6 +30,8 @@ internal sealed class Arguments
3030 public int ColumnCount ;
3131 public bool HasHeader ;
3232 public int MaxRowsToRead ;
33+ public uint ? LabelColumnIndex ;
34+ public string Label ;
3335
3436 public Arguments ( )
3537 {
@@ -68,13 +70,31 @@ public IntermediateColumn(ReadOnlyMemory<char>[] data, int columnId)
6870 }
6971
7072 public ReadOnlyMemory < char > [ ] RawData { get { return _data ; } }
73+
74+ public string Name { get ; set ; }
75+
76+ public bool HasAllBooleanValues ( )
77+ {
78+ if ( this . RawData . Skip ( 1 )
79+ . All ( x => {
80+ bool value ;
81+ // (note: Conversions.TryParse parses an empty string as a Boolean)
82+ return ! string . IsNullOrEmpty ( x . ToString ( ) ) &&
83+ Conversions . TryParse ( in x , out value ) ;
84+ } ) )
85+ {
86+ return true ;
87+ }
88+
89+ return false ;
90+ }
7191 }
7292
73- public struct Column
93+ public class Column
7494 {
7595 public readonly int ColumnIndex ;
76- public readonly PrimitiveType ItemType ;
7796
97+ public PrimitiveType ItemType ;
7898 public string SuggestedName ;
7999
80100 public Column ( int columnIndex , string suggestedName , PrimitiveType itemType )
@@ -131,13 +151,10 @@ public void Apply(IntermediateColumn[] columns)
131151 {
132152 foreach ( var col in columns )
133153 {
134- if ( ! col . RawData . Skip ( 1 )
135- . All ( x =>
136- {
137- bool value ;
138- return Conversions . TryParse ( in x , out value ) ;
139- } )
140- )
154+ // skip columns that already have a suggested type,
155+ // or that don't have all Boolean values
156+ if ( col . SuggestedType != null ||
157+ ! col . HasAllBooleanValues ( ) )
141158 {
142159 continue ;
143160 }
@@ -156,12 +173,6 @@ public void Apply(IntermediateColumn[] columns)
156173 {
157174 foreach ( var col in columns )
158175 {
159- // skip columns that already have a suggested type
160- if ( col . SuggestedType != null )
161- {
162- continue ;
163- }
164-
165176 if ( ! col . RawData . Skip ( 1 )
166177 . All ( x =>
167178 {
@@ -215,9 +226,9 @@ public void Apply(IntermediateColumn[] columns)
215226 private static IEnumerable < ITypeInferenceExpert > GetExperts ( )
216227 {
217228 // Current logic is pretty primitive: if every value (except the first) of a column
218- // parses as a boolean it's boolean, if it parses as numeric then it's numeric. Otherwise, it is text.
219- yield return new Experts . BooleanValues ( ) ;
229+ // parses as numeric then it's numeric. Else if it parses as a Boolean, it's Boolean. Otherwise, it is text.
220230 yield return new Experts . AllNumericValues ( ) ;
231+ yield return new Experts . BooleanValues ( ) ;
221232 yield return new Experts . EverythingText ( ) ;
222233 }
223234
@@ -329,7 +340,6 @@ private static InferenceResult InferTextFileColumnTypesCore(MLContext env, IMult
329340 }
330341
331342 // suggest names
332- var names = new List < string > ( ) ;
333343 usedNames . Clear ( ) ;
334344 foreach ( var col in cols )
335345 {
@@ -338,14 +348,23 @@ private static InferenceResult InferTextFileColumnTypesCore(MLContext env, IMult
338348 name0 = name = SuggestName ( col , args . HasHeader ) ;
339349 int i = 0 ;
340350 while ( ! usedNames . Add ( name ) )
351+ {
341352 name = string . Format ( "{0}_{1:00}" , name0 , i ++ ) ;
342- names . Add ( name ) ;
353+ }
354+ col . Name = name ;
355+ }
356+
357+ // validate & retrieve label column
358+ var labelColumn = GetAndValidateLabelColumn ( args , cols ) ;
359+
360+ // if label column has all Boolean values, set its type as Boolean
361+ if ( labelColumn . HasAllBooleanValues ( ) )
362+ {
363+ labelColumn . SuggestedType = BoolType . Instance ;
343364 }
344- var outCols =
345- cols . Select ( ( x , i ) => new Column ( x . ColumnId , names [ i ] , x . SuggestedType ) ) . ToArray ( ) ;
346365
347- var numerics = outCols . Count ( x => x . ItemType . IsNumber ( ) ) ;
348-
366+ var outCols = cols . Select ( x => new Column ( x . ColumnId , x . Name , x . SuggestedType ) ) . ToArray ( ) ;
367+
349368 return InferenceResult . Success ( outCols , args . HasHeader , cols . Select ( col => col . RawData ) . ToArray ( ) ) ;
350369 }
351370
@@ -361,6 +380,31 @@ private static string Sanitize(string header)
361380 return string . Join ( "" , header . Select ( x => Char . IsLetterOrDigit ( x ) ? x : '_' ) ) ;
362381 }
363382
383+ private static IntermediateColumn GetAndValidateLabelColumn ( Arguments args , IntermediateColumn [ ] cols )
384+ {
385+ IntermediateColumn labelColumn = null ;
386+ if ( args . LabelColumnIndex != null )
387+ {
388+ // if label column index > inferred # of columns, throw error
389+ if ( args . LabelColumnIndex >= cols . Count ( ) )
390+ {
391+ throw new ArgumentOutOfRangeException ( nameof ( args . LabelColumnIndex ) , $ "Label column index ({ args . LabelColumnIndex } ) is >= than # of inferred columns ({ cols . Count ( ) } ).") ;
392+ }
393+
394+ labelColumn = cols [ args . LabelColumnIndex . Value ] ;
395+ }
396+ else
397+ {
398+ labelColumn = cols . FirstOrDefault ( c => c . Name == args . Label ) ;
399+ if ( labelColumn == null )
400+ {
401+ throw new ArgumentException ( $ "Specified label column '{ args . Label } ' was not found.") ;
402+ }
403+ }
404+
405+ return labelColumn ;
406+ }
407+
364408 public static TextLoader . Column [ ] GenerateLoaderColumns ( Column [ ] columns )
365409 {
366410 var loaderColumns = new List < TextLoader . Column > ( ) ;
0 commit comments