これは一般的に解決するのが難しい問題です。最終的な解決策は、プロセスに大きく依存し、状況に固有のものになります。
そうは言っても、データを理解することから始める必要があります。あるサンプルから次のサンプルまで、どのような種類の変動が可能でしょうか? それを使用して、以前のデータ サンプル (およびおそらく将来のデータ サンプル) を使用して、現在のサンプルが偽物であるかどうかを判断できます。次に、次のようなフィルターになります。
const int MaxQueueLength = 100; // adjust these two values as necessary
const double MaxProjectionError = 5;
List<double> FilterData(List<double> rawData)
{
List<double> toRet = new List<double>(rawData.Count);
Queue<double> history = new Queue<double>(MaxQueueLength); // adjust queue length as necessary
foreach (double raw_Sample in rawData)
{
while (history.Count > MaxQueueLength)
history.Dequeue();
double ProjectedSample = GuessNext(history, raw_Sample);
double CurrentSample = (Math.Abs(ProjectedSample - raw_Sample) > MaxProjectionError) ? ProjectedSample : raw_Sample;
toRet.Add(CurrentSample);
history.Enqueue(CurrentSample);
}
return toRet;
}
魔法は、GuessNext 関数を考え出すことです。ここでは、自分の状況に固有の事柄について説明します。データを収集するプロセスについて知っていることをすべて考慮する必要があります。入力が変化する速度に物理的な制限はありますか? データに、簡単にフィルタリングできる既知の悪い値がありますか?
これは、データの 1 次導関数に基づいて機能する GuessNext 関数の簡単な例です (つまり、データの小さな部分だけを見ると、データはほぼ直線であると想定されます)。
double lastSample = double.NaN;
double GuessNext(Queue<double> history, double nextSample)
{
lastSample = double.IsNaN(lastSample) ? nextSample : lastSample;
//ignore the history for simple first derivative. Assume that input will always approximate a straight line
double toRet = (nextSample + (nextSample - lastSample));
lastSample = nextSample;
return toRet;
}
データに特にノイズが多い場合は、GuessNext に渡す前に平滑化フィルターを適用することをお勧めします。データにとって意味のあるものを考え出すには、アルゴリズムに少し時間を費やす必要があります。
サンプル データは、各サンプルが X 値と Y 値の両方を定義するという点でパラメトリックに見えます。上記のロジックを各ディメンションに個別に適用できる場合があります。これは、1 つのディメンションだけが悪い数値を与える場合に適しています。これは、たとえば、1 つのディメンションがタイムスタンプであり、タイムスタンプが時折偽物である場合に特に成功する可能性があります。