如何对一个集合进行分组并将相同的分组应用于其他相同长度的集合?

Posted

技术标签:

【中文标题】如何对一个集合进行分组并将相同的分组应用于其他相同长度的集合?【英文标题】:How to group one collection and apply same grouping to other, same-length collections? 【发布时间】:2019-01-20 21:17:22 【问题描述】:

我有多个长度相同的集合,一个List<DateTime> 类型的时间戳集合和几个List<double> 类型的数据集合。 List<double 中每个索引位置的值对应于List<DateTime> 中的相应索引位置。

我希望能够通过TimeSpan 压缩所有数据集合中的数据,该TimeSpan 应用于List<DateTime>,并将时间戳分组到TimeSpan 箱中,并对每个数据集合应用相同的分组。

这是我目前“压缩”时间戳的时间序列的方式:

var someTimeStamps = new List<DateTime>(); 
                var compression = TimeSpan.FromHours(1).Ticks;
                var compressedTimeStamps = from rawData in someTimeStamps
                    group rawData by rawData.Ticks / numberTicks
                    into tickData
                    select new DateTime(tickData.Key * compression);

如何调整代码以使相同的分组也适用于数据集合List&lt;double&gt;?我想应用一个对每个数据组中的值进行平均的分组逻辑。我的目标是计算效率,内存消耗不是我希望在这一点上优化的问题。

例如:

List&lt;DateTime&gt; items:(为简单起见,以下值的顺序为(年、月、日、小时、分钟、秒):

(1) 2018, 8, 14, 08, 20, 05 (2) 2018 年 8 月 14 日 08 年 45 月 25 日 (3) 2018 年 8 月 14 日 09 年 02 月 53 日 (4) 2018, 8, 14, 09, 34, 12 (5) 2018, 8, 14, 09, 44, 12

List&lt;value&gt;项目:

(1) 12 (2) 15 (3) 27 (4) 03 (5) 12

应用TimeSpan.FromHours(1) 的压缩,两个集合的期望结果是:

List&lt;DateTime&gt;项目:

(1) 2018, 8, 14, 08, 00, 00 (2) 2018 8 14 09 00 00

List&lt;double&gt; items(平均应用于每个组中的项目)

(1) 13.5(12 和 15 的平均值) (2) 14(平均 27、3 和 12)

【问题讨论】:

Zip 允许您将两个可枚举项投影到一个(新)可枚举项中。因此,您创建了一个具有两个属性的新匿名类型——一个是DateTime,一个是double。然后从那里做任何你需要做的事情。 @mjwills,这是有道理的,感谢您的详细说明,但是,如果我想将分组应用于多个数据系列,是否可以在一次通话中做到这一点,或者我是否必须运行投影并多次分组? github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs 如果你想Zip 超过两个系列,可以使用。 谢谢@mjwills,我去看看。是的,尽管问题仅使用一个数据列表,但我的意图是将其应用于具有一个匹配长度时间戳集合的多个数据列表。出于简化目的,我在问题中使用了一个数据集合,因为大多数解决方案最有可能在此特定方面进行扩展 【参考方案1】:

你可以通过下面的代码做到这一点

List<DateTime> dateTimes = new List<DateTime>();
dateTimes.Add(new DateTime(2018, 8, 14, 08, 20, 05));
dateTimes.Add(new DateTime(2018, 8, 14, 08, 45, 25));
dateTimes.Add(new DateTime(2018, 8, 14, 09, 02, 53));
dateTimes.Add(new DateTime(2018, 8, 14, 09, 34, 12));
dateTimes.Add(new DateTime(2018, 8, 14, 09, 44, 12));

List<int> ints = new List<int>();
ints.Add(12);
ints.Add(15);
ints.Add(27);
ints.Add(03);
ints.Add(12);



var averages = dateTimes.Select((k, v) => new  k, v )
                        .GroupBy(x => new DateTime(x.k.Year, x.k.Month, x.k.Day, x.k.Hour, 0, 0))
                        .ToDictionary(g => g.Key, g => g.Select(x => ints.ElementAt(x.v)).Average());

输出:

编辑:

如果您希望将您的数据分成两个列表,如List&lt;DateTime&gt;List&lt;double&gt;,那么您可以将上面的字典投影到分离的键和值列表。喜欢

 List<DateTime> dateTimeList = averages.Keys.ToList();
 List<double>  valuesList = averages.Values.ToList();

如果我理解正确的话

将该问题扩展到一个时间戳系列但多个数据系列

var grouped = dateTimes
              .Zip(ints, (k, v) => new  k, v )
              .GroupBy(g => new DateTime(g.k.Year, g.k.Month, g.k.Day, g.k.Hour, 0, 0), g => g.v);

上面的代码为您提供了日期时间和 wrt 多个数据系列的压缩

尝试一次可能对你有帮助。

【讨论】:

@Matt Wolf,查看答案可能对你有帮助:) 我需要将生成的系列放在单独的集合中,生成 List&lt;DateTime&gt;List&lt;double&gt; ,并将该问题扩展到一个时间戳系列但多个数据系列 临时将多个集合转换为包含这些集合中的值的元素序列,将其分组,然后将其拆分。您还可以将主集合转换为“索引+元素”对象,按对象对其进行分组,从组中提取索引并使用它来拼凑其他集合的组。除此之外,您必须自己实现分组逻辑。 @Lasse Vågsæther Karlsen,是的,正在尝试做 @Matt Wolf,请查看答案中的编辑部分可能对您有帮助:)【参考方案2】:

我决定对每个数据点进行一次经典迭代,因为它只需要一次迭代,而不管数据收集的数量如何(感谢我的一位朋友,他建议对这种方法进行分析):

public void CompressData(TimeSpan compression)
    
        //declare generic buffer value function (bid/ask here)
        var bufferFunction = new Func<int, double>(index => (Bid[index] + Ask[index]) / 2);

        Open = new List<double>();
        High = new List<double>();
        Low = new List<double>();
        Close = new List<double>();
        var lastCompTs = -1L;
        var dataBuffer = new List<double>();
        var timeStamps = new List<DateTime>();

        for (int i = 0; i < TimeStamps.Count; ++i)
        
            var compTs = TimeStamps[i].Ticks / compression.Ticks;
            if (compTs == lastCompTs)
            
                //same timestamp -> add to buffer
                dataBuffer.Add(bufferFunction(i));
            
            else
            
                if (dataBuffer.Count > 0)
                
                    timeStamps.Add(new DateTime(compTs * compression.Ticks));
                    Open.Add(dataBuffer.First());
                    High.Add(dataBuffer.Max());
                    Low.Add(dataBuffer.Min());
                    Close.Add(dataBuffer.Last());
                

                lastCompTs = compTs;
                dataBuffer.Clear();
            
        

        if (dataBuffer.Count > 0)
        
            timeStamps.Add(new DateTime(lastCompTs * compression.Ticks));
            Open.Add(dataBuffer.First());
            High.Add(dataBuffer.Max());
            Low.Add(dataBuffer.Min());
            Close.Add(dataBuffer.Last());
        

        //assign time series collection
        TimeStamps = timeStamps;
    

【讨论】:

以上是关于如何对一个集合进行分组并将相同的分组应用于其他相同长度的集合?的主要内容,如果未能解决你的问题,请参考以下文章

在一列中对具有相同数据的行进行分组,并将其相关数据汇总在另一列中 [ORACLE SQL]

如何根据相同的值对数组进行分组

如何对相同布尔值的块进行分组?

如何对按顺序排列的相同值进行分组

SQL以多种方式对相同数据进行分组

如何对具有相同大小的连续数字进行分组