kot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/kot.rb +4 -0
- data/lib/kot/hill_climbing_estimator.rb +62 -0
- data/lib/kot/item_response_theory.rb +54 -0
- data/lib/kot/randomesque_selector.rb +15 -0
- data/lib/kot/test.rb +41 -0
- data/lib/kot/version.rb +3 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d51222d3929d9c0712fe696713d63329b18950e6fdde1975e1c97b7076b4959d
|
4
|
+
data.tar.gz: 802eecd963724fd4398292b645a2f1a681eddadcc177b3d63b66785e4a7a8479
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b024eda53798ada11f65efbc172843da5a6d7836cc0140178da8ab0381618e10094cb7fe51d6f5636218d75edeb1425d55b05ddcd9c82287bedbd62728ee6dcf
|
7
|
+
data.tar.gz: 772bc25d250dc89b2ce11e148cca0a5638b93575de92d979f911d49e39e7fca1abd978338b24a70582a3dafbe096b0b878338a4236f4b68624994ca4fa83d33b
|
data/lib/kot.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Kot
|
2
|
+
|
3
|
+
class HillClimbingEstimator
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
# "The variable stepsize changed the 0 estimate by half the distance to the appropriate ... value in the item pool."
|
8
|
+
def dodd(est_theta:0.0, items:[], last_response:[])
|
9
|
+
max_b = items.map(&:b).max
|
10
|
+
min_b = items.map(&:b).min
|
11
|
+
|
12
|
+
last_response ? est_theta + ((max_b - est_theta) / 2.0) : est_theta - ((est_theta - min_b) / 2.0)
|
13
|
+
end
|
14
|
+
|
15
|
+
def estimate(responses:[], items:[], all_items:[], est_theta:0.0)
|
16
|
+
|
17
|
+
|
18
|
+
if responses.uniq.count == 1
|
19
|
+
raise ArgumentError.new("Responses are all #{responses.first} but missing all_items argument") if all_items.empty?
|
20
|
+
return dodd(est_theta:est_theta, items:all_items, last_response:responses.last)
|
21
|
+
end
|
22
|
+
|
23
|
+
lower_bound = items.map(&:b).min
|
24
|
+
upper_bound = items.map(&:b).max
|
25
|
+
|
26
|
+
return lower_bound if upper_bound == lower_bound
|
27
|
+
|
28
|
+
best_theta = - Float::INFINITY
|
29
|
+
max_ll = - Float::INFINITY
|
30
|
+
|
31
|
+
10.times do
|
32
|
+
|
33
|
+
step_size = (upper_bound - lower_bound) / 10
|
34
|
+
intervals = (lower_bound...upper_bound).step(step_size)
|
35
|
+
|
36
|
+
intervals.each do |ii|
|
37
|
+
|
38
|
+
ll = ItemResponseTheory.log_likelihood(ii, responses, items)
|
39
|
+
|
40
|
+
|
41
|
+
if ll > max_ll
|
42
|
+
max_ll = ll
|
43
|
+
|
44
|
+
#TODO - precision-based early exit
|
45
|
+
best_theta = ii
|
46
|
+
else
|
47
|
+
lower_bound = best_theta - step_size
|
48
|
+
upper_bound = [ii, best_theta+step_size].max #HACK
|
49
|
+
break
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# End of hillclimb
|
55
|
+
end
|
56
|
+
|
57
|
+
return best_theta
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Kot
|
2
|
+
|
3
|
+
|
4
|
+
# Requires a, b, c, d
|
5
|
+
module ItemResponseTheory
|
6
|
+
|
7
|
+
#
|
8
|
+
# Module methods for statistics based on estimated theta, items and responses
|
9
|
+
#
|
10
|
+
|
11
|
+
def self.log_likelihood(est_theta, responses, items)
|
12
|
+
ps = items.map {|i| i.icc(est_theta) }
|
13
|
+
ls = ps.each_with_index.map {|e,i| responses[i] ? Math.log(e) : Math.log(1.0 - e)} #TODO: Polychotomous
|
14
|
+
ls.inject(:+)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.test_info(est_theta, items)
|
18
|
+
items.map {|i| i.inf(est_theta) }.inject(:+)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.var(est_theta, items)
|
22
|
+
1.0/test_info(est_theta, items)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.see(est_theta, items)
|
26
|
+
Math.sqrt(var(est_theta, items))
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
#
|
31
|
+
# Methods intended for inclusion into Item-classes
|
32
|
+
#
|
33
|
+
|
34
|
+
def icc_component(theta)
|
35
|
+
Math.exp(-a * (theta - b))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Item characteristic curve
|
39
|
+
def icc(theta)
|
40
|
+
c + ((d - c) / (1 + icc_component(theta)))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Information value of an item
|
44
|
+
def inf(theta)
|
45
|
+
vp = icc(theta)
|
46
|
+
|
47
|
+
top = (a ** 2) * ((vp - c) ** 2) * ((d - vp) ** 2)
|
48
|
+
bottom = ((d - c) ** 2) * vp * (1.0 - vp)
|
49
|
+
|
50
|
+
top/bottom
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/kot/test.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Kot
|
2
|
+
|
3
|
+
|
4
|
+
class Test
|
5
|
+
|
6
|
+
attr_reader :est_theta
|
7
|
+
|
8
|
+
def initialize(item_bank, est_theta = 0.0, selector = RandomesqueSelector.new, estimator = HillClimbingEstimator.new)
|
9
|
+
@item_bank = item_bank
|
10
|
+
@est_theta = est_theta
|
11
|
+
@selector = selector
|
12
|
+
@estimator = estimator
|
13
|
+
|
14
|
+
@responses = []
|
15
|
+
@asked_items = []
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def see
|
20
|
+
return Float::INFINITY if @asked_items.empty?
|
21
|
+
ItemResponseTheory.see(@est_theta, @asked_items)
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_est_theta
|
25
|
+
@est_theta = @estimator.estimate(est_theta: @est_theta, responses:@responses, items:@asked_items, all_items:@item_bank)
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_item
|
29
|
+
possible_items = @item_bank - @asked_items
|
30
|
+
@selector.select(@est_theta, possible_items)
|
31
|
+
end
|
32
|
+
|
33
|
+
def respond(response, item)
|
34
|
+
@responses << response
|
35
|
+
@asked_items << item
|
36
|
+
update_est_theta
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/kot/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Watkins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-08-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: " Kot is a basic toolkit for getting started with computerised adaptive
|
14
|
+
testing (CAT). It includes a module to calculate item response theory (IRT) statistics
|
15
|
+
for dichotomous items with 1-4PL characteristic curves (ICCs), a Hill Climbing ability
|
16
|
+
(theta) estimator, a Randomesque selector and a Test class to make tying all this
|
17
|
+
together easier.\n"
|
18
|
+
email:
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/kot.rb
|
24
|
+
- lib/kot/hill_climbing_estimator.rb
|
25
|
+
- lib/kot/item_response_theory.rb
|
26
|
+
- lib/kot/randomesque_selector.rb
|
27
|
+
- lib/kot/test.rb
|
28
|
+
- lib/kot/version.rb
|
29
|
+
homepage: https://github.com/stupidpupil/kot
|
30
|
+
licenses:
|
31
|
+
- GPL-3.0+
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.7.6
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: Kot is a basic toolkit for getting started with computerised adaptive testing
|
53
|
+
(CAT).
|
54
|
+
test_files: []
|