If the lookupMap IDataView in the MapValue API is constructed so that the first column contains the keys, and the second one contains the values, this API will work as expected; if the lookup idv is constructed in any other order, the API won't work, because the Train method of the transformer has hardcoded the index = 0 to be the keys of the idv, and index = 1 to be the values .
The ValueMapping Train should not assume that the first column is the keys, and the second is the values; it should instead save the columns passed to it from the API (and not just the column names, like it is doing now) and get the cursor based on the columns, rather than indices.
Easy repro: : swap the order of the members in the LookupMap class below, and see how it fails.
// Type for the IDataView that will be serving as the mapprivateclassLookupMap{publicfloatValue{get;set;}publicstringCategory{get;set;}}usingSystem;usingSystem.Collections.Generic;namespaceMicrosoft.ML.Samples.Dynamic{publicstaticclassMapValueIdvLookup{/// This example demonstrates the use of MapValue by mapping floats to strings, looking up the mapping in an IDataView. /// This is useful to map types to a grouping. publicstaticvoidExample(){// Create a new ML context, for ML.NET operations. It can be used for exception tracking and logging, // as well as the source of randomness.varmlContext=newMLContext();// Get a small dataset as an IEnumerable.varrawData=new[]{newDataPoint(){Price=3.14f},newDataPoint(){Price=2000f},newDataPoint(){Price=1.19f},newDataPoint(){Price=2.17f},newDataPoint(){Price=33.784f},};// Convert to IDataViewvardata=mlContext.Data.LoadFromEnumerable(rawData);// Create the lookup map data IEnumerable. varlookupData=new[]{newLookupMap{Value=3.14f,Category="Low"},newLookupMap{Value=1.19f,Category="Low"},newLookupMap{Value=2.17f,Category="Low"},newLookupMap{Value=33.784f,Category="Medium"},newLookupMap{Value=2000f,Category="High"}};// Convert to IDataViewvarlookupIdvMap=mlContext.Data.LoadFromEnumerable(lookupData);// Constructs the ValueMappingEstimator making the ML.NET pipelinevarpipeline=mlContext.Transforms.Conversion.MapValue("PriceCategory",lookupIdvMap,lookupIdvMap.Schema["Value"],lookupIdvMap.Schema["Category"],"Price");// Fits the ValueMappingEstimator and transforms the data converting the Price to PriceCategory.IDataViewtransformedData=pipeline.Fit(data).Transform(data);// Getting the resulting data as an IEnumerable.IEnumerable<TransformedData>features=mlContext.Data.CreateEnumerable<TransformedData>(transformedData,reuseRowObject:false);Console.WriteLine($" Price PriceCategory");foreach(varfeatureRowinfeatures)Console.WriteLine($"{featureRow.Price}\t\t{featureRow.PriceCategory}");// TransformedData obtained post-transformation.//// Price PriceCategory// 3.14 Low// 2000 High// 1.19 Low// 2.17 Low// 33.784 Medium}// Type for the IDataView that will be serving as the mapprivateclassLookupMap{publicfloatValue{get;set;}publicstringCategory{get;set;}}privateclassDataPoint{publicfloatPrice{get;set;}}privateclassTransformedData:DataPoint{publicstringPriceCategory{get;set;}}}}
If the lookupMap IDataView in the MapValue API is constructed so that the first column contains the keys, and the second one contains the values, this API will work as expected; if the lookup idv is constructed in any other order, the API won't work, because the Train method of the transformer has hardcoded the index = 0 to be the keys of the idv, and index = 1 to be the values .
The ValueMapping Train should not assume that the first column is the keys, and the second is the values; it should instead save the columns passed to it from the API (and not just the column names, like it is doing now) and get the cursor based on the columns, rather than indices.
Easy repro: : swap the order of the members in the LookupMap class below, and see how it fails.