1+ """
2+ $(TYPEDEF)
3+ $(TYPEDFIELDS)
4+
5+ This content balancing procedure takes target proportions for each group of items.
6+ At each step the group with the lowest ratio of seen items to target is selected.
7+
8+ http://dx.doi.org/10.1207/s15324818ame0403_4
9+ """
10+ struct GreedyForcedContentBalancer{InnerRuleT <: NextItemRule } <: NextItemRule
11+ targets:: Vector{Float64}
12+ groups:: Vector{Int}
13+ inner_rule:: InnerRuleT
14+ end
15+
16+ function GreedyForcedContentBalancer (targets:: Dict , groups, bits... )
17+ targets_vec = zeros (Float64, length (targets))
18+ groups_idxs = zeros (Int, length (groups))
19+ group_lookup = Dict {Any, Int} ()
20+ for (idx, group) in enumerate (groups)
21+ if haskey (group_lookup, group)
22+ group_idx = group_lookup[group]
23+ else
24+ group_idx = length (group_lookup) + 1
25+ group_lookup[group] = group_idx
26+ end
27+ groups_idxs[idx] = group_idx
28+ end
29+ if length (group_lookup) != length (targets)
30+ error (" Number of groups $(length (group_lookup)) does not match number of targets $(length (targets)) " )
31+ end
32+ for (group, group_idx) in pairs (group_lookup)
33+ targets_vec[group_idx] = get (targets, group, 0.0 )
34+ end
35+ GreedyForcedContentBalancer (targets_vec, groups_idxs, bits... )
36+ end
37+
38+ function GreedyForcedContentBalancer (targets:: AbstractVector , groups, bits... )
39+ GreedyForcedContentBalancer (targets, groups, NextItemRule (bits... ))
40+ end
41+
42+ function show (io:: IO , :: MIME"text/plain" , rule:: GreedyForcedContentBalancer )
43+ indent_io = indent (io, 2 )
44+ println (io, " Greedy + forced content balancer" )
45+ println (indent_io, " Target ratio: " * join (rule. targets, " , " ))
46+ print (indent_io, " Using rule: " )
47+ show (indent_io, MIME (" text/plain" ), rule. inner_rule)
48+ end
49+
50+ function next_item_bank (targets, groups, responses, items)
51+ seen = zeros (UInt, size (targets))
52+ indices = responses. responses. indices
53+ for group_idx in groups[indices]
54+ seen[group_idx] += 1
55+ end
56+ next_group_idx = argmin (seen ./ targets)
57+ matching_indicator = groups .== next_group_idx
58+ next_items = subset_view (items, matching_indicator)
59+ return (next_items, matching_indicator)
60+ end
61+
62+ function best_item (
63+ rule:: GreedyForcedContentBalancer ,
64+ responses:: TrackedResponses ,
65+ items
66+ )
67+ next_items, matching_indicator = next_item_bank (rule. targets, rule. groups, responses, items)
68+ inner_idx = best_item (rule. inner_rule, responses, next_items)
69+ for (outer_idx, in_group) in enumerate (matching_indicator)
70+ if in_group
71+ inner_idx -= 1
72+ if inner_idx <= 0
73+ return outer_idx
74+ end
75+ end
76+ end
77+ error (" No item found in group length $(length (next_items)) with inner index $inner_idx " )
78+ end
79+
80+ function compute_criteria (
81+ rule:: GreedyForcedContentBalancer ,
82+ responses:: TrackedResponses ,
83+ items
84+ )
85+ next_items, matching_indicator = next_item_bank (rule. targets, rule. groups, responses, items)
86+ criteria = compute_criteria (rule. inner_rule, responses, next_items)
87+ expanded = fill (Inf , length (items))
88+ expanded[matching_indicator] .= criteria
89+ return expanded
90+ end
0 commit comments