【问题标题】:Ruby: calculate average() while excluding nil values from dataRuby:计算平均值(),同时从数据中排除零值
【发布时间】:2014-12-02 09:34:27
【问题描述】:

我对 Ruby 很陌生,但在处理一个看似简单的问题时遇到了一些困难。

代码在这里...

https://github.com/sensu/sensu-community-plugins/blob/master/plugins/graphite/check-stats.rb

...但我在最后包含了当前源代码的完整副本,因为它可能会随着新版本提交到 Github 而发生变化。

这是一个 Sensu 插件。它通过 HTTP 请求从 Graphite 收集数据。将回复存储在正文中,然后将 JSON.parse() 存储到数据中。

对于数据中的每个指标,它都会收集数据点,并对数据点进行平均。如果平均值高于某些阈值(选项 -w 或 -c),则会引发警告或严重。

有时 Graphite 商店有点落后。某些指标可能缺少最新的数据点。发生这种情况时,数据点为零。

问题是,在计算平均值(数据点)时,nil 被视为零。这会人为地降低平均值,有时会导致插件在应该触发的时候没有触发。

从平均值计算中消除零值的最佳方法是什么?

理想情况下,消除 nil 的方式应该是,如果所有数据点都是 nil,那么它应该触发 datapoints.empty 条件。基本上,在它们到达“除非 datapoints.empty?”之前杀死所有的 nil。因为如果全部为零,那么我们实际上没有任何数据点。

或者不知何故 metric.collect{} 应该跳过 nil 值。

我尝试使用 .compact,但似乎没有什么不同(可能我用错了)。


这是当前版本的代码:

#!/usr/bin/env ruby

#
# Checks metrics in graphite, averaged over a period of time.
#
# The fired sensu event will only be critical if a stat is
# above the critical threshold. Otherwise, the event will be warning,
# if a stat is above the warning threshold.
#
# Multiple stats will be checked if * are used
# in the "target" query.
#
# Author: Alan Smith (alan@asmith.me)
# Date: 08/28/2014
#

require 'rubygems' if RUBY_VERSION < '1.9.0'
require 'json'
require 'net/http'
require 'sensu-plugin/check/cli'

class CheckGraphiteStat < Sensu::Plugin::Check::CLI

  option :host,
    :short => "-h HOST",
    :long => "--host HOST",
    :description => "graphite hostname",
    :proc => proc {|p| p.to_s },
    :default => "graphite"

  option :period,
    :short => "-p PERIOD",
    :long => "--period PERIOD",
    :description => "The period back in time to extract from Graphite. Use -24hours, -2days, -15mins, etc, same format as in Graphite",
    :proc => proc {|p| p.to_s },
    :required => true

  option :target,
    :short => "-t TARGET",
    :long => "--target TARGET",
    :description => "The graphite metric name. Can include * to query multiple metrics",
    :proc => proc {|p| p.to_s },
    :required => true

  option :warn,
    :short => "-w WARN",
    :long => "--warn WARN",
    :description => "Warning level",
    :proc => proc {|p| p.to_f },
    :required => false

  option :crit,
    :short => "-c Crit",
    :long => "--crit CRIT",
    :description => "Critical level",
    :proc => proc {|p| p.to_f },
    :required => false

  def average(a)
    total = 0
    a.to_a.each {|i| total += i.to_f}

    total / a.length
  end

  def danger(metric)
    datapoints = metric['datapoints'].collect {|p| p[0].to_f}

    unless datapoints.empty?
      avg = average(datapoints)

      if !config[:crit].nil? && avg > config[:crit]
        return [2, "#{metric['target']} is #{avg}"]
      elsif !config[:warn].nil? && avg > config[:warn]
        return [1, "#{metric['target']} is #{avg}"]
      end
    end
    [0, nil]
  end

  def run
    body =
      begin
        uri = URI("http://#{config[:host]}/render?format=json&target=#{config[:target]}&from=#{config[:period]}")
        res = Net::HTTP.get_response(uri)
        res.body
      rescue Exception => e
        warning "Failed to query graphite: #{e.inspect}"
      end

    status = 0
    message = ''
    data =
      begin
        JSON.parse(body)
      rescue
        []
      end

    unknown "No data from graphite" if data.empty?

    data.each do |metric|
      s, msg = danger(metric)

      message += "#{msg} " unless s == 0
      status = s unless s < status
    end

    if status == 2
      critical message
    elsif status == 1
      warning message
    end
    ok
  end
end

【问题讨论】:

    标签: ruby null


    【解决方案1】:

    您可以使用 Array#compact 来做到这一点:

    ["a", nil, "b", nil, "c", nil].compact
    #=> [ "a", "b", "c" ]
    

    http://ruby-doc.org/core-2.1.3/Array.html#method-i-compact

    【讨论】:

      【解决方案2】:

      好吧,如果你想在收集之前消除 nil,你可以这样做

      metric['datapoints'].reject { |p| p.nil? }.collect {|p| p[0].to_f}
      

      而不是

      metric['datapoints'].collect {|p| p[0].to_f}
      

      顺便说一句,你average也可以改写成

      def average(a)
        a.reduce(0,:+)/a.size
      end
      

      【讨论】:

      • 那是你爱上 Ruby 收藏的那一刻。
      猜你喜欢
      • 1970-01-01
      • 2020-09-06
      • 2017-05-26
      • 1970-01-01
      • 2020-10-08
      • 2014-05-19
      • 2016-02-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多