3

私は疑問に思っていました。私が働いている会社では、大量のデータを管理していますが、データは顧客から事実上提供されているため、必ずしも信頼できるとは限りません。それには正当な理由があります。その多くのタイムスタンプが間違っているか、一部が欠落しているか、またはその他のものがあります。

私が最近やらなければならなかったタスクの 1 つは、基本的に、一連の要素内で null である要素を見つけてから、次の非 null 要素を見つけて、それらの null レコード間の差を平均化することです。つまり、データセット A があるとします。

A = { 0f, 1f, 2f, 5f, Null, Null, Null, 7f, Null, 8f }

0 と Null を区別する必要があることに注意してください。違いは明らかに、0 は 0 であるのに対し、Null はデータがまったくないことです。

LINQ を使用して、基本的に A の次のサブセクションにアクセスできる方法はありますか。

Subsection { Null, Null, Null, 7f }

そして、4 つのレコードにわたって (7/4f) に変換できるようにコレクションに入れます。

Subsection { 1.75f, 1.75f, 1.75f, 1.75f }

もう一度繰り返すとA、次の出力が得られます。

{ 0f, 1f, 2f, 5f, 1.75f, 1.75f, 1.75f, 1.75f, 4f, 4f }

現在、私がこれを行う方法は、null 要素を探してから、連続するすべての null を a に格納しList<T>、次の非 null を見つけた後、上記を反復してすべての変数を割り当てることList<T>です。それは仕事をしますが、かなり厄介に見えます。

では、ナルシシズムのために、これをきちんと行う (= コードの混乱を少なくする) 方法はありますか?

擬似

a = { 0, 1, 2, 5, null, null, null, 7, null, 0 }


nullList = new List()
for i = 0, a.length
    if i == null
        nullList.add(i)
    else
        if nullList.length > 0
            nullList.add(i)
            int avg = nullList.Aggregate(x => x)
            foreach element in nullList
                element = avg
            nullList.clear()
4

6 に答える 6

2

私があなたの質問を正しく理解していればnull、リスト内の値を最初の非値に基づく値に置き換えたいと考えていnullます。これに s の 2 番目のリストが必要な理由がわかりませんnull。これは、リストをその場で変更する試みですが、既に持っているものよりもはるかに短くはありません。

var A = new List<float?> { 0f, 1f, 2f, 5f, null, null, null, 7f, null, 8f };

for (int i = A.IndexOf(null); i != -1; i = A.IndexOf(null, i))
{
    int j = 0;
    do { j++; } while (A[i + j] == null);
    float f = A[i + j].Value / (j + 1);
    do { A[i++] = f; } while (j --> 0);
}

// A == { 0f, 1f, 2f, 5f, 1.75f, 1.75f, 1.75f, 1.75f, 4f, 4f }

このコードは、s のリストを繰り返し検索しnull(以前に a を見つけたときに中断したところから続行します)、隣り合った s のnull数を数え、最初の非値をギャップ全体に分配します。このコードは、各ギャップの後に常に非値があると想定しています。nullnullnull

多くのコメントで指摘されているように、ここでは LINQ を使用しても実際の利点はありません。

于 2013-10-15T14:30:43.713 に答える
2

そのため、最初に というヘルパー メソッドを使用しますGroupWhile。シーケンスと関数を受け取ります。その関数には、前のアイテムと現在のアイテムが与えられ、それに基づいて、現在のアイテムが新しいグループの一部であるか、前のグループの一部であるかが決定されます。いくつかの条件が満たされている間、アイテムをグループ化できます。

public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
    this IEnumerable<T> source, Func<T, T, bool> predicate)
{
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
            yield break;

        List<T> list = new List<T>() { iterator.Current };

        T previous = iterator.Current;

        while (iterator.MoveNext())
        {
            if (predicate(previous, iterator.Current))
            {
                list.Add(iterator.Current);
            }
            else
            {
                yield return list;
                list = new List<T>() { iterator.Current };
            }

            previous = iterator.Current;
        }
        yield return list;
    }
}

これを使用して、前のアイテムが null のときにアイテムをグループ化できます。次に、各グループを取得し、そのグループgroup.Count()時間の平均値を繰り返し、シーケンスを再び平坦化します。

public static IEnumerable<float> ConsolodateNulls<T>(IEnumerable<float?> source)
    where T : struct
{
    return source.GroupWhile((prev, curr) => prev == null)
        .SelectMany(group => Enumerable.Repeat(
            group.LastOrDefault(item => item != null) ?? 0 / group.Count(),
            group.Count()));
}
于 2013-10-15T14:37:21.953 に答える
1

純粋にLINQ内でそれを行う方法は次のとおりです。

var data = new List<float?> { 0f, 1f, 2f, 5f, null, null, null, 7f, null, 8f };
var corrected = data
    .Select((v,i) => new {
        Index = i
        // Find the index of the next non-null item in the list
    ,   NextNonNull = i + data
            .Skip(i)
            .Select((vv,j) => new {j,vv})
            .First(p => p.vv.HasValue).j
    ,   Value = v
    })
    .GroupBy(p => p.NextNonNull)
    // For each group, insert its average g.Count() times
    .SelectMany(g => g.Select(e => data[g.Key]/g.Count()))
    .ToList();
for (var i = 0 ; i != data.Count ; i++ ) {
    Console.WriteLine("{0} - {1}", data[i], corrected[i]);
}

免責事項: このソリューションは娯楽目的でのみ提供されています。ループに基づくソリューションよりも遅くなりfor、複雑さに余分な順序が追加される可能性があります (つまり、O(n^2)代わりにループを作成しますO(n))。

于 2013-10-15T14:52:07.200 に答える
1

Aggregateあなたの娯楽のために使用する純粋なLINQバージョン:

float?[] A = { 0f, 1f, 2f, 5f, null, null, null, 7f, null, 8f };
var result = A.Aggregate(Tuple.Create(new List<float>(), 0), 
 (items, current) => 
 {
    if(current.HasValue)
    {
        if(items.Item2 == 0)
            items.Item1.Add(current.Value);
        else
        {
            var avg = current.Value / (items.Item2 + 1);
            for(int i = 0; i <= items.Item2; i++)
                items.Item1.Add(avg);
        }
        return Tuple.Create(items.Item1, 0);
    }
    else
        return Tuple.Create(items.Item1, items.Item2 + 1);
 }).Item1;

平均的な開発者の頭は で爆発するので、私はこれを本番コードでは使用しませAggregateTuple

于 2013-10-15T15:07:59.653 に答える