Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用于记录分析的滑动时间窗口_C#_.net_Algorithm - Fatal编程技术网

C# 用于记录分析的滑动时间窗口

C# 用于记录分析的滑动时间窗口,c#,.net,algorithm,C#,.net,Algorithm,我有一个电话的数据结构。这个问题有两个字段,CallTime和NumberDialled 我要执行的分析是“在10秒钟的窗口中是否有两个以上呼叫相同的号码”集合已按CallTime排序,并且是一个列表 我的解决办法是 List<Cdr> records = GetRecordsSortedByCallTime(); for (int i = 0; i < records.Count; i++) { var baseRecord = records[i]; for

我有一个电话的数据结构。这个问题有两个字段,
CallTime
NumberDialled

我要执行的分析是“在10秒钟的窗口中是否有两个以上呼叫相同的号码”集合已按
CallTime
排序,并且是一个
列表

我的解决办法是

List<Cdr> records = GetRecordsSortedByCallTime();
for (int i = 0; i < records.Count; i++)
{
    var baseRecord = records[i];
    for (int j = i; j < records.Count; j++)
    {
        var comparisonRec = records[j];

        if (comparisonRec.CallTime.Subtract(baseRecord.CallTime).TotalSeconds < 20)
        {
            if (comparisonRec.NumberDialled == baseRecord.NumberDialled)
                ReportProblem(baseRecord, comparisonRec);
        }
        else
        {
            // We're more than 20 seconds away from the base record.  Break out of the inner loop
            break; 
        }
    }
}
List records=GetRecordsSortedByCallTime();
for(int i=0;i
惠斯至少可以说是丑陋的。有没有更好、更干净、更快的方法

虽然我还没有在大型数据集上测试它,但我将在大约每小时100000条记录上运行它,因此将对每条记录进行大量比较


更新数据是按时间而不是数字排序的,与问题的早期版本一样

我不知道您的确切结构,因此我为本演示创建了自己的结构:

class CallRecord
{
    public long NumberDialled { get; set; }
    public DateTime Stamp { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var calls = new List<CallRecord>()
        {
            new CallRecord { NumberDialled=123, Stamp=new DateTime(2011,01,01,10,10,0) },
            new CallRecord { NumberDialled=123, Stamp=new DateTime(2011,01,01,10,10,9) },
            new CallRecord { NumberDialled=123, Stamp=new DateTime(2011,01,01,10,10,18) },
        };

        var dupCalls = calls.Where(x => calls.Any(y => y.NumberDialled == x.NumberDialled && (x.Stamp - y.Stamp).Seconds > 0 && (x.Stamp - y.Stamp).Seconds <= 10)).Select(x => x.NumberDialled).Distinct();

        foreach (var dupCall in dupCalls)
        {
            Console.WriteLine(dupCall);
        }

        Console.ReadKey();
    }
}
类调用记录
{
公共长编号调用{get;set;}
公共日期时间戳{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var calls=新列表()
{
新通话记录{NumberDialled=123,Stamp=new DateTime(2011,01,01,10,10,0)},
新通话记录{NumberDialled=123,Stamp=new DateTime(2011,01,01,10,10,9)},
新通话记录{NumberDialled=123,Stamp=new DateTime(2011,01,01,10,10,18)},
};
var dupCalls=calls.Where(x=>calls.Any(y=>y.NumberDialled==x.NumberDialled&&(x.Stamp-y.Stamp).Seconds>0&(x.Stamp-y.Stamp).Seconds x.NumberDialled.Distinct();
foreach(dupCall中的var dupCall)
{
控制台写入线(dupCall);
}
Console.ReadKey();
}
}

LINQ表达式循环遍历所有记录,并查找当前记录之前(
.Seconds>0
)和时间限制内(
.Seconds如果电话呼叫已按呼叫时间排序,则可以执行以下操作:

  • 初始化一个哈希表,该哈希表对每个电话号码都有一个计数器(哈希表可以先为空,然后在运行时向其添加元素)
  • 有两个指针指向你的链表,我们称它们为“左”和“右”
  • 每当“左”和“右”呼叫之间的时间戳小于10秒时,将“右”向前移动一次,并将新遇到的电话号码的计数增加一次
  • 当差值超过10秒时,将“左”键向前移动1秒,并将“左”键指针向左移动的电话号码的计数减少1秒
  • 在任何时候,如果有一个电话号码的哈希表中的计数器为3或更多,则您已找到一个电话号码,该电话号码在10秒内有2次以上的通话
这是一个线性时间算法,并行处理所有数字。

records.OrderBy(p=>p.CallTime)
records.OrderBy(p => p.CallTime)
    .GroupBy(p => p.NumberDialled)
    .Select(p => new { number = p.Key, cdr = p.ToList() })
    .Select(p => new
    {
        number = p.number,
        cdr =
            p.cdr.Select((value, index) => index == 0 ? null : (TimeSpan?)(value.CallTime - p.cdr[index - 1].CallTime))
            .FirstOrDefault(q => q.HasValue && q.Value.TotalSeconds < 10)
    }).Where(p => p.cdr != null);
.GroupBy(p=>p.NumberDialled) .Select(p=>new{number=p.Key,cdr=p.ToList()}) .选择(p=>new { 数字=p.number, cdr= p、 Select((value,index)=>index==0?null:(TimeSpan?)(value.CallTime-p.cdr[index-1].CallTime)) .FirstOrDefault(q=>q.HasValue&&q.Value.TotalSeconds<10) }).其中(p=>p.cdr!=null);
我建议您使用该方法

无功扩展(Rx)是一个库,用于使用可观测序列和LINQ样式的查询运算符组合异步和基于事件的程序。使用Rx,开发人员使用可观测数据表示异步数据流,使用LINQ运算符查询异步数据流,并使用调度程序参数化异步数据流中的并发性

Interval方法返回一个可观察序列,该序列在每个周期后产生一个值

下面是一个简单的例子:

    var callsPer10Seconds = Observable.Interval(TimeSpan.FromSeconds(10));

    from x in callsPer10Seconds 
           group x by x into g 
           let count = g.Count() 
           orderby count descending 
           select new {Value = g.Key, Count = count}; 

    foreach (var x in q) 
    { 
        Console.WriteLine("Value: " + x.Value + " Count: " + x.Count); 
    } 
分两步进行:

  • 使用调用本身和感兴趣范围内的所有调用生成枚举
  • 筛选此列表以查找连续呼叫
  • 使用AsParallel扩展方法对每个记录并行进行计算

    也可以不在最后调用ToArray,让计算完成,而其他代码可以在线程上执行,而不是强制它等待并行计算完成

    var records = new [] {
        new { CallTime= DateTime.Now, NumberDialled = 1 },
        new { CallTime= DateTime.Now.AddSeconds(1), NumberDialled = 1 }
    };
    var span = TimeSpan.FromSeconds(10);
    
    // Select for each call itself and all other calls in the next 'span' seconds
    var callInfos = records.AsParallel()
        .Select((r, i) =>
            new
            {
                Record = r,
                Following = records.Skip(i+1)
                                .TakeWhile(r2 => r2.CallTime - r.CallTime < span)
            }
        );
    
    // Filter the calls that interest us
    var problematic = (from callinfo in callInfos 
                    where callinfo.Following.Any(r => callinfo.Record.NumberDialled == r.NumberDialled)
                    select callinfo.Record)
                    .ToArray();
    
    var记录=new[]{
    new{CallTime=DateTime.Now,NumberDialled=1},
    new{CallTime=DateTime.Now.AddSeconds(1),NumberDialled=1}
    };
    var span=从秒开始的时间跨度(10);
    //为每个呼叫本身以及下一个“span”秒内的所有其他呼叫选择
    var callinfo=records.AsParallel()
    .选择((r,i)=>
    新的
    {
    记录=r,
    Following=记录。跳过(i+1)
    .TakeWhile(r2=>r2.CallTime-r.CallTimecallinfo.Record.numberCalled==r.numberCalled)
    选择callinfo.Record)
    .ToArray();
    
    如果性能是可以接受的(我认为应该是这样,因为100k记录并不特别多),那么这种方法(我认为)很好而且干净:

    首先,我们按编号对记录进行分组:

    var byNumber = 
        from cdr in calls
        group cdr by cdr.NumberDialled into g
        select new 
                 {
                     NumberDialled = g.Key,
                     Calls = g.OrderBy(cdr => cdr.CallTime)
                 };
    
    我们现在所做的是
    Zip
    (.NET4)每个调用集合,并将其自身移动一个,以转换
    var interestingNumbers =
        from g in byNumber
        let callGaps = g.Calls.Zip(g.Calls.Skip(1), 
            (cdr1, cdr2) => cdr2.CallTime - cdr1.CallTime)
        where callGaps.Any(ts => ts.TotalSeconds <= 10)
        select g.NumberDialled;