Sha256: 9cfa3f534113e1915fc120d1e5e96cc52e6073634542943607499b880698bac6

Contents?: true

Size: 1.93 KB

Versions: 3

Compression:

Stored size: 1.93 KB

Contents

# extensions
require "anomaly_detection/ext"

# modules
require "anomaly_detection/version"

module AnomalyDetection
  class << self
    def detect(series, period:, max_anoms: 0.1, alpha: 0.05, direction: "both", plot: false, verbose: false)
      raise ArgumentError, "series must contain at least 2 periods" if series.size < period * 2

      if series.is_a?(Hash)
        sorted = series.sort_by { |k, _| k }
        x = sorted.map(&:last)
      else
        x = series
      end

      res = _detect(x, period, max_anoms, alpha, direction, verbose)
      res.map! { |i| sorted[i][0] } if series.is_a?(Hash)
      res
    end

    # TODO add tooltips
    def plot(series, anomalies)
      require "vega"

      data =
        if series.is_a?(Hash)
          series.map { |k, v| {x: iso8601(k), y: v, anomaly: anomalies.include?(k)} }
        else
          series.map.with_index { |v, i| {x: i, y: v, anomaly: anomalies.include?(i)} }
        end

      if series.is_a?(Hash)
        x = {field: "x", type: "temporal"}
        x["scale"] = {type: "utc"} if series.keys.first.is_a?(Date)
      else
        x = {field: "x", type: "quantitative"}
      end

      Vega.lite
        .data(data)
        .layer([
          {
            mark: {type: "line"},
            encoding: {
              x: x,
              y: {field: "y", type: "quantitative", scale: {zero: false}},
              color: {value: "#fa9088"}
            }
          },
          {
            transform: [{"filter": "datum.anomaly == true"}],
            mark: {type: "point", size: 200},
            encoding: {
              x: x,
              y: {field: "y", type: "quantitative"},
              color: {value: "#19c7ca"}
            }
          }
        ])
        .config(axis: {title: nil, labelFontSize: 12})
    end

    private

    def iso8601(v)
      if v.is_a?(Date)
        v.strftime("%Y-%m-%d")
      else
        v.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
      end
    end
  end
end

Version data entries

3 entries across 3 versions & 1 rubygems

Version Path
anomaly_detection-0.1.4 lib/anomaly_detection.rb
anomaly_detection-0.1.3 lib/anomaly_detection.rb
anomaly_detection-0.1.2 lib/anomaly_detection.rb