Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using LINQ to create an IEnumerable<> of delta values

I've got a list of timestamps (in ticks), and from this list I'd like to create another one that represents the delta time between entries.

Let's just say, for example, that my master timetable looks like this:

  1. 10
  2. 20
  3. 30
  4. 50
  5. 60
  6. 70

What I want back is this:

  1. 10
  2. 10
  3. 20
  4. 10
  5. 10

What I'm trying to accomplish here is detect that #3 in the output table is an outlier by calculating the standard deviation. I've not taken statistics before, but I think if I look for the prevalent value in the output list and throw out anything outside of 1 sigma that this will work adequately for me.

I'd love to be able to create the output list with a single LINQ query, but I haven't figured it out yet. Currently I'm just brute forcing it with a loop.

like image 211
Dave Avatar asked Oct 06 '10 15:10

Dave


2 Answers

If you are running .NET 4.0, this should work fine:

var deltas = list.Zip(list.Skip(1), (current, next) => next - current);

Apart from the multiple enumerators, this is quite efficient; it should work well on any kind of sequence.

Here's an alternative for .NET 3.5:

var deltas = list.Skip(1)
                 .Select((next, index) => next - list[index]);

Obviously, this idea will only be efficient when the list's indexer is employed. Modifying it to use ElementAt may not be a good idea: quadratic run-time will occur for non IList<T> sequences. In this case, writing a custom iterator is a good solution.

EDIT: If you don't like the Zip + Skip(1) idea, writing an extension such as this (untested) maybe useful in these sorts of circumstances:

public class CurrentNext<T>
{
    public T Current { get; private set; }
    public T Next { get; private set; }

    public CurrentNext(T current, T next)
    {
        Current = current;
        Next = next;
    }
}

...

public static IEnumerable<CurrentNext<T>> ToCurrentNextEnumerable<T>(this IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentException("source");

    using (var source = enumerable.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            yield break;

        T current = enumerator.Current;

        while (enumerator.MoveNext())
        {
            yield return new CurrentNext<T>(current, enumerator.Current);
            current = enumerator.Current;
        }
    }
}

Which you could then use as:

var deltas = list.ToCurrentNextEnumerable()
                 .Select(c=> c.Next - c.Current);
like image 69
Ani Avatar answered Sep 29 '22 10:09

Ani


You can use Ani's answer:-

var deltas = list.Zip(list.Skip(1), (current, next) => next - current);

With a super-simple implementation of the Zip extension method:-

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
  this IEnumerable<TFirst> first,
  IEnumerable<TSecond> second,
  Func<TFirst, TSecond, TResult> func)
{
  var ie1 = first.GetEnumerator();
  var ie2 = second.GetEnumerator();

  while (ie1.MoveNext() && ie2.MoveNext())
    yield return func(ie1.Current, ie2.Current);
}

That'll work with 3.5.

like image 40
Iain Galloway Avatar answered Sep 29 '22 09:09

Iain Galloway