System.InvalidCastException: Unable to cast object of type 'System.Double' to type 'System.Single'.
Looking to convert this example (https://github.com/dotnet/machinelearning-samples/tree/master/samples/csharp/getting-started/AnomalyDetection_Sales) to read from a local SqlServer database. The problem seems to be in the lazy loading of the data from the database. I'm reading two integers from a table (an ID and Status), and trying to figure out when a change in Status occurs. The example code works when reading from a CSV file.
Do I need to do something to convert the integer being read from the db table to a float? I didn't need to do that when reading from the CSV file.
Using ML.Net 1.5.2
Exception:
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Double' to type 'System.Single'.
at System.Data.SqlClient.SqlBuffer.get_Single()
at System.Data.SqlClient.SqlDataReader.GetFloat(Int32 i)
at Microsoft.ML.Data.DatabaseLoader.Cursor.<>c__DisplayClass37_0.<CreateSingleGetterDelegate>b__0(Single& value)
at Microsoft.ML.Data.TypedCursorable1.TypedRowBase.<>c__DisplayClass10_01.<CreateDirectSetter>b__0(TRow row)
at Microsoft.ML.Data.TypedCursorable1.TypedRowBase.FillValues(TRow row) at Microsoft.ML.Data.TypedCursorable1.TypedCursor.FillValues(TRow row)
at Microsoft.ML.Data.TypedCursorable1.RowCursorImplementation.FillValues(TRow row) at Microsoft.ML.Transforms.StatefulFilterTransform3.Cursor.RunLambda(Boolean& isRowAccepted)
at Microsoft.ML.Transforms.StatefulFilterTransform3.Cursor.MoveNextCore() at Microsoft.ML.Data.RootCursorBase.MoveNext() at Microsoft.ML.Data.SynchronizedCursorBase.MoveNext() at Microsoft.ML.Data.TypedCursorable1.TypedCursor.MoveNext()
at Microsoft.ML.Data.TypedCursorable1.RowCursorImplementation.MoveNext() at Microsoft.ML.PipeEngine1.RunPipe(Boolean reuseRowObject)+MoveNext()
at MLNetDemo.Program.Main(String[] args) in C:\MLNetDemo\Program.cs:line 58
Code: ` public class ConnectionStatus { public long ConnectionId; public float Status; } public class ConnectionStatusPrediction { [VectorType(3)] public double[] Prediction { get; set; } } public class Program { static void Main(string[] args) { int size = 36;
MLContext mlContext = new MLContext();
IDataView dataView = GetDatabaseDataView(mlContext);
IidChangePointEstimator estimator = mlContext.Transforms.DetectIidChangePoint(nameof(ConnectionStatusPrediction.Prediction), nameof(ConnectionStatus.Status), 95, size / 5);
ITransformer tansformedModel = estimator.Fit(CreateEmptyDataView(mlContext));
IDataView transformedData = tansformedModel.Transform(dataView);
IEnumerable<ConnectionStatusPrediction> predictions = mlContext.Data.CreateEnumerable<ConnectionStatusPrediction>(transformedData, reuseRowObject: false);
Console.WriteLine("Alert\tScore\tP-Value");
// code throws exception here, when iterating through the list of predictions
foreach(ConnectionStatusPrediction p in predictions)
{
Console.WriteLine("{0}\t{1:0.00}\t{2:0.00}", p.Prediction[0], p.Prediction[1], p.Prediction[2]);
Console.ResetColor();
}
Console.WriteLine("");
}
private static IDataView CreateEmptyDataView(MLContext mlContext)
{
//Create empty DataView. We just need the schema to call fit()
IEnumerable<ConnectionStatus> enumerableData = new List<ConnectionStatus>();
IDataView dv = mlContext.Data.LoadFromEnumerable(enumerableData);
return dv;
}
private static IDataView GetDatabaseDataView(MLContext mlContext)
{
string connectionStr = "Data Source=localhost; Initial Catalog=connectionstatus;Integrated Security=SSPI;";
string query = "select ConnectionId, cast([Status] as float) as [Status] from connectionstatus";
DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ConnectionStatus>();
DatabaseSource source = new DatabaseSource(SqlClientFactory.Instance, connectionStr, query);
IDataView dataView = loader.Load(source);
return source.ProviderFactory.CreateDataAdapter();
}
}`
Solution for me was to cast to real (maps to single) instead of float (maps to double).
https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings