Нужны идеи для анализа данных почти в реальном времени за определенные интервалы с эффективностью памяти/процессора.

У меня есть несколько датчиков окружающей среды, и я хочу обнаруживать внезапные изменения температуры и медленные тенденции с течением времени... однако я хотел бы выполнять большую часть математических операций на основе того, что находится в памяти, с параметрами, которые могут выглядеть следующим образом: (с учетом сдача)

(примечание: элементы в скобках вычисляются в реальном времени по мере добавления данных)

  • 5 минут (производная, максимум, минимум, среднее) + 36 точек данных за последние 3 часа
  • 10 минут (производная, максимум, минимум, среднее) + 0 точек данных, расчет основан на 5-минутной выборке
  • 30 минут (производная, максимум, минимум, среднее) + 0 точек данных, расчет основан на 5-минутной выборке
  • Почасовая (производная, макс., мин., средняя) + 24 точки данных за последний 1 день
  • Ежедневно (производная, макс, мин, среднее) + 32 точки данных за последний месяц
  • Ежемесячно (производная, макс, мин, среднее) + 12 точек данных за прошлый год

Каждая точка данных представляет собой двухбайтовое число с плавающей запятой. Таким образом, каждый датчик будет потреблять до 124 чисел с плавающей запятой плюс 24 вычисляемых переменных. Я хотел бы поддерживать столько датчиков, сколько позволяет встроенное устройство .NET.

Поскольку я использую встроенное устройство для этого проекта, моя память ограничена, а также мощность ввода-вывода и процессора.

Как бы вы реализовали это в .NET? До сих пор я создал пару структур и назвал их «TrackableFloat», где вставка значения приводит к тому, что старое удаляется из массива, и выполняется пересчет.

Единственное, что делает это более сложным, чем могло бы быть, это то, что для любого датчика не возвращаются данные, тогда эта точка данных должна быть исключена/игнорирована из всех последующих вычислений в реальном времени.

Когда все сказано и сделано, если какое-либо из значений: (производное, максимальное, минимальное, среднее) достигает предварительно определенного значения, тогда срабатывает событие .NET.

Я думаю, что кто-то сочтет это интересной проблемой и хотел бы услышать, как они подошли бы к ее реализации.

  1. Вы бы использовали класс или структуру?

  2. Как бы вы инициировали расчеты? (События наиболее вероятны)

  3. Как будут срабатывать оповещения?

  4. Как бы вы хранили данные по уровням?

  5. Есть ли библиотека, которая уже делает что-то подобное? (возможно, это должен быть мой первый вопрос)

  6. Как бы вы эффективно вычислили производную?

Вот мой первый взлом, и он не полностью соответствует спецификации, но очень эффективен. Было бы интересно услышать ваши мысли.

enum UnitToTrackEnum
{
    Minute,
    FiveMinute,
    TenMinute,
    FifteenMinute,
    Hour,
    Day,
    Week,
    Month,
    unknown
}
class TrackableFloat
{
    object Gate = new object();

    UnitToTrackEnum trackingMode = UnitToTrackEnum.unknown;
    int ValidFloats = 0;
    float[] FloatsToTrack;

    public TrackableFloat(int HistoryLength, UnitToTrackEnum unitToTrack)
    {
        if (unitToTrack == UnitToTrackEnum.unknown)
            throw new InvalidOperationException("You must not have an unknown measure of time to track.");

        FloatsToTrack = new float[HistoryLength];

        foreach (var i in FloatsToTrack)
        {
            float[i] = float.MaxValue;
        }


        trackingMode = unitToTrack;

        Min = float.MaxValue;
        Max = float.MinValue;
        Sum = 0;
    }
    public void Add(DateTime dt, float value)
    {
        int RoundedDTUnit = 0;

        switch (trackingMode)
        {
            case UnitToTrackEnum.Minute:
                {
                    RoundedDTUnit = dt.Minute;
                    break;
                }
            case UnitToTrackEnum.FiveMinute:
                {
                    RoundedDTUnit = System.Math.Abs(dt.Minute / 5);
                    break;
                }
            case UnitToTrackEnum.TenMinute:
                {
                    RoundedDTUnit = System.Math.Abs(dt.Minute / 10);
                    break;
                }
            case UnitToTrackEnum.FifteenMinute:
                {
                    RoundedDTUnit = System.Math.Abs(dt.Minute / 15);
                    break;
                }
            case UnitToTrackEnum.Hour:
                {
                    RoundedDTUnit = dt.Hour;
                    break;
                }
            case UnitToTrackEnum.Day:
                {
                    RoundedDTUnit = dt.Day;
                    break;
                }
            case UnitToTrackEnum.Week:
                {
                    //RoundedDTUnit = System.Math.Abs( );
                    break;
                }
            case UnitToTrackEnum.Month:
                {
                    RoundedDTUnit = dt.Month;
                    break;
                }
            case UnitToTrackEnum.unknown:
                {
                    throw new InvalidOperationException("You must not have an unknown measure of time to track.");
                }
            default:
                break;
        }


        bool DoRefreshMaxMin = false;
        if (FloatsToTrack.Length < RoundedDTUnit)
        {
            if (value == float.MaxValue || value == float.MinValue)
            {
                // If invalid data...
                lock (Gate)
                {
                    // Get rid of old data...
                    var OldValue = FloatsToTrack[RoundedDTUnit];
                    if (OldValue != float.MaxValue || OldValue != float.MinValue)
                    {
                        Sum -= OldValue;
                        ValidFloats--;

                        if (OldValue == Max || OldValue == Min)
                            DoRefreshMaxMin = true;
                    }

                    // Save new data
                    FloatsToTrack[RoundedDTUnit] = value;
                }
            }
            else
            {
                lock (Gate)
                {
                    // Get rid of old data...
                    var OldValue = FloatsToTrack[RoundedDTUnit];
                    if (OldValue != float.MaxValue || OldValue != float.MinValue)
                    {
                        Sum -= OldValue;
                        ValidFloats--;
                    }

                    // Save new data
                    FloatsToTrack[RoundedDTUnit] = value;
                    Sum += value;
                    ValidFloats++;

                    if (value < Min)
                        Min = value;

                    if (value > Max)
                        Max = value;

                    if (OldValue == Max || OldValue == Min)
                        DoRefreshMaxMin = true;
                }
            }

            // Function is placed here to avoid a deadlock
            if (DoRefreshMaxMin == true)
                RefreshMaxMin();
        }
        else
        {
            throw new IndexOutOfRangeException("Index " + RoundedDTUnit + " is out of range for tracking mode: " + trackingMode.ToString());
        }
    }

    public float Sum { get; set; }
    public float Average
    {
        get
        {
            if (ValidFloats > 0)
                return Sum / ValidFloats;
            else
                return float.MaxValue;
        }
    }
    public float Min { get; set; }
    public float Max { get; set; }
    public float Derivative { get; set; }

    public void RefreshCounters()
    {
        lock (Gate)
        {
            float sum = 0;
            ValidFloats = 0;

            Min = float.MaxValue;
            Max = float.MinValue;

            foreach (var i in FloatsToTrack)
            {
                if (i != float.MaxValue || i != float.MinValue)
                {
                    if (Min == float.MaxValue)
                    {
                        Min = i;
                        Max = i;
                    }

                    sum += i;
                    ValidFloats++;

                    if (i < Min)
                        Min = i;

                    if (i > Max)
                        Max = i;
                }
            }
            Sum = sum;
        }
    }
    public void RefreshMaxMin()
    {
        if (ValidFloats > 0)
        {
            Min = float.MaxValue;
            Max = float.MinValue;

            lock (Gate)
            {
                foreach (var i in FloatsToTrack)
                {
                    if (i != float.MaxValue || i != float.MinValue)
                    {
                        if (i < Min)
                            Min = i;

                        if (i > Max)
                            Max = i;
                    }
                }
            }
        }
    }
}

person halfbit    schedule 27.01.2011    source источник
comment
В каком формате эти 2 байта с плавающей запятой? Вы делаете с ними вычисления или конвертируете в обычные числа с плавающей запятой? Какой это тип процессора?   -  person phkahler    schedule 27.01.2011


Ответы (1)


Вам следует рассмотреть возможность поиска библиотеки CEP, такой как Неспер.

person Chris Shain    schedule 27.01.2011
comment
Я могу использовать только L-GPL... GPL2 не годится, так как я встраиваю несовместимый лицензионный код от других поставщиков. - person halfbit; 27.01.2011
comment
Я обнаружил, что код GPL2 может размещать кто-то другой, и я могу ссылаться на него как на веб-службу, не затрагивая мое другое приложение. - person halfbit; 16.10.2011