Microsoft.ML Adding Label when using LoadFromEnumerable - c#

There is something that is not working, I cannot figured out...
I want to load data from a List. I have try several codes
MLContext mlContext = new MLContext();
var listData = new List<ProductEntry>
{
new ProductEntry {CoPurchaseProductID = 12, ProductID = 4},
new ProductEntry {CoPurchaseProductID = 12, ProductID = 3},
new ProductEntry {CoPurchaseProductID = 11, ProductID = 5},
new ProductEntry {CoPurchaseProductID = 11, ProductID = 3}
};
var data = mlContext.Data.LoadFromEnumerable(listData);
var options = new MatrixFactorizationTrainer.Options
{
MatrixColumnIndexColumnName = nameof(ProductEntry.ProductID),
MatrixRowIndexColumnName = nameof(ProductEntry.CoPurchaseProductID),
LabelColumnName = "Label",
LossFunction = MatrixFactorizationTrainer.LossFunctionType.SquareLossOneClass,
Alpha = 0.01,
Lambda = 0.025,
Quiet = false,
C = 0.00001,
ApproximationRank = 10,
NumberOfIterations = 20
};
var est = mlContext.Recommendation().Trainers.MatrixFactorization(options);
ITransformer model = est.Fit(data);
var predictionengine = mlContext.Model.CreatePredictionEngine<ProductEntry, Copurchase_prediction>(model);
Console.WriteLine("Calculating the top 3 products for product 3...");
var top5 = (from m in Enumerable.Range(1, 262111)
let p = predictionengine.Predict(
new ProductEntry()
{
ProductID = 3,
CoPurchaseProductID = (uint)m
})
orderby p.Score descending
select (ProductID: m, Score: p.Score)).Take(10);
foreach (var t in top5)
Console.WriteLine($" Score:{t.Score}\tProduct: {t.ProductID}");
Console.ReadKey();
I am getting a problem about a "Label"
This is the classes:
public class Copurchase_prediction
{
public float Score { get; set; }
}
public class ProductEntry
{
[KeyType(count: 262111)]
public float Label { get; set; }
[KeyType(count: 262111)]
public uint ProductID { get; set; }
[KeyType(count: 262111)]
public uint CoPurchaseProductID { get; set; }
}
Getting the error:
System.ArgumentOutOfRangeException: 'Member Label marked with KeyType attribute, but does not appear to be a valid kind of data for a key type (Parameter 'userType')'
Any idea?
Thanks guys!

Remove the [KeyType(count: 262111)] annotation for public float Label { get; set; } property
Label is not a key

Related

How do I update my property in my list based on the closest key in a list

I have 2 lists: list and listLookup
How do I update all the ValueToGet in list with closes KeyLookup in listLookup?
https://dotnetfiddle.net/QHd0Rr
using System.Collections.Generic;
public class Program
{
public static void Main()
{
//Arrange
//Keys needed to get ValueToGet property
var key1 = new WidgetA{Id = 1, Key = 52};
var key2 = new WidgetA{Id = 2, Key = 102};
var key3 = new WidgetA{Id = 3, Key = 152};
List<WidgetA> list = new List<WidgetA>();
list.Add(key1);
list.Add(key2);
list.Add(key3);
//Lookups
var keyLookup1 = new WidgetB()
{Id = 1, KeyLookup = 50, ValueLookup = "Fifty"};
var keyLookup2 = new WidgetB()
{Id = 2, KeyLookup = 100, ValueLookup = "One hundred"};
var keyLookup3 = new WidgetB()
{Id = 3, KeyLookup = 150, ValueLookup = "One hundred and fifty"};
List<WidgetB> listLookup = new List<WidgetB>();
listLookup.Add(keyLookup1);
listLookup.Add(keyLookup2);
listLookup.Add(keyLookup3);
//Act
/* Update all rows for ValueToGet property in list, using the closes KeyLookup in listLookup
Expected result:
key1: Key = 52, ValueToGet = "Fifty"
key2: Key = 102, ValueToGet = “One hundred”
key3: Key = 152, ValueToGet = “One hundred and fifty”
*/
}
}
public class WidgetA
{
public int Id { get; set; }
public int Key { get; set; }
public string ValueToGet { get; set; }
}
public class WidgetB
{
public int Id { get; set; }
public int KeyLookup { get; set; }
public string ValueLookup { get; set; }
}
In SQL it would be kind of like this but finding the closest key somehow:
update list
set ValueToGet = ValueLookup
from list l1
join listLookup l2
on l1.key = l2.keyLookup
If we define closest as:
Take the KeyLookup that is lower than the current Key
If there is no KeyLookup lower than the current key, take the closest higher one
Than this would solve your problem
private static void AddValueLookup(IEnumerable<WidgetA> list, IEnumerable<WidgetB> listLookup)
{
// Ensure the collection is ordered
using var bENumerator = listLookup.OrderBy(b => b.KeyLookup).GetEnumerator();
// Get the first two values.
// If the collection does not have at least one value, return.
if(!bENumerator.MoveNext())
return;
var currentKey = bENumerator.Current;
bENumerator.MoveNext();
var nextKey = bENumerator.Current;
// Ensure the collection is ordered.
// If the list contains a key that is smaller, than the smallest one in the lookup list,
// it will get the next higher ValueLookup
foreach (var a in list.OrderBy(a => a.Key))
{
while (a.Key > nextKey.KeyLookup)
{
currentKey = nextKey;
if (bENumerator.MoveNext())
nextKey = bENumerator.Current;
else
break;
}
a.ValueLookup = currentKey.ValueLookup;
}
}

Copy add or combine a model

I am having problem adding to the model. I want to have a list in the Root1 or in the Viewmodel like fullname and authors list.
newModel1
AuthorsAccepted count=4
FullName = Jack
AuthorsAccepted count=4
FullName = Time Dean
With the code below it only adds the last one to the Root1. I created a a third model. Is there a way I can add to the third model both the lists or in Root1.
var fullName = "Jack";
var fullName2 = "Tim Dean";
var authors1 = new List<AuthorsAccepted1>() {
new AuthorsAccepted1(){ id = 1, Name="Bill"},
new AuthorsAccepted1(){ id = 2, Name="Steve"},
new AuthorsAccepted1(){ id = 3, Name="jon"},
new AuthorsAccepted1(){ id = 4, Name="nick"}
};
var authors2 = new List<AuthorsAccepted1>() {
new AuthorsAccepted1(){ id = 1, Name="jack"},
new AuthorsAccepted1(){ id = 2, Name="tim"},
new AuthorsAccepted1(){ id = 3, Name="james"},
new AuthorsAccepted1(){ id = 4, Name="mary"}
};
var newModel1 = new Root1();
newModel1.FullName = fullName;
newModel1.AuthorsAccepted = authors1;
var newModel2 = new Root1();
newModel2.FullName = fullName2;
newModel2.AuthorsAccepted = authors2;
}
}
public class Root1
{
public string FullName { get; set; }
public List<AuthorsAccepted1> AuthorsAccepted { get; set; }
}
public class AuthorsAccepted1
{
public string Name { get; set; }
public int id { get; set; }
}
public class Viewmodel
{
public Root1 AllModel { get; set; }
}

How to aggregate data, pass and fail as final result?

I have a list of result either having or not having Data plus has Pass and Fail like below,
var results = new List<Result>
{
new Result{Data = new Data{Name = "A"}, Pass = 1, Fail = 0},
new Result{Data = new Data{Name = "B"}, Pass = 3, Fail = 1},
new Result{Pass = 1, Fail = 0}
};
I need to aggregate data and need this as final result output,
var finalResult = new FinalResult
{
Datas = new List<Data> { new Data { Name = "A" }, new Data { Name = "B" } },
TotalPass = 5,
TotalFail = 1,
Divident = 5/1
}
I tried something like below, but totalPass and totalfail, are not coming correct. Plus, how to aggregate Data?
int totalPass = 0;
int totalfail = 0;
var finalResult = new FinalResult();
foreach (var r in results)
{
totalPass += r.Pass;
totalfail += r.Fail;
}
finalResult.TotalFail = totalPass;
finalResult.TotalFail = totalfail;
finalResult.Divident = totalPass / totalfail;
Here are the two classes:
public class FinalResult
{
public List<Data> Datas { get; set; }
public int TotalPass { get; set; }
public int TotalFail { get; set; }
public int Divident { get; set; }
}
public class Result
{
public Data Data { get; set; }
public int Pass { get; set; }
public int Fail { get; set; }
}
public class Data
{
public string Name { get; set; }
}
You can achieve this easily using LINQ:
var finalResult = new FinalResult
{
Datas = results.Where(r => r.Data != null).Select(r => r.Data).ToList(),
TotalPass = results.Sum(r => r.Pass),
TotalFail = results.Sum(r => r.Fail)
};
// Beware of division by zero and integer division.
finalResult.Divident = finalResult.TotalPass / finalResult.TotalFail;
Notes:
You should probably check the value of TotalFail before the division to prevent division by zero.
As Groo mentioned in the comments, Divident should probably be declared as double and you should cast one of the parts to double unless you do want Integer Division.

Convert Single Object into Multiple Object c#

I have a list as below
I want to convert this into a list which looks like this
Can anybody suggest some sample code?
I have tried the bellow code
class Program
{
static void Main(string[] args)
{
var mockForDataColection = Mock.MockForDataColection();
Factory f = new Factory();
List<ResultSet> rl = new List<ResultSet>();
var groups = mockForDataColection.GroupBy(x => new { group1 = x.GroupIdOne, group2 = x.GroupIdTwo, group3 = x.GroupIdThree }).ToList();
f.Name = mockForDataColection.FirstOrDefault(c => c.Key == "Chamber").Value;
f.RunNumber = mockForDataColection.FirstOrDefault(c => c.Key == "RunNumber").Value;
var groupBy = mockForDataColection.GroupBy(c => c.GroupIdThree).Skip(1);
foreach (var dataCollection in groupBy)
{
ResultSet r = new ResultSet();
r.BarCode = dataCollection.FirstOrDefault(d => d.Key == "BarCode").Value;
r.IsPassed = dataCollection.FirstOrDefault(d => d.Key == "IsPassed").Value;
rl.Add(r);
}
f.ResultSet = rl;
}
}
public class DataCollection
{
public int GroupIdOne { get; set; }
public int GroupIdTwo { get; set; }
public int GroupIdThree { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
public class Factory
{
public string Name { get; set; }
public string RunNumber { get; set; }
public List<ResultSet> ResultSet { get; set; }
}
public class ResultSet
{
public string BarCode { get; set; }
public string IsPassed { get; set; }
public int FailiureCode { get; set; }
}
public static class Mock
{
public static List<DataCollection> MockForDataColection()
{
List<DataCollection> dataCollectionList = new List<DataCollection>();
DataCollection dataCollection = new DataCollection
{
GroupIdOne = 1,
GroupIdTwo = 1,
GroupIdThree = 0,
Key = "Chamber",
Value = "Test Chamber"
};
DataCollection dataCollection1 = new DataCollection
{
GroupIdOne = 1,
GroupIdTwo = 1,
GroupIdThree = 0,
Key = "RunNumber",
Value = "2"
};
DataCollection dataCollection2 = new DataCollection
{
GroupIdOne = 1,
GroupIdTwo = 2,
GroupIdThree = 3,
Key = "IsPassed",
Value = "P"
};
DataCollection dataCollection3 = new DataCollection
{
GroupIdOne = 1,
GroupIdTwo = 2,
GroupIdThree = 3,
Key = "BarCode",
Value = "PQWERT"
};
DataCollection dataCollection4 = new DataCollection
{
GroupIdOne = 1,
GroupIdTwo = 2,
GroupIdThree = 4,
Key = "IsPassed",
Value = "F"
};
DataCollection dataCollection5 = new DataCollection
{
GroupIdOne = 1,
GroupIdTwo = 2,
GroupIdThree = 4,
Key = "BarCode",
Value = "IUTYY"
};
dataCollectionList.Add(dataCollection);
dataCollectionList.Add(dataCollection1);
dataCollectionList.Add(dataCollection2);
dataCollectionList.Add(dataCollection3);
dataCollectionList.Add(dataCollection4);
dataCollectionList.Add(dataCollection5);
return dataCollectionList;
}
}
I think it's not a good code. Please suggest some other easy way to achieve this one.
Need to convert the data collection class into Factory class. In data, collection class has Key and Value Properties. Key is act as a property in Factory class Value is Value. Based on the GroupId Only we will decide it will go to Parent class (Factory) or child class (ResultSet).
GroupId One value is same means it all under a single entity. GroupId Two value differs means it is a related entity.
I have completely edited the answer, changed everything so that it is right and according to what was asked.
listA, your data to be grouped
var listA = new List<DataCollection>
{
new DataCollection { GroupIdOne = 1, GroupIdTwo = 1, GroupIdThree = 0, Key = "Chamber", Value = "Test Chamber" },
new DataCollection { GroupIdOne = 1, GroupIdTwo = 1, GroupIdThree = 0, Key = "RunNumber", Value = "2" },
new DataCollection { GroupIdOne = 1, GroupIdTwo = 2, GroupIdThree = 3, Key = "IsPassed", Value = "P" },
new DataCollection { GroupIdOne = 1, GroupIdTwo = 2, GroupIdThree = 3, Key = "BarCode", Value = "PQWERT" },
new DataCollection { GroupIdOne = 1, GroupIdTwo = 2, GroupIdThree = 4, Key = "IsPassed", Value = "F"},
new DataCollection { GroupIdOne = 1, GroupIdTwo = 2, GroupIdThree = 4, Key = "BarCode", Value = "IUTYY"}
};
listB
var listB = listA
.Where(x => x.Key.Contains("BarCode"))
.GroupBy(g => new {g.GroupIdOne, g.GroupIdTwo, g.GroupIdThree, g.Key, g.Value})
.Select(s =>
new ResultSet
{
BarCode = s.Key.Value,
IsPassed = listA.FirstOrDefault(a => a.Key.Equals("IsPassed")
&& a.GroupIdOne == s.Key.GroupIdOne
&& a.GroupIdTwo == s.Key.GroupIdTwo
&& a.GroupIdThree == s.Key.GroupIdThree
)?.Value ?? ""
})
.ToList();

How to combine List items

I've a class like this:
public class ReportList
{
public int? ProjectId { get; set; }
public string Name { get; set; }
public string ProjectName { get; set; }
public int LevelId { get; set; }
public int Minutes { get; set; }
public int Hours { get; set; }
public int ExtraMinutes { get; set; }
public int ExtraHours { get; set; }
}
And I've list of this class
List<ReportList> repList = new List<ReportList>();
I've added items to this list:
repList.Add(new ReportList(1 , "a" , "project a", 2, 30, 1, 45, 2));
repList.Add(new ReportList(1 , "b" , "project a", 2, 30, 2, 15, 1));
repList.Add(new ReportList(1 , "c" , "project a", 2, 0, 3, 10, 0));
I want to combine this list items into one item by sum minutes and hours. So the list should be like this:
{1, "a", "project a", 2, 60, 6, 70, 3};
What can I do?
Use GroupBy extension method on ProjectId,ProjectName and LevelId fields.
var results = repList.GroupBy(x=> new {x.ProjectId, x.ProjectName, LevelId })
.Select(x=> new // or create new ReportList object.
{
ProjectId = x.Key.ProjectId,
ProjectName = x.Key.ProjectName,
Name = x.First().Name, // I assume it is first one as per example, modify if you want.
LevelId = x.Key.LevelId,
Minutes = x.Sum(s=>s.Minutes),
Hours = x.Sum(s=>s.Hours ),
ExtraMinutes = x.Sum(s=>s.ExtraMinutes ),
ExtraHours = x.Sum(s=>s.ExtraHours)
})
.ToList() ;
If you want more optimized version of answer posted by user Hari Prasad you could use following;
int minuteSum = 0;
int hoursSum = 0;
int extraMinutesSum = 0;
int extraHoursSum = 0;
foreach (var report in repList)
{
minuteSum += report.Minutes;
hoursSum += report.Hours;
extraMinutesSum += report.ExtraMinutes;
extraHoursSum += report.ExtraHours;
}
var firstItemInRepList = repList.First();
var result = new ReportList(firstItemInRepList.ProjectId,
firstItemInRepList.Name,
firstItemInRepList.ProjectName,
firstItemInRepList.LevelId,
minuteSum,
hoursSum,
extraMinutesSum,
extraHoursSum);
I know its more crude version but it will take less cpu.
var results = repList
.GroupBy(x => "all")
.Select(x=> new {
ProjectId = x.First().ProjectId,
Name = x.First().Name,
ProjectName = x.First().ProjectName,
LevelId = x.First().LevelId,
Minutes = x.Sum(s=>s.Minutes),
Hours = x.Sum(s=>s.Hours ),
ExtraMinutes = x.Sum(s=>s.ExtraMinutes),
ExtraHours = x.Sum(s=>s.ExtraHours)
});
I am refering answer posted by user Hari Prasad, but as per question requirement we need to apply groupby only on ProjectId i guess.
Please refer below code.
var processedResult = repList.GroupBy(x => x.ProjectId)
.Select(x => new ReportList
{
ProjectId = x.Key,
ProjectName = x.First().ProjectName, //As per your example it is first row data
Name = x.First().Name, //As per your example it is first row data
LevelId = x.First().LevelId,
Minutes = x.Sum(s => s.Minutes),
Hours = x.Sum(s => s.Hours),
ExtraMinutes = x.Sum(s => s.ExtraMinutes),
ExtraHours = x.Sum(s => s.ExtraHours)
}).ToList();

Categories

Resources