Mercurial Hosting > luan
view lucene/src/luan/modules/lucene/Ab_testing.luan @ 338:f05c631ebaaf
README.md edited online with Bitbucket
author | Hugo Teixeira <hugo.tech@gmail.com> |
---|---|
date | Wed, 25 Mar 2015 04:29:32 +0000 |
parents | 78a6a71afbfd |
children | 5b36f663a1b8 |
line wrap: on
line source
local Luan = require "luan:Luan" local pairs = Luan.pairs local ipairs = Luan.ipairs local error = Luan.error local range = Luan.range local Math = require "luan:Math" local Table = require "luan:Table" local Io = require "luan:Io" local Http = require "luan:web/Http" local Logging = require "luan:logging/Logging" local logger = Logging.logger "Ab_testing" function of(index) local ab_testing = {} ab_testing.test_map = {} function ab_testing.test(test) test.name or error "name not defined" test.values or error "values not defined" -- list of event names test.events or error "events not defined" -- map of event name to aggregator factory test.aggregator_factories or error "aggregator_factories not defined" -- test.date_field is optional local field = "ab_test_" .. test.name index.fields[field] == nil or error("test "+test.name+" already defined") index.fields[field] = field .. " index" test.field = field -- returns map of event name to (map of value to result) and "start_date" function test.results() return index.Searcher( function(searcher) local results = {} for name in pairs(test.aggregator_factories) do results[name] = {} end local date_field = test.date_field local start_date = nil for _, value in ipairs(test.values) do local aggregators = {} for name, factory in pairs(test.aggregator_factories) do aggregators[name] = factory() end local query = index.Query.term{ [field] = value } searcher.search(query, function(doc) for _, aggregator in pairs(aggregators) do aggregator.aggregate(doc) end if date_field ~= nil then local date = doc[date_field] if date ~= nil and (start_date==nil or start_date > date) then start_date = date end end end) for name, aggregator in pairs(aggregators) do results[name][value] = aggregator.result end end results.start_date = start_date return results end ) end function test.fancy_results() local events = test.events local results = test.results() local fancy = {} fancy.start_date = results.start_date local event = events[1] fancy[event] = {} for value, count in pairs(results[event]) do fancy[event][value] = {} fancy[event][value].count = count fancy[event][value].pct_of_total = 100 fancy[event][value].pct_of_prev = 100 end local all = results[event] local prev = all for i in range(2,#events) do event = events[i] fancy[event] = {} for value, count in pairs(results[event]) do fancy[event][value] = {} fancy[event][value].count = count fancy[event][value].pct_of_total = percent(count,all[value]) fancy[event][value].pct_of_prev = percent(count,prev[value]) end prev = results[event] end return fancy end ab_testing.test_map[test.name] = test return test end function ab_testing.value(test_name,values) return values[test_name] or ab_testing.test_map[test_name].values[1] end -- returns map from test name to value function ab_testing.from_doc(doc) local values = {} for _, test in pairs(ab_testing.test_map) do values[test.name] = doc[test.field] end return values end function ab_testing.to_doc(doc,values,tests) tests = tests or ab_testing.test_map if values == nil then values = {} for _, test in pairs(tests) do values[test.name] = test.values[Math.random(#test.values)] end end for _, test in pairs(tests) do doc[test.field] = values[test.name] end return values end function ab_testing.web_page(test_names) return { service = function() local results = {} for _, name in ipairs(test_names) do local test = ab_testing.test_map[name] test or error("test not found: "..name) results[name] = test.fancy_results() end Io.stdout = Http.response.text_writer() html(test_names,ab_testing.test_map,results) end } end return ab_testing end -- aggregator factories -- fn(doc) should return boolean whether doc should be counted function count(fn) return function() local aggregator = {} aggregator.result = 0 function aggregator.aggregate(doc) if fn(doc) then aggregator.result = aggregator.result + 1 end end return aggregator end end count_all = count( function(doc) return true end ) -- fn(doc) should return number to add to result, return 0 for nothing function sum(fn) return function() local aggregator = {} aggregator.result = 0 function aggregator.aggregate(doc) aggregator.result = aggregator.result + fn(doc) end return aggregator end end function percent(x,total) if total==0 then return 0 else return 100 * x / total end end -- I will change this to use SimplyHTML when this is used again. local function basic_style() %> body {font-family:'Arial',sans-serif;font-size:16px;padding:1em 2em} h1 {font-weight:bold;font-size:20px} h2 {margin:2em 0 0em;font-size:18px;color:#3589B1} table.results {margin-top:.5em;border-collapse:collapse;font-size:90%} table.results th {background:#eee} table.results th,table.results td {border-left:1px solid #bbb;padding:.4em 2em} table.results tr:nth-child(odd) td {background:#f8f8f8} <% end local function format(v) v = v .. '' return v.gsub([[(\d+\.\d{1})\d+]],'$1') end function html(test_names,tests,results) %> <!DOCTYPE html> <html lang="en"> <head> <title>A/B Test Results</title> <style><% basic_style() %></style> </head> <body> <h1>A/B Test Results</h1> <% for _, test_name in ipairs(test_names) do local test = tests[test_name] local result = results[test_name] local n = #test.values %> <h2><%=test_name%></h2> <table class="results"> <tr> <th>Event</th> <th class="top" colspan="<%=n%>">Count</th> <th class="top" colspan="<%=n%>">% of total</th> <th class="top" colspan="<%=n%>">% of prev</th> </tr> <tr> <th></th> <% for _ in range(1,3) do for _, value in ipairs(test.values) do %><th class="top"><%=value%></th><% end end %> </tr> <% for _, event in ipairs(test.events) do local event_values = result[event] %> <tr> <td><%=event%></td> <% for _, value in ipairs(test.values) do %><td><%=format(event_values[value].count)%></th><% end for _, value in ipairs(test.values) do %><td><%=format(event_values[value].pct_of_total)%></th><% end for _, value in ipairs(test.values) do %><td><%=format(event_values[value].pct_of_prev)%></th><% end %> </tr> <% end %> </table> <% end %> </body> </html> <% end