如何查找跨List>对于每个内部值

本文关键字:List 何查找 于每个 内部 double 查找 | 更新日期: 2023-09-27 18:16:21

这与我之前问的另一个问题非常相似。我不知道如何在Linq做事情,所以我需要一些帮助。我想找到List>中每个内部值的模态值。

我有以下列表:

    List<List<double>> myFullList = new List<List<double>>();
    for(int i = 1; i <= numberOfLoops; i++)
    { 
       List<double> myInnerList = new List<double>();
        for(int i = 1; i <= 10; i++)
        {
           // Populate inner list with random numbers
           myInnerList.Add(double myRandomNumber);
        }
    // Add the inner list to the full list
    myFullList.Add(myInnerList);
   }

列表应该是这样的:

myFullList[0] = {rand#1,rand#2,rand#3,...,rand#10}
myFulllist[1] = {rand#1,rand#2,rand#3,...,rand#10}
.
.
.
.
myFulllist[1] = {rand#1,rand#2,rand#3,...,rand#10}

我需要找到MODAL值的数据,形成一个单一的列表,看起来像这样:

List<double> mode= new List<double>();
mode= {mode#1, mode#2........mode#10}

该输出变量将查找内部列表中同一"行"数据的数据模式。

简单的例子:

innerList[0] = {1.00,2.00,3.00};
innerList[1] = {3.00,2.00,8.00};    
innerList[2] = {3.00,9.00,1.00};
innerList[3] = {3.00,1.00,1};
fullList = {innerList[0], innerList[1], innerList[2], innerList[3]};
modeList = {3,2,1};

如何查找跨List<List<double>>对于每个内部值

不是最优雅的方式,但可能更容易理解。它已被成功测试:)

class Program
{
    static void Main(string[] args)
    {
        Random rnd = new Random();
        int numberOfLoops = 10;
        List<List<int>> myFullList = new List<List<int>>();
        for (int i = 0; i < numberOfLoops; i++)
        {
            List<int> myInnerList = new List<int>();
            for (int j = 0; j < 10; j++)
            {
                // Populate inner list with random numbers
                myInnerList.Add(rnd.Next(0, 10));
            }
            // Add the inner list to the full list
            myFullList.Add(myInnerList);
        }

        myFullList = Transpose<int>(myFullList);
        List<int> result = new List<int>();
        foreach (List<int> subList in myFullList)
            result.Add(Mode(subList));
        //TO-DO: linq version!
        //List<int> result = myFullList.ForEach(num => Mode(num));
    }
    public static int Mode(List<int> x)
    {
        int mode = x.GroupBy(v => v)
            .OrderByDescending(g => g.Count())
            .First()
            .Key;
        return mode;
    }
    public static List<List<T>> Transpose<T>(List<List<T>> lists)
    {
        var longest = lists.Any() ? lists.Max(l => l.Count) : 0;
        List<List<T>> outer = new List<List<T>>(longest);
        for (int i = 0; i < longest; i++)
            outer.Add(new List<T>(lists.Count));
        for (int j = 0; j < lists.Count; j++)
            for (int i = 0; i < longest; i++)
                outer[i].Add(lists[j].Count > i ? lists[j][i] : default(T));
        return outer;
    }
}

这很简单,下面是代码(抱歉,还没有完全测试过,但很好开始):

public static class ModalHelper
    {
        public static List<double> GetModals(List<List<double>> source)
        {
            return source.Select(list => list.Sum()/list.Count).ToList();
        }
    }

这个linq查询应该能达到这个目的

var result = list.Select<List<double>, List<KeyValuePair<int, double>>>(sub =>
{
    List<KeyValuePair<int, double>> elems = new List<KeyValuePair<int, double>>(sub.Count);
    for (int i = 0; i < sub.Count; ++i)
        elems.Add(new KeyValuePair<int, double>(i, sub[i]));
    return elems;
}).SelectMany((x) => x).GroupBy((x) => x.Key).Select<IGrouping<int, KeyValuePair<int, double>>, double>(x =>
{
    var y = x.GroupBy(g => g.Value).OrderByDescending(g => g.Count());
    return y.First().First().Value;
});

下面是一个例子:

static void Main(string[] args)
{
    List<List<double>> list = new List<List<double>>();
    list.Add(new List<double> { 1.00, 2.00, 3.00 });
    list.Add(new List<double> { 3.00, 2.00, 8.00 });
    list.Add(new List<double> { 3.00, 9.00, 1.00 });
    list.Add(new List<double> { 3.00, 1.00, 1 });
    var result = list.Select<List<double>, List<KeyValuePair<int, double>>>(sub =>
    {
        List<KeyValuePair<int, double>> elems = new List<KeyValuePair<int, double>>(sub.Count);
        for (int i = 0; i < sub.Count; ++i)
            elems.Add(new KeyValuePair<int, double>(i, sub[i]));
        return elems;
    }).SelectMany((x) => x).GroupBy((x) => x.Key).Select<IGrouping<int, KeyValuePair<int, double>>, double>(x =>
    {
        var y = x.GroupBy(g => g.Value).OrderByDescending(g => g.Count());
        return y.First().First().Value;
    });
    foreach (double val in result)
        Console.Write(val + " ");
    Console.WriteLine();
}

这里是ideone的实时版本:http://ideone.com/ye2EhG

首先将列表转换为键值对列表,键值对在每个列表中添加索引信息。然后这些列表被平摊成一个单独的列表,然后这个新列表按索引分组。这些组按照值的计数排序,并为每个组返回最频繁的元素。

像这样应该给出模式:

var temp = myFullList.SelectMany(l => l).GroupBy(all => all).Select(result => new
{
    Value = result.Key,
    Count = result.Count()
}).OrderByDescending(t => t.Count);

解释:来自MSDN - SelectMany

将序列中的每个元素投影到IEnumerable中,并平坦化将结果序列合并为一个序列。

它给出了子列表中的每一个小数。然后,我们按小数本身分组,并选择每个小数的计数及其值。最后,我们按计数排序,首先给出出现频率最高的小数。

根据Robert S的评论编辑

上面的代码似乎不是需要的。正如Robert S指出的那样,代码给出了List<List<double>>中所有数字的模式,但问题是如何从每个获得模式。

以下代码应该给出每个列的模式。注意,这段代码忽略了重复项;如果多个数字出现的次数相同,则给出第一个数字:

var result1 = myFullList[0].Select((l, i) => new
{
    Column = i,
    Mode = myFullList.GroupBy(fl => fl[i]).OrderByDescending(t => t.Count()).Select(t => t.Key).FirstOrDefault()
});
foreach (var item in result1)
{
    Console.WriteLine(string.Format("{0} {1}", item.Column, item.Mode));
}

代码使用Select的重载来获取元素的索引(OP定义中的列)。然后对该索引处的每个项进行分组。注意在myFullList上没有边界检查,但是在生产代码中应该有。

如果是重复的问题,我们需要两个步骤:

var temp2 = myFullList[0].Select((l, i) => new
{
    Column = i,
    Mode = myFullList.GroupBy(fl => fl[i]).Select(t => new { Number = t.Key, Count = t.Count() }).OrderByDescending(a => a.Count)
});
var result2 = temp2.Select(t => new
{
    Column = t.Column,
    Mode = t.Mode.Where(m => m.Count == t.Mode.Max(tm => tm.Count))
});
foreach (var item in result2)
{
    for (int i = 0; i < item.Mode.Count(); i++)
    {
        Console.WriteLine(string.Format("{0} {1}", item.Column, item.Mode.ElementAt(i)));
    }
}

在上面的代码中,temp2.Mode将包含一个匿名对象的IEnumerable,该对象包含数字和该数字出现的次数。然后通过抓取那些计数与计数最大值相匹配的条目来填充result2

给定输入:

myFullList.Add(new List<double> { 1.00, 2.00, 3.00 });
myFullList.Add(new List<double> { 3.00, 2.00, 3.00 });
myFullList.Add(new List<double> { 3.00, 9.00, 1.00 });
myFullList.Add(new List<double> { 3.00, 1.00, 1 });
第一个代码输出
0 3
1 2
2 3

第二个输出

0 3
1 2
2 3
2 1 

注意,第2列有两个输出,因为3和1同样流行。