Ran*_*ger 5 c# linq entity-framework
我正在研究一种计算餐馆之间相似系数的算法.在我们能够计算出所述相似度之前,我需要3位对这两家餐馆进行评分的用户.这是一个以可能的方案为例的表:
| Restaurant 1 | Restaurant 2
User 1 | X | 2
User 2 | 1 | 5
User 3 | 4 | 3
User 4 | 2 | 1
User 5 | X | 5
Run Code Online (Sandbox Code Playgroud)
这里X没有评论,评级是用户对餐厅的评论.您可以看到可以计算相似度,因为用户2,3和4对两个餐馆都进行了评分.
因为我使用调整后的余弦相似度,所以我需要每个用户的平均评分.
现在我正在检索所有餐馆的列表和一个双循环检查是否可以计算餐馆之间的相似性.
我正在使用以下双循环来检查是否可能:
for (int i = 0; i < allRestaurants.Count; i++)
for (int j = 0; j < allRestaurants.Count; j++)
if (i < j)
matrix.Add(new Similarity()
{
Id = Guid.NewGuid(),
FirstRest = allRestaurants[i],
SecondRest = allRestaurants[j],
Sim = ComputeSimilarity(allRestaurants[i], allRestaurants[j], allReviews)
});
Run Code Online (Sandbox Code Playgroud)
在里面ComputeSimilarity我使用以下LINQ语句来检查'匹配'的数量:
public double ComputeSimilarity(Guid restaurant1, Guid restaurant2, IEnumerable<Tuple<List<Review>, double>> allReviews)
{ //The double in the list of allReviews is the average rating of the user.
var matches = (from R1 in allReviews.SelectMany(x => x.Item1).Where(x => x.RestaurantId == subject1)
from R2 in allReviews.SelectMany(x => x.Item1).Where(x => x.RestaurantId == subject2)
where R1.UserId == R2.UserId
select Tuple.Create(R1, R2, allReviews.Where(x => x.Item1.FirstOrDefault().UserId == R1.UserId)
.Select(x => x.Item2)
.FirstOrDefault()))
.DistinctBy(x => x.Item1.UserId);
int amountOfMatches = matches.Count(); //Don't mind this, not looking for performance here at the moment.
if (amountOfMatches < 4)
return 0;
Run Code Online (Sandbox Code Playgroud)
现在你可以看到这种方法非常重要,当你增加double for循环的餐馆数量时需要花费很多时间.
我认为更好的方法是检索所有已满足此要求的餐馆,但我仍然坚持如何做到这一点.我想你可以检索一个'匹配'列表,它将是一个元组列表,如下所示:Tuple<Review, Review, double>.这些评论来自同一用户,而double是来自用户的评论的平均评分.
我一直在尝试多次尝试,但是当我想要添加条件时,我仍然会陷入困境,只需要通过3场比赛检索餐馆.
作为参考,我的评论对象如下所示:
public class Review
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual Guid Id { get; set; }
public virtual int Rating { get; set; }
public virtual Guid RestaurantId { get; set; }
public virtual Guid UserId { get; set; }
//More irrelevant attributes here
}
Run Code Online (Sandbox Code Playgroud)
我的餐厅对象:
public class Restaurant
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual Guid Id { get; set; }
//More irrelevant attributes here
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找比我目前的方法表现更好的东西,是否有人可以指出我正确的方向或建议更好的方法?另外,如果您需要更多信息,请告诉我们!提前致谢!
编辑:第一个示例显示了两家餐馆,但列表当然可以更大.关键在于我只想要能够计算相似度的餐馆.
所以请看以下示例:
| Restaurant 1 | Restaurant 2 | Restaurant 3
User 1 | X | 2 | X
User 2 | 1 | 5 | X
User 3 | 4 | 3 | 3
User 4 | 2 | 1 | 2
User 5 | X | 5 | X
User 6 | X | X | 2
Run Code Online (Sandbox Code Playgroud)
唯一可能的匹配是在餐厅1和餐厅2之间.因为没有足够的匹配(在这种情况下至少3),所以不可能计算相似性.因此,要优化这个方式,是创造的餐馆列表,它是可以计算的相似性.
为了进一步解释,a match是两个用户评价两个餐馆的地方.Restaurant 3有3条评论,但只有2条评论,因为User 6只评了该餐厅.
因此,如果我们将上面的3家餐厅作为输入,它应该只创建一个可以计算相似性的餐馆列表(在这种情况下只有餐馆1和2).
编辑2:我将添加一个示例,我希望的输出应该如何:
"匹配"是至少3个用户评价相同的2家餐馆的地方.所以假设我们有餐厅X和Y,输出可能如下所示:
| Restaurant X | Restaurant Y
User 1 | 5 | 3
User 2 | 2 | 5
User 3 | 1 | 2
Run Code Online (Sandbox Code Playgroud)
现在,如果我们在列表中添加第三家餐厅,每个用户也已经审核过:
| Restaurant X | Restaurant Y | Restaurant Z
User 1 | 5 | 3 | 2
User 2 | 2 | 5 | 3
User 3 | 1 | 2 | 1
Run Code Online (Sandbox Code Playgroud)
现在您可以看到可以在这里生成每个餐厅之间的相似性.X和Y,X和Z,Y和Z之间的相似性.
这可以在一个单独的类中建模,如下所示:
public class Match
{
public Review rev1 { get; set; } //These two reviews have been left by the same users, on separate restaurants.
public Review rev2 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
如果我们有3个这样的匹配,其中每个对象RestaurantId与RestaurantIdrev1 相同,而rev2 则相同.
所以这些匹配的列表可能如下所示:
rev1.RestaurantId = 1| rev2.RestaurantId = 2| UserId = 11此用户ID在rev1和rev2上是相同的rev1.RestaurantId = 1| rev2.RestaurantId = 2| UserId = 12此用户ID在rev1和rev2上是相同的rev1.RestaurantId = 1| rev2.RestaurantId = 2| UserId = 13此用户ID在rev1和rev2上是相同的我知道id是guids,但这仅仅是一个例子.
我希望这是有道理的..
我想我已经完成了你想要实现的目标。
我已经在您的帖子中使用评论表构建了一个数据库,并且放置了与您在 Edit 中向我们展示的表相同的数据。
步骤1
因此,我首先按 RestaurantId 进行分组,并将值作为对这家餐厅进行评分的用户的所有评论的列表。
它给了我们这个:
第2步
排除评论少于 2 条餐厅的用户的评论少于 3 条的餐厅。
它给了我们这个:
步骤3
我们现在有了正确的餐厅列表,但我们需要排除不匹配的用户评论。然后将所有这些扁平化,只剩下餐厅和评论。
它给了我们这个,这是最终结果:
这是代码:
var matches = this.Reviews.GroupBy(r => r.RestaurantId, r => this.Reviews.Where(rr => rr.UserId == r.UserId))
.ToList()
.Where(g => g.Where(gg => gg.Count() >= 2).Count() >= 3);
var matchingReviewsByRestaurant = matches.ToDictionary(m => m.Key, m => m.Where(g => g.Count() >= 2).SelectMany(g => g));
Run Code Online (Sandbox Code Playgroud)
我希望这是你想要的!
最终答案,这就是您想要的,用户的评论对。
// Step 1 : Get the right reviews
var matches = this.Reviews.GroupBy(r => r.RestaurantId, r => this.Reviews.Where(rr => rr.UserId == r.UserId)).ToList()
.Where(g => g.Where(gg => gg.Count() >= 2).Count() >= 3);
var matchingReviewsByRestaurant = matches.ToDictionary(m => m.Key, m => m.Where(g => g.Count() >= 2).SelectMany(g => g));
// Step 2 : Create the matching couples
var reviewsByUsers = matchingReviewsByRestaurant.SelectMany(m => m.Value).Distinct().ToLookup(r => r.UserId);
var matchingReviewsCouples = new List<Match>();
foreach (var reviews in reviewsByUsers)
{
var combinations = reviews.SelectMany(x => reviews, (x, y) => new Match(x, y))
.Where(m => m.Review1.Id.CompareTo(m.Review2.Id) > 0)
.ToList();
matchingReviewsCouples.AddRange(combinations);
}
// Final Results are in matchingReviewsCouples
Run Code Online (Sandbox Code Playgroud)
根据我的示例的数据,结果如下:
| 归档时间: |
|
| 查看次数: |
206 次 |
| 最近记录: |