AuctioneerSuite/Auc-Stat-StdDev/StatStdDev.lua
2026-04-13 17:48:13 -04:00

469 lines
18 KiB
Lua

--[[
Auctioneer - Standard Deviation Statistics module
Version: 5.7.4568 (KillerKoala)
Revision: $Id: StatStdDev.lua 4840 2010-08-04 21:44:00Z Nechckn $
URL: http://auctioneeraddon.com/
This is an addon for World of Warcraft that adds statistical history to the auction data that is collected
when the auction is scanned, so that you can easily determine what price
you will be able to sell an item for at auction or at a vendor whenever you
mouse-over an item in the game
License:
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program(see GPL.txt); if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Note:
This AddOn's source code is specifically designed to work with
World of Warcraft's interpreted AddOn system.
You have an implicit license to use this AddOn with these facilities
since that is its designated purpose as per:
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
--]]
if not AucAdvanced then return end
local AucAdvanced = AucAdvanced
local libType, libName = "Stat", "StdDev"
local lib,parent,private = AucAdvanced.NewModule(libType, libName)
if not lib then return end
local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals()
local tonumber,strsplit,select,pairs=tonumber,strsplit,select,pairs
local setmetatable=setmetatable
local wipe=wipe
local floor,ceil,abs=floor,ceil,abs
local concat=table.concat
local tinsert,tremove=table.insert,table.remove
-- GLOBALS: AucAdvancedStatStdDevData
local GetFaction = AucAdvanced.GetFaction
local SSDRealmData
local cache = {} -- setmetatable({}, {__mode="v"})
local ZValues = {.063, .126, .189, .253, .319, .385, .454, .525, .598, .675, .756, .842, .935, 1.037, 1.151, 1.282, 1.441, 1.646, 1.962, 20, 20000}
function lib.CommandHandler(command, ...)
local serverKey = GetFaction()
local _,_,keyText = AucAdvanced.SplitServerKey(serverKey)
if (command == "help") then
aucPrint(_TRANS('SDEV_Help_SlashHelp1') )--Help for Auctioneer - StdDev
local line = AucAdvanced.Config.GetCommandLead(libType, libName)
aucPrint(line, "help}} - ".._TRANS('SDEV_Help_SlashHelp2') ) --this StdDev help
aucPrint(line, "clear}} - ".._TRANS('SDEV_Help_SlashHelp3'):format(keyText) ) --clear current %s StdDev price database
elseif (command == "clear") then
lib.ClearData(serverKey)
end
end
function lib.Processor(callbackType, ...)
if (callbackType == "tooltip") then
lib.ProcessTooltip(...)
elseif (callbackType == "config") then
--Called when you should build your Configator tab.
private.SetupConfigGui(...)
elseif (callbackType == "scanstats") then
wipe(cache)
end
end
lib.Processors = {}
function lib.Processors.tooltip(callbackType, ...)
lib.ProcessTooltip(...)
end
function lib.Processors.config(callbackType, ...)
--Called when you should build your Configator tab.
private.SetupConfigGui(...)
end
function lib.Processors.scanstats(callbackType, ...)
wipe(cache)
end
lib.ScanProcessors = {}
function lib.ScanProcessors.create(operation, itemData, oldData)
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
-- This function is responsible for processing and storing the stats after each scan
-- Note: itemData gets reused over and over again, so do not make changes to it, or use
-- it in places where you rely on it. Make a deep copy of it if you need it after this
-- function returns.
-- We're only interested in items with buyouts.
local buyout = itemData.buyoutPrice
if not buyout or buyout == 0 then return end
if (itemData.stackSize > 1) then
buyout = buyout.."/"..itemData.stackSize
end
-- Get the signature of this item and find it's stats.
local linkType,itemId,property,factor = AucAdvanced.DecodeLink(itemData.link)
if (linkType ~= "item") then return end
if (factor and factor ~= 0) then property = property.."x"..factor end
local serverKey = GetFaction()
if not SSDRealmData[serverKey] then SSDRealmData[serverKey] = {} end
local stats = private.UnpackStats(SSDRealmData[serverKey][itemId])
if not stats[property] then stats[property] = {} end
if #stats[property] >= 100 then
tremove(stats[property], 1)
end
tinsert(stats[property], buyout)
SSDRealmData[serverKey][itemId] = private.PackStats(stats)
end
local BellCurve = AucAdvanced.API.GenerateBellCurve();
-----------------------------------------------------------------------------------
-- The PDF for standard deviation data, standard bell curve
-----------------------------------------------------------------------------------
function lib.GetItemPDF(hyperlink, serverKey)
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
-- Get the data
local average, mean, _, stddev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey)
if not (average and stddev) or average == 0 or stddev == 0 then
return nil; -- No data, cannot determine pricing
end
local lower, upper = average - 3 * stddev, average + 3 * stddev;
-- Build the PDF based on standard deviation & average
BellCurve:SetParameters(average, stddev);
return BellCurve, lower, upper; -- This has a __call metamethod so it's ok
end
-----------------------------------------------------------------------------------
function private.GetCfromZ(Z)
--C = 0.05*i
if (not Z) then
return .05
end
if (Z > 10) then
return .99
end
local i = 1
while Z > ZValues[i] do
i = i + 1
end
if i == 1 then
return .05
else
i = i - 1 + ((Z - ZValues[i-1]) / (ZValues[i] - ZValues[i-1]))
return i*0.05
end
end
local datapoints_price = {} -- used temporarily in .GetPrice() to avoid unpacking strings multiple times
local datapoints_stack = {}
function lib.GetPrice(hyperlink, serverKey)
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
local linkType,itemId,property,factor = AucAdvanced.DecodeLink(hyperlink)
if (linkType ~= "item") then return end
if (factor and factor ~= 0) then property = property.."x"..factor end
if not serverKey then serverKey = GetFaction() end
if not SSDRealmData[serverKey] then return end
if not SSDRealmData[serverKey][itemId] then return end
local cacheKey = serverKey ..":"..itemId..":"..property
if cache[cacheKey] then
return unpack(cache[cacheKey])
end
local stats = private.UnpackStats(SSDRealmData[serverKey][itemId])
if not stats[property] then return end
local count = #stats[property]
if (count < 1) then return end
local total, number = 0, 0
for i = 1, count do
local price, stack = strsplit("/", stats[property][i])
price = tonumber(price) or 0
stack = tonumber(stack) or 1
if (stack < 1) then stack = 1 end
datapoints_price[i] = price -- cache these for further processing below (so they don't need to strsplit etc)
datapoints_stack[i] = stack
total = total + price
number = number + stack
end
local mean = total / number
if (count < 2) then return 0,0,0, mean, count end
local variance = 0
for i = 1, count do
variance = variance + ((mean - datapoints_price[i]/datapoints_stack[i]) ^ 2);
end
variance = variance / count;
local stdev = variance ^ 0.5
local deviation = 1.5 * stdev
total = 0 -- recompute them from entries inside the allowed deviation
number = 0
for i = 1, count do
local price,stack = datapoints_price[i], datapoints_stack[i]
if abs((price/stack) - mean) < deviation then
total = total + price
number = number + stack
end
end
local confidence = .01
local average
if (number > 0) then -- number<1 will happen if we have e.g. two big clusters: one at 1g and one at 10g
average = total / number
confidence = (.15*average)*(number^0.5)/(stdev)
confidence = private.GetCfromZ(confidence)
end
cache[cacheKey] = { average, mean, false, stdev, variance, count, confidence }
return average, mean, false, stdev, variance, count, confidence
end
function lib.GetPriceColumns()
return "Average", "Mean", false, "Std Deviation", "Variance", "Count"
end
local array = {}
function lib.GetPriceArray(hyperlink, serverKey)
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
-- Clean out the old array
wipe(array)
-- Get our statistics
local average, mean, _, stdev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey)
-- These 3 are the ones that most algorithms will look for
array.price = average or mean
array.seen = count
array.confidence = confidence
-- This is additional data
array.normalized = average
array.mean = mean
array.deviation = stdev
array.variance = variance
-- Return a temporary array. Data in this array is
-- only valid until this function is called again.
return array
end
function private.SetupConfigGui(gui)
local id = gui:AddTab(lib.libName, lib.libType.." Modules")
--gui:MakeScrollable(id)
gui:AddHelp(id, "what stddev stats",
_TRANS('SDEV_Help_StdDevStats') ,--What are StdDev stats?
_TRANS('SDEV_Help_StdDevStatsAnswer') --StdDev stats are the numbers that are generated by the StdDev module consisting of a filtered Standard Deviation calculation of item cost.
)
--all options in here will be duplicated in the tooltip frame
function private.addTooltipControls(id)
gui:AddHelp(id, "filtered stddev",
_TRANS('SDEV_Help_Filtered') ,--What do you mean filtered?
_TRANS('SDEV_Help_FilteredAnswer') --Items outside a (1.5*Standard) variance are ignored and assumed to be wrongly priced when calculating the deviation.
)
gui:AddHelp(id, "what standard deviation",
_TRANS('SDEV_Help_StandardDeviationCalculation') ,--What is a Standard Deviation calculation?
_TRANS('SDEV_Help_StandardDeviationCalculationAnswer') --In short terms, it is a distance to mean average calculation.
)
gui:AddHelp(id, "what normalized",
_TRANS('SDEV_Help_Normalized') ,--What is the Normalized calculation?
_TRANS('SDEV_Help_NormalizedAnswer') --In short terms again, it is the average of those values determined within the standard deviation variance calculation.
)
gui:AddHelp(id, "what confidence",
_TRANS('SDEV_Help_Confidence') ,--What does confidence mean?
_TRANS('SDEV_Help_ConfidenceAnswer') --Confidence is a value between 0 and 1 that determines the strength of the calculations (higher the better).
)
gui:AddHelp(id, "why multiply stack size stddev",
_TRANS('SDEV_Help_WhyMultiplyStack') ,--Why have the option to multiply by stack size?
_TRANS('SDEV_Help_WhyMultiplyStackAnswer') --The original Stat-StdDev multiplied by the stack size of the item, but some like dealing on a per-item basis.
)
gui:AddControl(id, "Header", 0, _TRANS('SDEV_Interface_StdDevOptions') )--StdDev options
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
gui:AddControl(id, "Checkbox", 0, 1, "stat.stddev.enable", _TRANS('SDEV_Interface_EnableStdDevStats') )--Enable StdDev Stats
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_EnableStdDevStats') )--Allow StdDev to gather and return price data
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
gui:AddControl(id, "Checkbox", 0, 4, "stat.stddev.tooltip", _TRANS('SDEV_Interface_Show') )--Show stddev stats in the tooltips?
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_Show') )--Toggle display of stats from the StdDev module on or off
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.mean", _TRANS('SDEV_Interface_DisplayMean') )--Display Mean
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_DisplayMean') )--Toggle display of 'Mean' calculation in tooltips on or off
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.normal", _TRANS('SDEV_Interface_DisplayNormalized') )--Display Normalized
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_DisplayNormalized') )--Toggle display of 'Normalized' calculation in tooltips on or off'
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.stdev", _TRANS('SDEV_Interface_DisplayStandardDeviation') )--Display Standard Deviation
gui:AddTip(id,_TRANS('SDEV_HelpTooltip_DisplayStandardDeviation') )--Toggle display of 'Standard Deviation' calculation in tooltips on or off
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.confid", _TRANS('SDEV_Interface_DisplayConfidence') )--Display Confidence
gui:AddTip(id,_TRANS('SDEV_HelpTooltip_DisplayConfidence') )--Toggle display of 'Confidence' calculation in tooltips on or off
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
gui:AddControl(id, "Checkbox", 0, 4, "stat.stddev.quantmul", _TRANS('SDEV_Interface_MultiplyStack') )--Multiply by Stack Size
gui:AddTip(id,_TRANS('SDEV_HelpTooltip_MultiplyStack') )--Multiplies by current stack size if on
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
end
--This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place
local tooltipID = AucAdvanced.Settings.Gui.tooltipID
--now we create a duplicate of these in the tooltip frame
private.addTooltipControls(id)
if tooltipID then private.addTooltipControls(tooltipID) end
end
function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, ...)
-- In this function, you are afforded the opportunity to add data to the tooltip should you so
-- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should
-- display in the tooltip.
if not AucAdvanced.Settings.GetSetting("stat.stddev.tooltip") then return end
if not quantity or quantity < 1 then quantity = 1 end
if not AucAdvanced.Settings.GetSetting("stat.stddev.quantmul") then quantity = 1 end
local average, mean, _, stdev, var, count, confidence = lib.GetPrice(hyperlink)
if (mean and mean > 0) then
tooltip:AddLine(_TRANS('SDEV_Tooltip_PricesPoints'):format(count) )--StdDev prices %d points:
if AucAdvanced.Settings.GetSetting("stat.stddev.mean") then
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_MeanPrice'), mean*quantity)-- Mean price
end
if (average and average > 0) then
if AucAdvanced.Settings.GetSetting("stat.stddev.normal") then
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Normalized'), average*quantity)-- Normalized
if (quantity > 1) then
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Individually'), average)-- (or individually)
end
end
if AucAdvanced.Settings.GetSetting("stat.stddev.stdev") then
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_StdDeviation'), stdev*quantity)-- Std Deviation
if (quantity > 1) then
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Individually'), stdev)-- (or individually)
end
end
if AucAdvanced.Settings.GetSetting("stat.stddev.confid") then
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Confidence')..(floor(confidence*1000))/1000)-- Confidence:
end
end
end
end
function lib.OnLoad(addon)
if SSDRealmData then return end
AucAdvanced.Settings.SetDefault("stat.stddev.tooltip", false)
AucAdvanced.Settings.SetDefault("stat.stddev.mean", false)
AucAdvanced.Settings.SetDefault("stat.stddev.normal", false)
AucAdvanced.Settings.SetDefault("stat.stddev.stdev", true)
AucAdvanced.Settings.SetDefault("stat.stddev.confid", true)
AucAdvanced.Settings.SetDefault("stat.stddev.quantmul", true)
AucAdvanced.Settings.SetDefault("stat.stddev.enable", true)
private.InitData()
end
function lib.ClearItem(hyperlink, serverKey)
local linkType, itemID, property, factor = AucAdvanced.DecodeLink(hyperlink)
if (linkType ~= "item") then
return
end
if (factor ~= 0) then property = property.."x"..factor end
if not serverKey then serverKey = GetFaction() end
if SSDRealmData[serverKey] and SSDRealmData[serverKey][itemID] then
local stats = private.UnpackStats(SSDRealmData[serverKey][itemID])
if stats[property] then
stats[property] = nil
SSDRealmData[serverKey][itemID] = private.PackStats(stats)
wipe(cache)
local _, _, keyText = AucAdvanced.SplitServerKey(serverKey)
aucPrint(libType.._TRANS('SDEV_Interface_ClearingData'):format(hyperlink, keyText))--- StdDev: clearing data for %s for {{%s}}
end
end
end
function lib.ClearData(serverKey)
serverKey = serverKey or GetFaction()
wipe(cache)
if AucAdvanced.API.IsKeyword(serverKey, "ALL") then
wipe(SSDRealmData)
aucPrint(_TRANS('SDEV_Help_SlashHelp4').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing StdDev stats for // All realms
elseif SSDRealmData[serverKey] then
SSDRealmData[serverKey] = nil
local _, _, keyText = AucAdvanced.SplitServerKey(serverKey)
aucPrint(_TRANS('SDEV_Help_SlashHelp4').." {{"..keyText.."}}") --Clearing StdDev stats for
end
end
--[[ Local functions ]]--
function private.UnpackStatIter(data, ...)
local c = select("#", ...)
local v
for i = 1, c do
v = select(i, ...)
local property, info = strsplit(":", v)
property = tonumber(property) or property
if (property and info) then
data[property] = {strsplit(";", info)}
local item
for i=1, #data[property] do
item = data[property][i]
data[property][i] = tonumber(item) or item
end
end
end
end
function private.UnpackStats(dataItem)
local data = {}
if (dataItem) then
private.UnpackStatIter(data, strsplit(",", dataItem))
end
return data
end
local tmp={}
function private.PackStats(data)
local n=0
for property, info in pairs(data) do
n=n+1
tmp[n]=property..":"..concat(info, ";")
end
return concat(tmp,",",1,n)
end
function private.InitData()
private.InitData = nil
-- Do any database upgrades here
if not AucAdvancedStatStdDevData then AucAdvancedStatStdDevData = {} end
SSDRealmData = AucAdvancedStatStdDevData
-- Do any regular database maintenance here
end
AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-StdDev/StatStdDev.lua $", "$Rev: 4840 $")