Mercurial Hosting > luan
comparison lucene/src/luan/modules/lucene/Ab_testing.luan @ 274:8afe9f2fdfec
AB testing, not fully tested
git-svn-id: https://luan-java.googlecode.com/svn/trunk@275 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Mon, 10 Nov 2014 03:28:32 +0000 |
parents | |
children | a35d1177bbf0 |
comparison
equal
deleted
inserted
replaced
273:073044e3ac03 | 274:8afe9f2fdfec |
---|---|
1 import "luan:Math" | |
2 import "luan:Table" | |
3 | |
4 | |
5 function of(index) | |
6 | |
7 local ab_testing = {} | |
8 | |
9 ab_testing.test_map = {} | |
10 ab_testing.test_list = {} | |
11 | |
12 function ab_testing.test(test) | |
13 test.name or error "name not defined" | |
14 test.values or error "values not defined" | |
15 -- test.date_field is optional | |
16 | |
17 local field = "ab_test_" .. test.name | |
18 index.fields[field] == nil or error("test "+test.name+" already defined") | |
19 index.fields[field] = field .. " index" | |
20 test.field = field | |
21 | |
22 -- pass in map of name to aggregator factory | |
23 -- returns map of name to (map of value to result) and "start_date" | |
24 function test.results(aggregator_factories) | |
25 return index.Searcher( function(searcher) | |
26 local results = {} | |
27 for name in pairs(aggregator_factories) do | |
28 results[name] = {} | |
29 end | |
30 local date_field = test.date_field | |
31 local start_date = nil | |
32 for _, value in ipairs(test.values) do | |
33 local aggregators = {} | |
34 for name, factory in pairs(aggregator_factories) do | |
35 aggregators[name] = factory() | |
36 end | |
37 local query = { [field] = value } | |
38 searcher.search(query, function(doc) | |
39 for _, aggregator in pairs(aggregators) do | |
40 aggregator.aggregate(doc) | |
41 end | |
42 if date_field ~= nil then | |
43 local date = doc[date_field] | |
44 if date ~= nil and (start_date==nil or start_date > date) then | |
45 start_date = date | |
46 end | |
47 end | |
48 end) | |
49 for name, aggregator in pairs(aggregators) do | |
50 results[name][value] = aggregator.result | |
51 end | |
52 end | |
53 results.start_date = start_date | |
54 return results | |
55 end ) | |
56 end | |
57 | |
58 ab_testing.test_map[test.name] = test | |
59 ab_testing.test_list[#ab_testing.test_list + 1] = test | |
60 | |
61 return test | |
62 end | |
63 | |
64 function ab_testing.value(test_name,values) | |
65 return values[test_name] or ab_testing.test_map[test_name].values[1] | |
66 end | |
67 | |
68 -- returns map from test name to value | |
69 function ab_testing.from_doc(doc) | |
70 local tests = ab_testing.test_list | |
71 local values = {} | |
72 for _, test in ipairs(tests) do | |
73 values[test.name] = doc[test.field] | |
74 end | |
75 return values | |
76 end | |
77 | |
78 function ab_testing.to_doc(doc,values,tests) | |
79 tests = tests or ab_testing.test_list | |
80 if values == nil then | |
81 for _, test in ipairs(tests) do | |
82 doc[test.field] = test.values[Math.random(#test.values)] | |
83 end | |
84 else | |
85 for _, test in ipairs(tests) do | |
86 doc[test.field] = values[test.name] | |
87 end | |
88 end | |
89 end | |
90 | |
91 return ab_testing | |
92 end | |
93 | |
94 | |
95 -- aggregator factories | |
96 | |
97 -- fn(doc) should return boolean whether doc should be counted | |
98 function count(fn) | |
99 return function() | |
100 local aggregator = {} | |
101 aggregator.result = 0 | |
102 function aggregator.aggregate(doc) | |
103 if fn(doc) then | |
104 aggregator.result = aggregator.result + 1 | |
105 end | |
106 end | |
107 return aggregator | |
108 end | |
109 end | |
110 | |
111 count_all = count( function() return true end ) | |
112 | |
113 -- fn(doc) should return number to add to result, return 0 for nothing | |
114 function sum(fn) | |
115 return function() | |
116 local aggregator = {} | |
117 aggregator.result = 0 | |
118 function aggregator.aggregate(doc) | |
119 aggregator.result = aggregator.result + fn(doc) | |
120 end | |
121 return aggregator | |
122 end | |
123 end | |
124 | |
125 | |
126 | |
127 local function percent(x,total) | |
128 if total==0 then | |
129 return 0 | |
130 else | |
131 return 100 * x / total | |
132 end | |
133 end | |
134 | |
135 function fancy(results,names) | |
136 local fancy = {} | |
137 fancy.start_date = results.start_date | |
138 local name = names[1] | |
139 fancy[name] = {} | |
140 for value, count in pairs(result[name]) do | |
141 fancy[name][value] = {} | |
142 fancy[name][value].count = count | |
143 fancy[name][value].pct_of_total = 100 | |
144 fancy[name][value].pct_of_prev = 100 | |
145 end | |
146 local all = result[name] | |
147 local prev = all | |
148 for i in range(2,#names) do | |
149 name = names[i] | |
150 fancy[name] = {} | |
151 for value, count in pairs(result[name]) do | |
152 fancy[name][value] = {} | |
153 fancy[name][value].count = count | |
154 fancy[name][value].pct_of_total = percent(count,all[value]) | |
155 fancy[name][value].pct_of_prev = percent(count,prev[value]) | |
156 end | |
157 prev = result[name] | |
158 end | |
159 return fancy | |
160 end |