adaptivetesting 1.0.0__py3-none-any.whl
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.
- adaptivetesting/__init__.py +7 -0
- adaptivetesting/data/__init__.py +2 -0
- adaptivetesting/data/__pickle_context.py +51 -0
- adaptivetesting/data/__sqlite_context.py +76 -0
- adaptivetesting/implementations/__default_implementation.py +72 -0
- adaptivetesting/implementations/__init__.py +3 -0
- adaptivetesting/implementations/__pre_test.py +83 -0
- adaptivetesting/implementations/__semi_implementation.py +134 -0
- adaptivetesting/math/__init__.py +2 -0
- adaptivetesting/math/__standard_error.py +59 -0
- adaptivetesting/math/__test_information.py +43 -0
- adaptivetesting/models/__adaptive_test.py +183 -0
- adaptivetesting/models/__algorithm_exception.py +3 -0
- adaptivetesting/models/__init__.py +7 -0
- adaptivetesting/models/__item_pool.py +246 -0
- adaptivetesting/models/__item_selection_exception.py +3 -0
- adaptivetesting/models/__test_item.py +29 -0
- adaptivetesting/models/__test_result.py +23 -0
- adaptivetesting/services/__estimator_interface.py +40 -0
- adaptivetesting/services/__init__.py +2 -0
- adaptivetesting/services/__test_results_interface.py +27 -0
- adaptivetesting/simulation/__init__.py +1 -0
- adaptivetesting/simulation/__simulation.py +75 -0
- adaptivetesting/tests/__init__.py +0 -0
- adaptivetesting/tests/test_adaptive_test.py +67 -0
- adaptivetesting/tests/test_bayes_modal.py +31 -0
- adaptivetesting/tests/test_code_examples.py +22 -0
- adaptivetesting/tests/test_information_test.py +38 -0
- adaptivetesting/tests/test_load_test_items.py +112 -0
- adaptivetesting/tests/test_mle.py +73 -0
- adaptivetesting/tests/test_pretest.py +39 -0
- adaptivetesting/tests/test_standard_error.py +57 -0
- adaptivetesting/tests/test_urrys_rule.py +28 -0
- adaptivetesting-1.0.0.dist-info/METADATA +108 -0
- adaptivetesting-1.0.0.dist-info/RECORD +38 -0
- adaptivetesting-1.0.0.dist-info/WHEEL +5 -0
- adaptivetesting-1.0.0.dist-info/licenses/LICENSE +372 -0
- adaptivetesting-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from adaptivetesting.models import AdaptiveTest, TestItem, ItemPool
|
|
3
|
+
|
|
4
|
+
item1 = TestItem()
|
|
5
|
+
item1.b = 0.24
|
|
6
|
+
item1.id = 1
|
|
7
|
+
item2 = TestItem()
|
|
8
|
+
item2.b = 0.89
|
|
9
|
+
item2.id = 2
|
|
10
|
+
item3 = TestItem()
|
|
11
|
+
item3.b = -0.6
|
|
12
|
+
item3.id = 3
|
|
13
|
+
|
|
14
|
+
items = [item1, item2, item3]
|
|
15
|
+
simulated_responses = [1, 0, 1]
|
|
16
|
+
|
|
17
|
+
item_pool = ItemPool(
|
|
18
|
+
test_items=items.copy(),
|
|
19
|
+
simulated_responses=simulated_responses.copy()
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestAdaptiveTest(unittest.TestCase, AdaptiveTest):
|
|
24
|
+
def __init__(self, methodName='runTest'):
|
|
25
|
+
AdaptiveTest.__init__(
|
|
26
|
+
self,
|
|
27
|
+
item_pool=item_pool,
|
|
28
|
+
simulation_id="1",
|
|
29
|
+
true_ability_level=0,
|
|
30
|
+
participant_id=0
|
|
31
|
+
)
|
|
32
|
+
unittest.TestCase.__init__(self, methodName)
|
|
33
|
+
|
|
34
|
+
def estimate_ability_level(self) -> float:
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
def test_get_difficulties(self):
|
|
38
|
+
difficulties = self.get_item_difficulties()
|
|
39
|
+
self.assertEqual(difficulties, [0.24, 0.89, -0.6])
|
|
40
|
+
|
|
41
|
+
def test_standard_error(self):
|
|
42
|
+
"""This should calculate a standard error without failing"""
|
|
43
|
+
self.answered_items = [item1, item2]
|
|
44
|
+
error = self.get_ability_se()
|
|
45
|
+
|
|
46
|
+
def test_get_next_item(self):
|
|
47
|
+
next_item = self.get_next_item()
|
|
48
|
+
self.assertEqual(next_item, item1)
|
|
49
|
+
|
|
50
|
+
def test_testing_procedure_once(self):
|
|
51
|
+
self.run_test_once()
|
|
52
|
+
# test showed item
|
|
53
|
+
self.assertEqual(self.test_results[0].showed_item,
|
|
54
|
+
item1.b)
|
|
55
|
+
|
|
56
|
+
# print(self.test_results)
|
|
57
|
+
# print(simulated_responses)
|
|
58
|
+
# test response
|
|
59
|
+
self.assertEqual(
|
|
60
|
+
self.test_results[0].response,
|
|
61
|
+
simulated_responses[0]
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# test item is removed from pool
|
|
65
|
+
# print(self.item_pool.test_items)
|
|
66
|
+
self.assertEqual(self.item_pool.test_items, [item2, item3])
|
|
67
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from adaptivetesting.models import ItemPool, TestItem
|
|
3
|
+
from adaptivetesting.math.estimators import BayesModal, NormalPrior
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
class TestBayesModal(unittest.TestCase):
|
|
7
|
+
def __init__(self, methodName = "runTest"):
|
|
8
|
+
super().__init__(methodName)
|
|
9
|
+
|
|
10
|
+
def test_estimation_4pl(self):
|
|
11
|
+
items = pd.DataFrame({
|
|
12
|
+
"a": [1.32, 1.07, 0.84],
|
|
13
|
+
"b": [-0.63, 0.18, -0.84],
|
|
14
|
+
"c": [0.17, 0.10, 0.19],
|
|
15
|
+
"d": [0.87, 0.93, 1]
|
|
16
|
+
})
|
|
17
|
+
item_pool = ItemPool.load_from_dataframe(items)
|
|
18
|
+
|
|
19
|
+
response_pattern = [0,1,0]
|
|
20
|
+
estimator = BayesModal(
|
|
21
|
+
response_pattern=response_pattern,
|
|
22
|
+
items=item_pool.test_items,
|
|
23
|
+
prior=NormalPrior(0, 1),
|
|
24
|
+
optimization_interval=(-4, 4)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
result = estimator.get_estimation()
|
|
28
|
+
|
|
29
|
+
self.assertAlmostEqual(result, -0.4741753, 4)
|
|
30
|
+
|
|
31
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
class TestCodeExamples(unittest.TestCase):
|
|
4
|
+
def __init__(self, methodName = "runTest"):
|
|
5
|
+
super().__init__(methodName)
|
|
6
|
+
|
|
7
|
+
def test_default_example(self):
|
|
8
|
+
# read file
|
|
9
|
+
with open("code_examples/example_default.py", "r") as file:
|
|
10
|
+
content = file.read()
|
|
11
|
+
file.close()
|
|
12
|
+
|
|
13
|
+
exec(content)
|
|
14
|
+
|
|
15
|
+
def test_semi_adaptive_example(self):
|
|
16
|
+
# read file
|
|
17
|
+
with open("code_examples/example_semi.py", "r") as file:
|
|
18
|
+
content = file.read()
|
|
19
|
+
file.close()
|
|
20
|
+
|
|
21
|
+
exec(content)
|
|
22
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from adaptivetesting.math import test_information_function
|
|
3
|
+
import jax.numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
class TestInformation(unittest.TestCase):
|
|
8
|
+
def test_information_calculation_1pl(self):
|
|
9
|
+
difficulties = np.array([0.7, 0.9, 0.6])
|
|
10
|
+
ability = np.array([0], dtype="float32")
|
|
11
|
+
|
|
12
|
+
result = test_information_function(
|
|
13
|
+
mu=ability,
|
|
14
|
+
a=np.array(1),
|
|
15
|
+
b=difficulties,
|
|
16
|
+
c=np.array(1),
|
|
17
|
+
d=np.array(0),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
self.assertAlmostEqual(result, 0.6559974211, 3)
|
|
21
|
+
|
|
22
|
+
def test_information_calculation_4pl(self):
|
|
23
|
+
items = pd.DataFrame({
|
|
24
|
+
"a": [1.32, 1.07, 0.84],
|
|
25
|
+
"b": [-0.63, 0.18, -0.84],
|
|
26
|
+
"c": [0.17, 0.10, 0.19],
|
|
27
|
+
"d": [0.87, 0.93, 1]
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
result = test_information_function(np.array(0),
|
|
31
|
+
np.array(items.a.to_numpy()),
|
|
32
|
+
np.array(items.b.to_numpy()),
|
|
33
|
+
np.array(items.c.to_numpy()),
|
|
34
|
+
np.array(items.d.to_numpy()))
|
|
35
|
+
# convert test information into standard error
|
|
36
|
+
result = 1 / math.sqrt(result)
|
|
37
|
+
|
|
38
|
+
self.assertAlmostEqual(result, 1.444873, 3)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from unittest import TestCase
|
|
2
|
+
from adaptivetesting.models import TestItem, ItemPool
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
class TestLoadTestItems(TestCase):
|
|
6
|
+
def __init__(self, methodName = "runTest"):
|
|
7
|
+
super().__init__(methodName)
|
|
8
|
+
self.item1 = TestItem()
|
|
9
|
+
self.item1.a = 0.9
|
|
10
|
+
self.item1.b = 5
|
|
11
|
+
self.item1.c = 0.9
|
|
12
|
+
|
|
13
|
+
self.item2 = TestItem()
|
|
14
|
+
self.item2.a = 1.9
|
|
15
|
+
self.item2.b = 3
|
|
16
|
+
self.item2.c = 1.9
|
|
17
|
+
# List
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Dict
|
|
21
|
+
def test_load_test_items_from_dict_success(self):
|
|
22
|
+
source_dictionary: dict[str, list[float]] = {
|
|
23
|
+
"a": [0.9, 1.9],
|
|
24
|
+
"b": [5, 3],
|
|
25
|
+
"c": [0.9, 1.9],
|
|
26
|
+
"d": [1, 1]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
generated = ItemPool.load_from_dict(source_dictionary)
|
|
30
|
+
|
|
31
|
+
self.assertEqual([self.item1.as_dict(), self.item2.as_dict()], [i.as_dict() for i in generated.test_items])
|
|
32
|
+
|
|
33
|
+
def test_load_test_items_from_dict_error_none(self):
|
|
34
|
+
source_dictionary: dict[str, list[float]] = {
|
|
35
|
+
"a": [0.9, 1.9],
|
|
36
|
+
"b": [5, 3],
|
|
37
|
+
"c": [0.9, 1.9],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
with self.assertRaises(ValueError):
|
|
41
|
+
ItemPool.load_from_dict(source_dictionary)
|
|
42
|
+
|
|
43
|
+
def test_load_test_items_from_dict_error_length(self):
|
|
44
|
+
source_dictionary: dict[str, list[float]] = {
|
|
45
|
+
"a": [0.9, 1.9],
|
|
46
|
+
"b": [5, 3],
|
|
47
|
+
"c": [0.9, 1.9],
|
|
48
|
+
"d": [1]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
with self.assertRaises(ValueError):
|
|
52
|
+
ItemPool.load_from_dict(source_dictionary)
|
|
53
|
+
|
|
54
|
+
# Pandas DataFrame
|
|
55
|
+
def test_load_items_from_pandas_success(self):
|
|
56
|
+
dictionary = {
|
|
57
|
+
"a": [0.9, 1.9],
|
|
58
|
+
"b": [5, 3],
|
|
59
|
+
"c": [0.9, 1.9],
|
|
60
|
+
"d": [1, 1],
|
|
61
|
+
"simulated_responses": [1, 0]
|
|
62
|
+
}
|
|
63
|
+
df = pd.DataFrame(dictionary)
|
|
64
|
+
|
|
65
|
+
generated = ItemPool.load_from_dataframe(df)
|
|
66
|
+
|
|
67
|
+
# check that items are equal
|
|
68
|
+
self.assertEqual(
|
|
69
|
+
{
|
|
70
|
+
"a": 0.9,
|
|
71
|
+
"b": 5,
|
|
72
|
+
"c": 0.9,
|
|
73
|
+
"d": 1
|
|
74
|
+
},
|
|
75
|
+
generated.test_items[0].as_dict()
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
self.assertEqual(
|
|
79
|
+
{
|
|
80
|
+
"a": 1.9,
|
|
81
|
+
"b": 3,
|
|
82
|
+
"c": 1.9,
|
|
83
|
+
"d": 1
|
|
84
|
+
},
|
|
85
|
+
generated.test_items[1].as_dict()
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
self.assertEqual([1, 0], generated.simulated_responses)
|
|
89
|
+
|
|
90
|
+
def test_load_items_pandas_error_missing_column(self):
|
|
91
|
+
dictionary = {
|
|
92
|
+
"a": [0.9, 1.9],
|
|
93
|
+
"b": [5, 3],
|
|
94
|
+
"c": [0.9, 1.9]
|
|
95
|
+
}
|
|
96
|
+
df = pd.DataFrame(dictionary)
|
|
97
|
+
|
|
98
|
+
with self.assertRaises(ValueError):
|
|
99
|
+
ItemPool.load_from_dataframe(df)
|
|
100
|
+
|
|
101
|
+
def test_load_pandas_no_responses(self):
|
|
102
|
+
dictionary = {
|
|
103
|
+
"a": [0.9, 1.9],
|
|
104
|
+
"b": [5, 3],
|
|
105
|
+
"c": [0.9, 1.9],
|
|
106
|
+
"d": [1, 1]
|
|
107
|
+
}
|
|
108
|
+
df = pd.DataFrame(dictionary)
|
|
109
|
+
|
|
110
|
+
generated = ItemPool.load_from_dataframe(df)
|
|
111
|
+
|
|
112
|
+
self.assertIsNone(generated.simulated_responses)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from adaptivetesting.math.estimators import MLEstimator
|
|
3
|
+
from adaptivetesting.models import AlgorithmException, ItemPool
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
class TestMLE(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def test_ml_estimation(self):
|
|
9
|
+
response_pattern = [0, 1, 0]
|
|
10
|
+
difficulties = [0.7, 0.9, 0.6]
|
|
11
|
+
|
|
12
|
+
items = ItemPool.load_from_list(b=difficulties).test_items
|
|
13
|
+
|
|
14
|
+
estimator: MLEstimator = MLEstimator(
|
|
15
|
+
response_pattern,
|
|
16
|
+
items
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
result = estimator.get_estimation()
|
|
20
|
+
|
|
21
|
+
self.assertAlmostEqual(result, 0.0375530712, 2)
|
|
22
|
+
|
|
23
|
+
def test_one_item(self):
|
|
24
|
+
response = [0]
|
|
25
|
+
dif = [0.9]
|
|
26
|
+
ability = 0
|
|
27
|
+
|
|
28
|
+
items = ItemPool.load_from_list(dif).test_items
|
|
29
|
+
|
|
30
|
+
estimator = MLEstimator(response, items)
|
|
31
|
+
|
|
32
|
+
with self.assertRaises(AlgorithmException):
|
|
33
|
+
result = estimator.get_estimation()
|
|
34
|
+
print(f"Estimation Result {result}")
|
|
35
|
+
|
|
36
|
+
def test_eid(self):
|
|
37
|
+
response_pattern = [1, 0]
|
|
38
|
+
difficulties = [-1.603, 0.909]
|
|
39
|
+
|
|
40
|
+
items = ItemPool.load_from_list(difficulties).test_items
|
|
41
|
+
|
|
42
|
+
estimator = MLEstimator(response_pattern, items)
|
|
43
|
+
|
|
44
|
+
result = estimator.get_estimation()
|
|
45
|
+
|
|
46
|
+
self.assertAlmostEqual(round(result, 3), -0.347, places=5)
|
|
47
|
+
|
|
48
|
+
def test_catr_item_1_2(self):
|
|
49
|
+
response_pattern = [1, 0]
|
|
50
|
+
difficulties = [-2.1851, -0.2897194]
|
|
51
|
+
|
|
52
|
+
items = ItemPool.load_from_list(difficulties).test_items
|
|
53
|
+
estimator = MLEstimator(response_pattern, items)
|
|
54
|
+
|
|
55
|
+
result = estimator.get_estimation()
|
|
56
|
+
|
|
57
|
+
self.assertAlmostEqual(result, -1.237413, 3)
|
|
58
|
+
|
|
59
|
+
def test_4_pl_model(self):
|
|
60
|
+
items = pd.DataFrame({
|
|
61
|
+
"a": [1.32, 1.07, 0.84],
|
|
62
|
+
"b": [-0.63, 0.18, -0.84],
|
|
63
|
+
"c": [0.17, 0.10, 0.19],
|
|
64
|
+
"d": [0.87, 0.93, 1]
|
|
65
|
+
})
|
|
66
|
+
item_pool = ItemPool.load_from_dataframe(items)
|
|
67
|
+
|
|
68
|
+
response_pattern = [0,1,0]
|
|
69
|
+
estimator = MLEstimator(response_pattern, item_pool.test_items)
|
|
70
|
+
|
|
71
|
+
result = estimator.get_estimation()
|
|
72
|
+
|
|
73
|
+
self.assertAlmostEqual(result, -1.793097, places=2)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from adaptivetesting.implementations import PreTest
|
|
3
|
+
from adaptivetesting.models import TestItem
|
|
4
|
+
import jax.numpy as np
|
|
5
|
+
|
|
6
|
+
item0 = TestItem()
|
|
7
|
+
item0.b = 0
|
|
8
|
+
|
|
9
|
+
item1 = TestItem()
|
|
10
|
+
item1.b = 0.2
|
|
11
|
+
|
|
12
|
+
item2 = TestItem()
|
|
13
|
+
item2.b = 0.4
|
|
14
|
+
|
|
15
|
+
item3 = TestItem()
|
|
16
|
+
item3.b = 0.6
|
|
17
|
+
|
|
18
|
+
item4 = TestItem()
|
|
19
|
+
item4.b = 0.8
|
|
20
|
+
|
|
21
|
+
items = [item0, item1, item2, item3, item4]
|
|
22
|
+
|
|
23
|
+
test = PreTest(
|
|
24
|
+
items=items
|
|
25
|
+
)
|
|
26
|
+
# a seed is not set because
|
|
27
|
+
# the random selection should only allow one solution
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestPreTest(unittest.TestCase):
|
|
31
|
+
def test_quantile_calculation(self):
|
|
32
|
+
quantiles: np.ndarray = test.calculate_quantiles()
|
|
33
|
+
quantiles = quantiles.round(decimals=1)
|
|
34
|
+
self.assertListEqual(list(quantiles), [0.2, 0.4, 0.6])
|
|
35
|
+
|
|
36
|
+
def test_selection_random_item_quantile(self):
|
|
37
|
+
selected_items = test.select_random_item_quantile()
|
|
38
|
+
self.assertListEqual(selected_items,
|
|
39
|
+
[item1, item2, item3, item4])
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from typing import List
|
|
3
|
+
from adaptivetesting.math import standard_error
|
|
4
|
+
from adaptivetesting.models import ItemPool, TestItem
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
class TestStandardError(unittest.TestCase):
|
|
8
|
+
def test_dummy_items(self):
|
|
9
|
+
items = [0.7, 0.9, 0.6]
|
|
10
|
+
ability = 0
|
|
11
|
+
|
|
12
|
+
item_list: List[TestItem] = ItemPool.load_from_list(items).test_items
|
|
13
|
+
|
|
14
|
+
result = standard_error(item_list, ability)
|
|
15
|
+
|
|
16
|
+
self.assertAlmostEqual(result, 1.234664423, 3)
|
|
17
|
+
|
|
18
|
+
def test_eid_items(self):
|
|
19
|
+
items = [-1.603, 0.909]
|
|
20
|
+
ability = -0.347
|
|
21
|
+
|
|
22
|
+
item_list = ItemPool.load_from_list(items).test_items
|
|
23
|
+
|
|
24
|
+
result = standard_error(item_list, ability)
|
|
25
|
+
|
|
26
|
+
self.assertAlmostEqual(result, 1.702372, 3)
|
|
27
|
+
|
|
28
|
+
def test_calculation_4pl(self):
|
|
29
|
+
items = pd.DataFrame({
|
|
30
|
+
"a": [1.32, 1.07, 0.84],
|
|
31
|
+
"b": [-0.63, 0.18, -0.84],
|
|
32
|
+
"c": [0.17, 0.10, 0.19],
|
|
33
|
+
"d": [0.87, 0.93, 1]
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
item_pool = ItemPool.load_from_dataframe(items)
|
|
37
|
+
|
|
38
|
+
result = standard_error(item_pool.test_items, 0)
|
|
39
|
+
|
|
40
|
+
self.assertAlmostEqual(result, 1.444873, 3)
|
|
41
|
+
|
|
42
|
+
def test_calculation_bm(self):
|
|
43
|
+
items = pd.DataFrame({
|
|
44
|
+
"a": [1.32, 1.07, 0.84],
|
|
45
|
+
"b": [-0.63, 0.18, -0.84],
|
|
46
|
+
"c": [0.17, 0.10, 0.19],
|
|
47
|
+
"d": [0.87, 0.93, 1]
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
item_pool = ItemPool.load_from_dataframe(items)
|
|
51
|
+
|
|
52
|
+
result = standard_error(item_pool.test_items,
|
|
53
|
+
0,
|
|
54
|
+
estimator="BM",
|
|
55
|
+
sd=1)
|
|
56
|
+
|
|
57
|
+
self.assertAlmostEqual(result, 0.8222712, 3)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from adaptivetesting.math.item_selection import urrys_rule
|
|
3
|
+
from adaptivetesting.models import TestItem
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestUrrysRule(unittest.TestCase):
|
|
7
|
+
item1 = TestItem()
|
|
8
|
+
item1.b = 0.24
|
|
9
|
+
item1.id = 1
|
|
10
|
+
|
|
11
|
+
item2 = TestItem()
|
|
12
|
+
item2.b = 0.89
|
|
13
|
+
item2.id = 2
|
|
14
|
+
|
|
15
|
+
item3 = TestItem()
|
|
16
|
+
item3.b = -0.6
|
|
17
|
+
item3.id = 3
|
|
18
|
+
|
|
19
|
+
items = [item1, item2, item3]
|
|
20
|
+
|
|
21
|
+
def test_selection_when_0(self):
|
|
22
|
+
ability_level = 0
|
|
23
|
+
|
|
24
|
+
self.assertEqual(urrys_rule(self.items, ability_level).id, 1)
|
|
25
|
+
|
|
26
|
+
def test_selection_when_minus_0_5(self):
|
|
27
|
+
ability_level = -0.5
|
|
28
|
+
self.assertEqual(urrys_rule(self.items, ability_level).id, 3)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adaptivetesting
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: adaptivetesting is a Python package that can be used to simulate and evaluate custom CAT scenarios as well as implement them in real-world testing scenarios from a single codebase
|
|
5
|
+
Home-page: https://github.com/condecon/adaptivetesting
|
|
6
|
+
Author: Jonas Engicht
|
|
7
|
+
Author-email: jonas.engicht@uni-jena.de
|
|
8
|
+
Maintainer: Jonas Engicht
|
|
9
|
+
Maintainer-email: jonas.engicht@uni-jena.de
|
|
10
|
+
License: Mozilla Public License Version 2.0
|
|
11
|
+
Keywords: statistics,psychology,item-response-theory,computerized-adaptive-testing
|
|
12
|
+
Requires-Python: >3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: jax>=0.5.1
|
|
16
|
+
Requires-Dist: numpy>=1.20.0
|
|
17
|
+
Requires-Dist: scipy>=1.15.0
|
|
18
|
+
Requires-Dist: pandas>=2.2.0
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: author-email
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: home-page
|
|
24
|
+
Dynamic: keywords
|
|
25
|
+
Dynamic: license
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: maintainer
|
|
28
|
+
Dynamic: maintainer-email
|
|
29
|
+
Dynamic: requires-dist
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
Dynamic: summary
|
|
32
|
+
|
|
33
|
+
# adaptivetesting
|
|
34
|
+
[](https://github.com/condecon/adaptivetesting/actions/workflows/python-test.yml)
|
|
35
|
+
[](https://github.com/condecon/adaptivetesting/actions/workflows/publish.yml)
|
|
36
|
+
|
|
37
|
+
<img src="/docs/source/_static/logo.svg" style="width: 100%">
|
|
38
|
+
</img>
|
|
39
|
+
|
|
40
|
+
_adaptivetesting_ is a Python package for computerized adaptive
|
|
41
|
+
testing that can be used to simulate and implement custom adaptive tests
|
|
42
|
+
in real-world testing scenarios.
|
|
43
|
+
|
|
44
|
+
## Getting Started
|
|
45
|
+
|
|
46
|
+
Required Python version: >= 3.11 (other versions may work, but they are not officially supported)
|
|
47
|
+
|
|
48
|
+
``
|
|
49
|
+
pip install adaptivetesting
|
|
50
|
+
``
|
|
51
|
+
|
|
52
|
+
If you want to install the current development version,
|
|
53
|
+
you can do so by running the following command:
|
|
54
|
+
|
|
55
|
+
``
|
|
56
|
+
pip install git+https://github.com/condecon/adaptivetesting
|
|
57
|
+
``
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
- IRT-Models:
|
|
61
|
+
- 4PL
|
|
62
|
+
- simplified derivates (e.g., 3PL, Rasch model)
|
|
63
|
+
- Ability estimators:
|
|
64
|
+
- Maximum Likelihood Estimation
|
|
65
|
+
- Bayes Modal
|
|
66
|
+
- Item selection algorithm:
|
|
67
|
+
- Urry’s rule
|
|
68
|
+
- Stopping criteria:
|
|
69
|
+
- test length
|
|
70
|
+
- ability estimation standard error
|
|
71
|
+
- Test results output formats
|
|
72
|
+
- SQLITE
|
|
73
|
+
- Pickle
|
|
74
|
+
- Functions and wrappers for CAT simulations and application implementations
|
|
75
|
+
|
|
76
|
+
__Any functionality can be modified and extended.__
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
## Implementations
|
|
80
|
+
The package comes with two CAT implementations that are ready to use.
|
|
81
|
+
### Default implementation
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
### Semi-Adaptive implementation
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+
### Custom testing procedures
|
|
89
|
+
Custom testing procedures can be implemented by implementing
|
|
90
|
+
the abstract class ``AdaptiveTest``.
|
|
91
|
+
Any existing functionality can be overridden while still
|
|
92
|
+
retaining full compatibility with the packages' functionality.
|
|
93
|
+
For more information, please consult the documentation for the ``AdaptiveTest`` class.
|
|
94
|
+
|
|
95
|
+
## Package structure
|
|
96
|
+
| submodule | description |
|
|
97
|
+
|------------|-------------|
|
|
98
|
+
| data | data management and processing of test results |
|
|
99
|
+
| implementations | concrete implementations of the adaptive process, provides actual |
|
|
100
|
+
| math | mathematical utilities and functions, such as estimators, item selection, test information |
|
|
101
|
+
| models | data model definitions and structures used in the package |
|
|
102
|
+
| services | interfaces that concrete implementations inherit from |
|
|
103
|
+
| simulations | functions and classes used in CAT simulation |
|
|
104
|
+
| tests | Unit test for the entire package |
|
|
105
|
+
|
|
106
|
+
## Documentation
|
|
107
|
+
Extensive documentation is available in the source code,
|
|
108
|
+
but it can also be compiled to a webpage using sphinx.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
adaptivetesting/__init__.py,sha256=kPaeNHwRmZWn3Fs7GfbHy5fRiyHLsssdfhuT-niAgww,156
|
|
2
|
+
adaptivetesting/data/__init__.py,sha256=Cix5L1BaXQmKFuWpYOVeseQXOxNa5tVyVYsAoLiu2TA,88
|
|
3
|
+
adaptivetesting/data/__pickle_context.py,sha256=ZBOmOhUs8DmfRe3aeFLdU1iC1hR70x-Z5M8Lsw_rgCg,1800
|
|
4
|
+
adaptivetesting/data/__sqlite_context.py,sha256=9bKy6PEdcX9buGV17nyFJWNZvL8KMUNf9NnIBJrpNC0,2561
|
|
5
|
+
adaptivetesting/implementations/__default_implementation.py,sha256=FRvHPezfnD3ef1gWvthCCNly4aEYbx7WaVYNp8gkDuc,2477
|
|
6
|
+
adaptivetesting/implementations/__init__.py,sha256=9VEeChzufY-tpV0A0aRruhyxKIPpo--W3C5nxE8Y5Vs,154
|
|
7
|
+
adaptivetesting/implementations/__pre_test.py,sha256=W2Byp7UCnzYMg537nK0DeKTBTQb_qV7pbrvsFQ8MWM0,2816
|
|
8
|
+
adaptivetesting/implementations/__semi_implementation.py,sha256=jimHdQ9lfb_jOLv4bk_BOpCEhy_YOVKwuFREvpH83zk,4627
|
|
9
|
+
adaptivetesting/math/__init__.py,sha256=XlYMm5BHbG4JIQdEw85Y6HCnoYQGgVs9RR1z0cmcLVg,103
|
|
10
|
+
adaptivetesting/math/__standard_error.py,sha256=kA-wCRsbQKT26-kazMujDnX91kn0-V0JVixeSLBICCo,2234
|
|
11
|
+
adaptivetesting/math/__test_information.py,sha256=wr0VMisYYhQ2hVnXrHkVLbFHQc03Tbd92Nindhm4Vq0,1175
|
|
12
|
+
adaptivetesting/models/__adaptive_test.py,sha256=iPPlHsdXNH7-U5QrDrvqLYfDJrEhRYCLEWXsrDv1MWA,5646
|
|
13
|
+
adaptivetesting/models/__algorithm_exception.py,sha256=QtgzsLE7nfVJ-SkbASlHohvJ_7dZYPk35WkZCH1CaVc,125
|
|
14
|
+
adaptivetesting/models/__init__.py,sha256=NYJuqxiemQ34NHqsJ0Vscp1gHHUlNcIE4aQdcE8wTZU,267
|
|
15
|
+
adaptivetesting/models/__item_pool.py,sha256=OP90_paKU-euN-csCDi88v6WF-XC4Euit-znr7tQqqU,8157
|
|
16
|
+
adaptivetesting/models/__item_selection_exception.py,sha256=EWlGdfExvV_UV0hVG6W-DsVTY2Q5kDVyODAFcvgh1BQ,103
|
|
17
|
+
adaptivetesting/models/__test_item.py,sha256=fDH-uK7_5AvGwzVudSdsW0cJ9SGNt5SQx61st4CvR84,699
|
|
18
|
+
adaptivetesting/models/__test_result.py,sha256=SAMrh7WwBpY6ASG0RdGmeqvvsK7vP3AZEOFpwg0eDZs,624
|
|
19
|
+
adaptivetesting/services/__estimator_interface.py,sha256=nl2UQUV6nbGYp7vE1tyg0LjZFaRLnnMNxXPMpbyaQns,1375
|
|
20
|
+
adaptivetesting/services/__init__.py,sha256=kAPgNpSYGSIfDWTYZYAkRU4Mox5M63mYnVEI2IpoMqQ,97
|
|
21
|
+
adaptivetesting/services/__test_results_interface.py,sha256=8uLDXCk0VI8YC82XKLJODxsQgtlnTYf6AsvAMPyl_aQ,807
|
|
22
|
+
adaptivetesting/simulation/__init__.py,sha256=gbmEgpLmvPfbFL96wSIQ0RDAr5-pGg46pydp_aH0y1o,27
|
|
23
|
+
adaptivetesting/simulation/__simulation.py,sha256=xvt02auTJvXyzyhEZF5iK-4gJIuu7WNYCUZW1gE6-oI,2544
|
|
24
|
+
adaptivetesting/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
adaptivetesting/tests/test_adaptive_test.py,sha256=GhGr0rAPNH8eBLsHjTwrugPhPiykcG9iM3NzaT_u-RE,1817
|
|
26
|
+
adaptivetesting/tests/test_bayes_modal.py,sha256=0daQlba5-k3qRR5s_j9wQcc_ClDr-a6Cs7elT3YRhw8,915
|
|
27
|
+
adaptivetesting/tests/test_code_examples.py,sha256=ChJSD-ltoHyS6a6787F-l-Rd83RqOSzEzxfm_HUmjmE,566
|
|
28
|
+
adaptivetesting/tests/test_information_test.py,sha256=zahyit9_PU4msmizm_lXAPcVAaaNtL0s1Kte-VfM8Fc,1296
|
|
29
|
+
adaptivetesting/tests/test_load_test_items.py,sha256=3n2egcnisgEfq_qHSgxIPUl5z0DZQTEl6Mog2sbM6uI,3019
|
|
30
|
+
adaptivetesting/tests/test_mle.py,sha256=83y9F9EPZN3d_RkQXuhdCKnXNzNFPnPH_T2DdbUixu0,2089
|
|
31
|
+
adaptivetesting/tests/test_pretest.py,sha256=QTrc4cotGMW7bJuYlGXHL4JE065cwoi24ieLHVWwXEU,935
|
|
32
|
+
adaptivetesting/tests/test_standard_error.py,sha256=IMlQy_howIbBaiwzER_p35xtFIfgX3Un_nH5YotNOAY,1640
|
|
33
|
+
adaptivetesting/tests/test_urrys_rule.py,sha256=09Xf8FW0KXJG6YBInbB62iRa2OwquGfleQ0TcL8zwhA,654
|
|
34
|
+
adaptivetesting-1.0.0.dist-info/licenses/LICENSE,sha256=gCou0u4LSh7afsTTnOlU4HuOLgVCQ1tDpFylelKfxAk,16661
|
|
35
|
+
adaptivetesting-1.0.0.dist-info/METADATA,sha256=OBXM_3tVVYqpS31OBqiRNEWNFx90ej2Te7vC7i-uOls,3825
|
|
36
|
+
adaptivetesting-1.0.0.dist-info/WHEEL,sha256=tTnHoFhvKQHCh4jz3yCn0WPTYIy7wXx3CJtJ7SJGV7c,91
|
|
37
|
+
adaptivetesting-1.0.0.dist-info/top_level.txt,sha256=bUXMybDG9CsmK1_gbtA1AxqHlyy8eLbfcYol1airQ2E,16
|
|
38
|
+
adaptivetesting-1.0.0.dist-info/RECORD,,
|