adaptivetesting 1.1.0__tar.gz → 1.1.1__tar.gz
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-1.1.0 → adaptivetesting-1.1.1}/PKG-INFO +1 -1
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/__gen_response_pattern.py +19 -5
- adaptivetesting-1.1.1/adaptivetesting/tests/test_generate_response_pattern.py +106 -0
- adaptivetesting-1.1.1/examples/.gitignore +3 -0
- adaptivetesting-1.1.1/examples/simulation.py +134 -0
- adaptivetesting-1.1.0/examples/real_world.py → adaptivetesting-1.1.1/examples/testing.py +17 -14
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/pyproject.toml +7 -1
- adaptivetesting-1.1.1/uv.lock +5341 -0
- adaptivetesting-1.1.0/adaptivetesting/tests/test_generate_response_pattern.py +0 -59
- adaptivetesting-1.1.0/examples/basic.py +0 -39
- adaptivetesting-1.1.0/profiler/profile_bm_beta_simu.py +0 -49
- adaptivetesting-1.1.0/profiler/profile_bm_normal_simu.py +0 -48
- adaptivetesting-1.1.0/profiler/profile_ml_simu.py +0 -45
- adaptivetesting-1.1.0/profiler/profile_sim_pool.py +0 -42
- adaptivetesting-1.1.0/uv.lock +0 -1075
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.github/workflows/publish.yml +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.github/workflows/python-test.yml +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.gitignore +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.gitlab-ci.yml +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.python-version +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.vscode/extensions.json +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/.vscode/settings.json +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/BREAKING_CHANGES.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/LICENSE +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/README.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/data/__csv_context.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/data/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/data/__pickle_context.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__default_implementation.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__pre_test.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__semi_implementation.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/implementations/__test_assembler.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__bayes_modal_estimation.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__expect_a_posteriori.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__functions/__bayes.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__functions/__estimators.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__ml_estimation.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__prior.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/estimators/__test_information.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/item_selection/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/item_selection/__maximum_information_criterion.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/item_selection/__urrys_rule.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__adaptive_test.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__algorithm_exception.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__item_pool.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__item_selection_exception.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__misc.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__test_item.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/models/__test_result.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__estimator_interface.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__item_selection_protocol.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/services/__test_results_interface.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/simulation/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/simulation/__simulation.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/__init__.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_adaptive_test.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_bayes_modal.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_eap.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_information_test.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_item_selection.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_load_test_items.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_mle.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_pretest.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_simulation.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_standard_error.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/tests/test_test_assembler.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/Makefile +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/data.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/implementations.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/index.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/make.bat +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/math.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/models.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/readme.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/services.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/simulation.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/_static/logo.svg +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/_static/main.css +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/conf.py +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/data.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/implementations.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/index.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/math.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/models.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/services.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/simulation.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/source/tests.rst +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/docs/tests.md +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/images/default.svg +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/images/semi-adaptive.svg +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/mypy.ini +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/requirements.txt +0 -0
- {adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adaptivetesting
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
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
5
|
Project-URL: Homepage, https://github.com/condecon/adaptivetesting
|
|
6
6
|
Project-URL: Documentation, https://github.com/condecon/adaptivetesting/tree/main/docs
|
{adaptivetesting-1.1.0 → adaptivetesting-1.1.1}/adaptivetesting/math/__gen_response_pattern.py
RENAMED
|
@@ -17,7 +17,12 @@ def generate_response_pattern(ability: float,
|
|
|
17
17
|
Returns:
|
|
18
18
|
list[int]: response pattern
|
|
19
19
|
"""
|
|
20
|
-
#
|
|
20
|
+
# Set seed once at the beginning if provided
|
|
21
|
+
if seed is not None:
|
|
22
|
+
rng = np.random.RandomState(seed)
|
|
23
|
+
else:
|
|
24
|
+
rng = np.random.RandomState()
|
|
25
|
+
|
|
21
26
|
responses: list[int] = []
|
|
22
27
|
|
|
23
28
|
for item in items:
|
|
@@ -27,10 +32,19 @@ def generate_response_pattern(ability: float,
|
|
|
27
32
|
c=np.array(item.c),
|
|
28
33
|
d=np.array(item.d))
|
|
29
34
|
|
|
30
|
-
#
|
|
31
|
-
if
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
# Handle numpy scalar/array return properly
|
|
36
|
+
if hasattr(probability_of_success, 'item'):
|
|
37
|
+
prob_scalar = probability_of_success.item()
|
|
38
|
+
else:
|
|
39
|
+
prob_scalar = float(probability_of_success)
|
|
40
|
+
|
|
41
|
+
# Validate probability bounds
|
|
42
|
+
if not (0 <= prob_scalar <= 1):
|
|
43
|
+
raise ValueError(f"Invalid probability: {prob_scalar}. Must be between 0 and 1.")
|
|
44
|
+
|
|
45
|
+
# simulate response based on probability of success
|
|
46
|
+
random_val = rng.random_sample()
|
|
47
|
+
response = 1 if random_val < prob_scalar else 0
|
|
34
48
|
responses.append(response)
|
|
35
49
|
|
|
36
50
|
return responses
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
import unittest
|
|
3
|
+
import math
|
|
4
|
+
from adaptivetesting.models import ItemPool
|
|
5
|
+
from adaptivetesting.math.estimators import MLEstimator
|
|
6
|
+
from adaptivetesting.math import generate_response_pattern
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
source_dictionary = {"a": [1.0507,
|
|
10
|
+
0.9943,
|
|
11
|
+
0.9914,
|
|
12
|
+
1.2737,
|
|
13
|
+
0.9548,
|
|
14
|
+
1.3033,
|
|
15
|
+
0.6902,
|
|
16
|
+
1.1169,
|
|
17
|
+
1.0248,
|
|
18
|
+
1.0432,
|
|
19
|
+
1.0759,
|
|
20
|
+
0.8995,
|
|
21
|
+
0.9334,
|
|
22
|
+
0.7963,
|
|
23
|
+
0.7856,
|
|
24
|
+
1.0607,
|
|
25
|
+
1.0896,
|
|
26
|
+
1.0106,
|
|
27
|
+
1.1845,
|
|
28
|
+
1.41,
|
|
29
|
+
0.9018,
|
|
30
|
+
0.5382,
|
|
31
|
+
1.2011,
|
|
32
|
+
0.8582,
|
|
33
|
+
0.8624, 1.2051, 0.943, 0.7559, 1.0363, 0.9722, 1.0012, 1.0771, 0.9259, 1.1289, 0.9559, 1.0664, 1.2194, 1.087, 0.9348, 1.2298, 1.1987, 1.1097, 1.0477, 0.8744, 1.2721, 0.8799, 1.4375, 1.3065, 0.9529, 0.7947],
|
|
34
|
+
"b":
|
|
35
|
+
[-0.5605, -0.2302, 1.5587, 0.0705, 0.1293, 1.7151, 0.4609, -1.2651, -0.6869, -0.4457, 1.2241, 0.3598, 0.4008, 0.1107, -0.5558, 1.7869, 0.4979, -1.9666, 0.7014, -0.4728, -1.0678, -0.218, -1.026, -0.7289, -0.625, -1.6867, 0.8378, 0.1534, -1.1381, 1.2538, 0.4265, -0.2951, 0.8951, 0.8781, 0.8216, 0.6886, 0.5539, -0.0619, -0.306, -0.3805, -0.6947, -0.2079, -1.2654, 2.169, 1.208, -1.1231, -
|
|
36
|
+
0.4029, -0.4667, 0.78, -0.0834], "c": [0.0597, 0.2406, 0.1503, 0.1288, 0.1006, 0.2201, 0.091, 0.0721, 0.0427, 0.043, 0.1205, 0.0632, 0.0541, 0.1686, 0.0119, 0.1752, 0.088, 0.1022, 0.2052, 0.2297, 0.0706, 0.2403, 0.1821, 0.1716, 0.0132, 0.0988, 0.1195, 0.1401, 0.1746, 0.2289, 0.1546, 0.1071, 0.1355, 0.0146, 0.0652, 0.0993, 0.0494, 0.208, 0.0382, 0.2009, 0.1367, 0.1656, 0.0429, 0.1583, 0.078, 0.1811, 0.0997, 0.2423, 0.2418, 0.1817], "d": [0.8143, 0.8054, 0.8983, 0.8169, 0.8828, 0.9463, 0.792, 0.8511, 0.8679, 0.967, 0.9814, 0.9705, 0.9185, 0.9875, 0.8791, 0.8941, 0.8341, 0.8368, 0.755, 0.8757, 0.9678, 0.7516, 0.768, 0.7911, 0.9426, 0.9338, 0.993, 0.8666, 0.7686, 0.9122, 0.9396, 0.7843, 0.8491, 0.8062, 0.7645, 0.849, 0.7662, 0.8065, 0.7637, 0.9176, 0.8244, 0.7752, 0.768, 0.9701, 0.9386, 0.9542, 0.9955, 0.7759, 0.7748, 0.9497]}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestGenerateResponsePattern(unittest.TestCase):
|
|
40
|
+
def test_compare_generation_to_estimation(self):
|
|
41
|
+
item_pool = ItemPool.load_from_dict(source_dictionary)
|
|
42
|
+
|
|
43
|
+
# results
|
|
44
|
+
results = []
|
|
45
|
+
|
|
46
|
+
for i in range(100):
|
|
47
|
+
# generate pattern
|
|
48
|
+
responses = generate_response_pattern(0, item_pool.test_items, seed=i)
|
|
49
|
+
|
|
50
|
+
# estimate
|
|
51
|
+
estimator = MLEstimator(responses, item_pool.test_items)
|
|
52
|
+
estimation = estimator.get_estimation()
|
|
53
|
+
results.append(estimation)
|
|
54
|
+
|
|
55
|
+
mean = sum(results) / len(results)
|
|
56
|
+
print(mean)
|
|
57
|
+
self.assertAlmostEqual(mean, 0, delta=0.3)
|
|
58
|
+
|
|
59
|
+
def test_debug_probabilities(self):
|
|
60
|
+
from adaptivetesting.math.estimators.__functions.__estimators import probability_y1
|
|
61
|
+
import numpy as np
|
|
62
|
+
|
|
63
|
+
item_pool = ItemPool.load_from_dict(source_dictionary)
|
|
64
|
+
|
|
65
|
+
print("First 5 items - Expected probabilities for ability=0:")
|
|
66
|
+
for i, item in enumerate(item_pool.test_items[:5]):
|
|
67
|
+
prob = probability_y1(mu=np.array(0.0),
|
|
68
|
+
a=np.array(item.a),
|
|
69
|
+
b=np.array(item.b),
|
|
70
|
+
c=np.array(item.c),
|
|
71
|
+
d=np.array(item.d))
|
|
72
|
+
print(f"Item {i}: a={item.a:.3f}, b={item.b:.3f}, c={item.c:.3f}, d={item.d:.3f} -> P={float(prob):.3f}")
|
|
73
|
+
|
|
74
|
+
def test_calculate_expected_vs_actual(self):
|
|
75
|
+
from adaptivetesting.math.estimators.__functions.__estimators import probability_y1
|
|
76
|
+
import numpy as np
|
|
77
|
+
|
|
78
|
+
item_pool = ItemPool.load_from_dict(source_dictionary)
|
|
79
|
+
|
|
80
|
+
# Calculate expected probabilities for all items
|
|
81
|
+
expected_probs = []
|
|
82
|
+
for item in item_pool.test_items:
|
|
83
|
+
prob = probability_y1(mu=np.array(0.0),
|
|
84
|
+
a=np.array(item.a),
|
|
85
|
+
b=np.array(item.b),
|
|
86
|
+
c=np.array(item.c),
|
|
87
|
+
d=np.array(item.d))
|
|
88
|
+
expected_probs.append(float(prob))
|
|
89
|
+
|
|
90
|
+
expected_total = sum(expected_probs)
|
|
91
|
+
expected_percentage = expected_total / len(expected_probs) * 100
|
|
92
|
+
|
|
93
|
+
percentage_results = []
|
|
94
|
+
for i in range(100):
|
|
95
|
+
# Generate actual responses
|
|
96
|
+
responses = generate_response_pattern(0, item_pool.test_items, seed=i)
|
|
97
|
+
actual_correct = sum(responses)
|
|
98
|
+
actual_percentage = actual_correct / len(responses) * 100
|
|
99
|
+
percentage_results.append(actual_percentage)
|
|
100
|
+
|
|
101
|
+
print(f"Expected percentage correct: {expected_percentage:.1f}%")
|
|
102
|
+
print(f"Actual percentage correct: {actual_percentage:.1f}%")
|
|
103
|
+
|
|
104
|
+
# The difference should be within reasonable bounds for random sampling
|
|
105
|
+
# With 50 items, we expect some variation
|
|
106
|
+
self.assertAlmostEqual(actual_percentage, expected_percentage, delta=3)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
import pandas as pd
|
|
3
|
+
previc_item_pool = pd.read_csv("item_pool.csv")
|
|
4
|
+
previc_item_pool.head()
|
|
5
|
+
|
|
6
|
+
from adaptivetesting.models import ItemPool
|
|
7
|
+
item_pool = ItemPool.load_from_list(
|
|
8
|
+
b=previc_item_pool["Difficulty"]
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from scipy.stats import norm
|
|
12
|
+
from numpy.random import seed
|
|
13
|
+
seed(1234)
|
|
14
|
+
theta_samples = norm.rvs(loc=0, scale=2, size=100)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from adaptivetesting.implementations import TestAssembler
|
|
18
|
+
from adaptivetesting.simulation import SimulationPool
|
|
19
|
+
from adaptivetesting.models import ResultOutputFormat, StoppingCriterion
|
|
20
|
+
from adaptivetesting.math.item_selection import maximum_information_criterion
|
|
21
|
+
from adaptivetesting.math.estimators import BayesModal, NormalPrior
|
|
22
|
+
|
|
23
|
+
tests = [
|
|
24
|
+
TestAssembler(
|
|
25
|
+
item_pool=item_pool,
|
|
26
|
+
simulation_id="example_bm",
|
|
27
|
+
participant_id=str(index),
|
|
28
|
+
ability_estimator=BayesModal,
|
|
29
|
+
estimator_args={
|
|
30
|
+
"prior": NormalPrior(0,1),
|
|
31
|
+
"optimization_interval":(-10, 10)
|
|
32
|
+
},
|
|
33
|
+
item_selector=maximum_information_criterion,
|
|
34
|
+
true_ability_level=theta,
|
|
35
|
+
simulation=True,
|
|
36
|
+
seed=index
|
|
37
|
+
)
|
|
38
|
+
for index, theta in enumerate(theta_samples)
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
sim_pool = SimulationPool(
|
|
42
|
+
adaptive_tests=tests,
|
|
43
|
+
test_result_output=ResultOutputFormat.CSV,
|
|
44
|
+
criterion=StoppingCriterion.SE,
|
|
45
|
+
value=0.4
|
|
46
|
+
)
|
|
47
|
+
sim_pool.start()
|
|
48
|
+
|
|
49
|
+
import os
|
|
50
|
+
|
|
51
|
+
dfs = []
|
|
52
|
+
for i in range(100):
|
|
53
|
+
filename = f"data/example_bm/{i}.csv"
|
|
54
|
+
if os.path.exists(filename):
|
|
55
|
+
try:
|
|
56
|
+
df = pd.read_csv(filename)
|
|
57
|
+
# Get the last row of every file
|
|
58
|
+
last_row = df.tail(1)
|
|
59
|
+
dfs.append(last_row)
|
|
60
|
+
# If there is an error, skip the file
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
results_df = pd.concat(dfs, ignore_index=True)
|
|
64
|
+
mse = ((results_df["ability_estimation"] - results_df["true_ability_level"]) ** 2).mean()
|
|
65
|
+
print(f"MSE of ability estimates: {mse:.4f}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
import matplotlib.pyplot as plt
|
|
69
|
+
|
|
70
|
+
fig, ax = plt.subplots()
|
|
71
|
+
ax.scatter(results_df["true_ability_level"],
|
|
72
|
+
results_df["ability_estimation"],
|
|
73
|
+
label="BM")
|
|
74
|
+
ax.plot(results_df["ability_estimation"],
|
|
75
|
+
results_df["ability_estimation"],
|
|
76
|
+
color="black")
|
|
77
|
+
ax.set_xlabel("True ability level")
|
|
78
|
+
ax.set_ylabel("Estimated ability level")
|
|
79
|
+
fig.savefig("sim_results_bm.pdf")
|
|
80
|
+
|
|
81
|
+
# other example
|
|
82
|
+
from adaptivetesting.math.estimators import ExpectedAPosteriori, CustomPrior
|
|
83
|
+
from scipy.stats.distributions import t
|
|
84
|
+
|
|
85
|
+
tests = [
|
|
86
|
+
TestAssembler(
|
|
87
|
+
item_pool=item_pool,
|
|
88
|
+
simulation_id="example_eap",
|
|
89
|
+
participant_id=str(index),
|
|
90
|
+
ability_estimator=ExpectedAPosteriori,
|
|
91
|
+
estimator_args={
|
|
92
|
+
"prior": CustomPrior(t, 3),
|
|
93
|
+
"optimization_interval":(-10, 10)
|
|
94
|
+
},
|
|
95
|
+
item_selector=maximum_information_criterion,
|
|
96
|
+
true_ability_level=theta,
|
|
97
|
+
simulation=True,
|
|
98
|
+
seed=index
|
|
99
|
+
)
|
|
100
|
+
for index, theta in enumerate(theta_samples)
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
sim_pool = SimulationPool(
|
|
104
|
+
adaptive_tests=tests,
|
|
105
|
+
test_result_output=ResultOutputFormat.CSV,
|
|
106
|
+
criterion=StoppingCriterion.SE,
|
|
107
|
+
value=0.4
|
|
108
|
+
)
|
|
109
|
+
sim_pool.start()
|
|
110
|
+
|
|
111
|
+
dfs = []
|
|
112
|
+
for i in range(100):
|
|
113
|
+
filename = f"data/example_eap/{i}.csv"
|
|
114
|
+
if os.path.exists(filename):
|
|
115
|
+
try:
|
|
116
|
+
df = pd.read_csv(filename)
|
|
117
|
+
# Get the last row of every file
|
|
118
|
+
last_row = df.tail(1)
|
|
119
|
+
dfs.append(last_row)
|
|
120
|
+
# If there is an error, skip the file
|
|
121
|
+
except Exception:
|
|
122
|
+
pass
|
|
123
|
+
results_df = pd.concat(dfs, ignore_index=True)
|
|
124
|
+
mse = ((results_df["ability_estimation"] - results_df["true_ability_level"]) ** 2).mean()
|
|
125
|
+
print(f"MSE of ability estimates: {mse:.4f}")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
ax.scatter(results_df["true_ability_level"],
|
|
129
|
+
results_df["ability_estimation"],
|
|
130
|
+
label="EAP")
|
|
131
|
+
ax.set_xlabel("True ability level")
|
|
132
|
+
ax.set_ylabel("Estimated ability level")
|
|
133
|
+
ax.legend()
|
|
134
|
+
fig.savefig("sim_results_eap.pdf")
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
# setup item pool
|
|
3
|
+
import pandas as pd
|
|
4
|
+
previc_item_pool = pd.read_csv("item_pool.csv")
|
|
5
|
+
previc_item_pool.head()
|
|
6
|
+
|
|
7
|
+
from adaptivetesting.models import ItemPool
|
|
8
|
+
item_pool = ItemPool.load_from_list(
|
|
9
|
+
b=previc_item_pool["Difficulty"]
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# import psychopy
|
|
1
13
|
from psychopy import visual, event
|
|
2
14
|
from psychopy.hardware import keyboard
|
|
3
15
|
from adaptivetesting.implementations import TestAssembler
|
|
4
16
|
from adaptivetesting.models import AdaptiveTest, ItemPool, TestItem
|
|
5
17
|
from adaptivetesting.data import CSVContext
|
|
6
|
-
from adaptivetesting.math.estimators import
|
|
18
|
+
from adaptivetesting.math.estimators import ExpectedAPosteriori, CustomPrior
|
|
7
19
|
from adaptivetesting.math.item_selection import maximum_information_criterion
|
|
8
20
|
from scipy.stats import t
|
|
9
21
|
import pandas as pd
|
|
10
22
|
|
|
11
|
-
|
|
12
|
-
# Create item pool from DataFrame
|
|
13
|
-
items_data = pd.DataFrame({
|
|
14
|
-
"a": [1.32, 1.07, 0.84, 1.19, 0.95], # discrimination
|
|
15
|
-
"b": [-0.63, 0.18, -0.84, 0.41, -0.25], # difficulty
|
|
16
|
-
"c": [0.17, 0.10, 0.19, 0.15, 0.12], # guessing
|
|
17
|
-
"d": [0.87, 0.93, 1.0, 0.89, 0.94] # upper asymptote
|
|
18
|
-
})
|
|
19
|
-
item_pool = ItemPool.load_from_dataframe(items_data)
|
|
20
|
-
|
|
21
23
|
# Create adaptive test
|
|
22
24
|
adaptive_test: AdaptiveTest = TestAssembler(
|
|
23
25
|
item_pool=item_pool,
|
|
24
26
|
simulation_id="example",
|
|
25
27
|
participant_id="dummy",
|
|
26
|
-
ability_estimator=
|
|
28
|
+
ability_estimator=ExpectedAPosteriori,
|
|
27
29
|
estimator_args={
|
|
28
|
-
"prior": CustomPrior(t,
|
|
30
|
+
"prior": CustomPrior(t, 3),
|
|
29
31
|
"optimization_interval":(-10, 10)
|
|
30
32
|
},
|
|
31
33
|
item_selector=maximum_information_criterion,
|
|
@@ -51,7 +53,8 @@ keyboard.Keyboard()
|
|
|
51
53
|
def get_response(item: TestItem) -> int:
|
|
52
54
|
# get index
|
|
53
55
|
item_difficulty: float = item.b
|
|
54
|
-
|
|
56
|
+
# select corresponding word from item pool data frame
|
|
57
|
+
stimuli: str = previc_item_pool.loc[previc_item_pool["Difficulty"] == item_difficulty, "word"].values[0]
|
|
55
58
|
|
|
56
59
|
# create text box and display stimulus
|
|
57
60
|
text_box = visual.TextBox2(win=win,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name="adaptivetesting"
|
|
3
|
-
version="1.1.
|
|
3
|
+
version="1.1.1"
|
|
4
4
|
author="Jonas Engicht"
|
|
5
5
|
author_email="jonas.engicht@uni-jena.de"
|
|
6
6
|
description="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"
|
|
@@ -37,6 +37,12 @@ dev = [
|
|
|
37
37
|
"sphinx-markdown-builder>=0.6.8",
|
|
38
38
|
"types-tqdm>=4.67.0.20250516",
|
|
39
39
|
]
|
|
40
|
+
plot = [
|
|
41
|
+
"matplotlib>=3.10.5",
|
|
42
|
+
]
|
|
43
|
+
test = [
|
|
44
|
+
"psychopy>=2025.1.1",
|
|
45
|
+
]
|
|
40
46
|
|
|
41
47
|
[tool.uv.sources]
|
|
42
48
|
adaptivetesting = { workspace = true }
|