我有一个如下所示的数据集:
[
{
"Size" : "Small",
"Details" :
{
"Detail 1" : 1.0,
"Detail 1" : 1.0,
"Detail 2" : 1.0,
}
},
{
"Size" : "Small",
"Details" :
{
"Detail 1" : 2.0,
"Detail 1" : 3.0,
"Detail 2" : 4.0,
}
},
{
"Size" : "Medium",
"Details" :
{
"Detail 1" : 1.0,
"Detail 1" : 1.0,
"Detail 2" : 1.0,
"Detail 3" : 1.0,
}
},
//... etc
]
对于具有相同“大小”的所有项目,我想单独总结匹配的“详细信息”条目,然后将它们平均为“大小”d项。即:
[
{
"Size" : "Small",
"Details" :
{
"Detail 1" : 3.5,
"Detail 2" : 2.5, // Average of the summation of the two 'small' items in original data set
},
{
"Size" : "Medium",
"Details" :
{
"Detail 1" : 2.0, // Average of the two details for medium.
"Detail 2" : 1.0,
"Detail 3" : 1.0,
}
},
]
我得到的代码是这样的,但我一直在搞清楚如何在嵌套集中求平均值。任何指针都会受到赞赏。
我的代码,到目前为止。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test
{
class ItemWithDetails
{
public string Size;
public List<KeyValuePair<string, double>> Details { get; private set; }
public ItemWithDetails(string size)
{
Size = size;
Details.Add(new KeyValuePair<string, double>("Detail 1", 1));
Details.Add(new KeyValuePair<string, double>("Detail 1", 1));
Details.Add(new KeyValuePair<string, double>("Detail 2", 1));
Details.Add(new KeyValuePair<string, double>("Detail 2", 1));
Details.Add(new KeyValuePair<string, double>("Detail 2", 1));
Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
if (size == "Large")
{
Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
}
}
}
class Program
{
static void Main(string[] args)
{
var testData = new List<ItemWithDetails>()
{
new ItemWithDetails("Small"),
new ItemWithDetails("Small"),
new ItemWithDetails("Medium"),
new ItemWithDetails("Medium"),
new ItemWithDetails("Medium"),
new ItemWithDetails("Large"),
new ItemWithDetails("Large"),
new ItemWithDetails("Large"),
};
// Trying to get the average of each detail, per size.
var detailSummed = from item in testData
select new
{
size = item.Size,
detailsSummed = from detail in item.Details
group detail by detail.Key into detailGroup
select new
{
detailName = detailGroup.Key,
detailSum = detailGroup.Sum(a => (a.Value))
}
};
var averageAcrossItems = from item in detailSummed
group item by item.size into itemGroup
select new
{
size = itemGroup.Key,
detailsAveraged = // not sure how I can average across items, while at this level. Do I have to flatten it?
}
}
}
}
更新:
使用Adam Mills代码,我已经接近了,结合了两个单独的LINQ查询。这可以成为一个希望更具可读性的LINQ查询吗?
var detailSummed = from item in testData
select new
{
size = item.Size,
detailsSummed = from detail in item.Details
group detail by detail.Key into detailGroup
select new
{
detailName = detailGroup.Key,
detailSum = detailGroup.Sum(a => (a.Value))
}
};
var test2 = detailSummed.GroupBy(x => x.size)
.Select(y =>
new
{
Size = y.Key,
DetailAverages = y .SelectMany(x => x.detailsSummed)
.GroupBy(x => x.detailName)
.Select(x => new KeyValuePair<string, double>(x.Key, x.Average(c => c.detailSum)))
});
答案 0 :(得分:2)
items.GroupBy(x => x.Size)
.Select(y =>
new
{
Size = y.Key,
Details = y.SelectMany(x => x.Details)
.GroupBy(x => x.Key)
.Select(x => new
{
Key = x.Key,
Average = x.Average(c => c.Value),
Sum = x.Sum(c => c.Value)
})
});
答案 1 :(得分:1)
以下内容会根据您的输入生成所需的输出,但它会将detail1
的两个medium
值相加:
var output = input
.Select(iwd => new { Size = iwd.Size, Sums = iwd.Details.GroupBy(kvp => kvp.Key).Select(g => new { Detail = g.Key, Sum = g.Sum(kvp => kvp.Value) }) })
.GroupBy(ds => ds.Size)
.ToDictionary(g => g.Key, g => g.SelectMany(ds => ds.Sums).GroupBy(ds => ds.Detail).ToDictionary(dsg => dsg.Key, dsg => dsg.Sum(ds => ds.Sum) / g.Count()));
请注意,它会产生Dictionary<string, Dictionary<string, int>>
。
答案 2 :(得分:1)
这个怎么样,
首先进行一些设置:
class ItemWithDetails {
public string Size;
public List<KeyValuePair<string, double>> Details { get; private set; }
public ItemWithDetails() {
Details=new List<KeyValuePair<string,double>>();
}
}
要初始化的样本数据;
var testData = new ItemWithDetails[] {
new ItemWithDetails { Size = "Small",
Details = {
new KeyValuePair<string,double>("Detail 1",1.0),
new KeyValuePair<string,double>("Detail 1",1.0),
new KeyValuePair<string,double>("Detail 2",1.0),
}
},
new ItemWithDetails { Size="Small",
Details = {
new KeyValuePair<string,double>("Detail 1",2.0),
new KeyValuePair<string,double>("Detail 1",3.0),
new KeyValuePair<string,double>("Detail 2",4.0),
}
},
new ItemWithDetails { Size="Medium",
Details = {
new KeyValuePair<string,double>("Detail 1",1.0),
new KeyValuePair<string,double>("Detail 1",1.0),
new KeyValuePair<string,double>("Detail 2",1.0),
new KeyValuePair<string,double>("Detail 3",1.0),
}
},
};
现在这个查询应该以你想要的方式转换数据;
var q = from i in testData
select new {
Size=i.Size,
Details=i.Details
.GroupBy(d =>d.Key)
.Select(d=>new KeyValuePair<string,double>(d.Key,d.Sum(a=>a.Value)))
} into x
group x by x.Size into g
let details = (
from a in g.SelectMany (b => b.Details)
group a by a.Key into g2
select new KeyValuePair<string,double>(g2.Key,g2.Average(b=>b.Value))
)
select new {
Size=g.Key,
Details=details
};
更具可读性,只是在答案中使用匿名类型而不是kvp:
var q = from i in testData
select new {
Size=i.Size,
Details= (
from d in i.Details
group d by d.Key into g1
select new {Key=g1.Key,Value=g1.Sum(a=>a.Value)}
)
} into x
group x by x.Size into g
select new {
Size=g.Key,
Details=(
from a in g.SelectMany (b => b.Details)
group a by a.Key into g2
select new {Key =g2.Key, Value= g2.Average(b=>b.Value)}
)
};
答案 3 :(得分:0)
试试这个:
var x =
from item in testData
group item by item.Size into sizeItems
select new
{
Size = sizeItems.Key,
Details =
from item in sizeItems
from detail in item.Details
group detail by detail.Key into detailNumbers
select new KeyValuePair<string, double>(detailNumbers.Key, detailNumbers.Sum(dn => dn.Value)) into detail
group detail by detail.Key into detailNumbers
select new KeyValuePair<string, double>(detailNumbers.Key, detailNumbers.Average(dn => dn.Value))
};
答案 4 :(得分:0)
var detailSummed = testData
.SelectMany(d => d.Details.GroupBy(de => de.Key).Select(de => new { Size = d.Size, DetailSize = de.Key, Sum = de.Sum(x => x.Value) } ))
.GroupBy (d => d.Size)
.Select (d =>
new
{
Size = d.Key,
Details = d.GroupBy (x => x.DetailSize)
.Select(x =>
new KeyValuePair<string, double>(x.Key, x.Average(xi => xi.Sum)))
.ToList()
});
让我们打破这个:
d.Details.GroupBy(de => de.Key).Select(de => new { Size = d.Size, DetailSize = de.Key, Sum = de.Sum(x => x.Value) } )
我们从测试数据开始,并将细节分组为Size, DetailSize, Sum
匿名类型。这样我们就可以区分不同的ItemWithDetails
。
.SelectMany(d => ...
这允许我们从列表列表(详细信息)转到我们汇总值的展平列表。
.GroupBy (d => d.Size)
这将按主要项目大小分组。此时,这会将我们的汇总数据分组到属于主要大小的所有详细信息中。我们将对这组分组数据执行选择。
.Select (d =>
new
{
Size = d.Key,
Details = d.GroupBy (x => x.DetailSize)
.Select(x => new KeyValuePair<string, double>(x.Key, x.Average(xi => xi.Sum)))
.ToList()
});
最后一种方法将我们的汇总值转换为各自的DetailSize,其中KeyValuePair
s列表中的平均值。