Download
70 lines 2.1 KiBLuaModified 2017-10-28 raw
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
local Histogram = setmetatable({}, {__call=function(self,options)
    local h = setmetatable({},{__index=self})
    h:init(options)
    return h
end})

function Histogram:init(options)
    self.pos = options.pos
    self.size = options.size
    local total = 0
    for _,entry in ipairs(options.data) do
        local x, count = unpack(entry)
        total = total + count
    end
    local t = 0
    local iqrMin, iqrMax
    for _,entry in ipairs(options.data) do
        local x, count = unpack(entry)
        t = t + count
        if not iqrMin and t >= .25*total then iqrMin = x end
        if not iqrMax and t >= .75*total then iqrMax = x end
    end
    local h = 2*(iqrMax-iqrMin)*total^(1/3)
    local xmin, xmax = options.xmin or options.data[1][1], options.xmax or options.data[#options.data][1]
    local numBuckets = options.numBuckets or math.max(3, math.min(options.size.x/10, math.ceil((xmax - xmin) * total / h)))
    local buckets = {}
    for i=1,numBuckets do
        buckets[i] = 0
    end
    local ymax = 0
    for _,entry in ipairs(options.data) do
        local x, count = unpack(entry)
        local b = 1 + math.floor((x - xmin)/(xmax - xmin) * (numBuckets-1))
        buckets[b] = (buckets[b] or 0) + count
        ymax = math.max(buckets[b], ymax)
    end
    self.ymax = ymax
    self.xmax = xmax
    self.buckets = buckets
    function self:getX(x)
        return self.pos.x + self.size.x * (x-xmin)/(xmax-xmin)
    end
end

function Histogram:fromList(list, options)
    table.sort(list)
    local data = {}
    for _,x in ipairs(list) do
        if not data[#data] or data[#data][1] ~= x then
            table.insert(data, {x,1})
        else
            data[#data][2] = data[#data][2] + 1
        end
    end
    options.data = data
    return Histogram(options)
end

function Histogram:draw()
    local height = self.size.y / (#self.buckets-1)
    for i,count in ipairs(self.buckets) do
        if count > 0 then
            local width = count/self.ymax * self.size.x
            love.graphics.rectangle('fill', self.pos.x,
                self.pos.y+(#self.buckets-i-1)*height, width, height)
        end
    end
end

return Histogram