pynsn 1.0.7.dev6__tar.gz → 1.0.9__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.
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/PKG-INFO +3 -4
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/__init__.py +1 -1
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/properties.py +1 -1
- pynsn-1.0.9/pynsn/collections/__init__.py +2 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/_coll_stim.py +22 -12
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/_coll_stim_pairs.py +19 -3
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/fit.py +9 -4
- pynsn-1.0.9/pynsn/geometry.py +51 -0
- pynsn-1.0.9/pynsn/plots/__init__.py +3 -0
- pynsn-1.0.9/pynsn/plots/_collection_plots.py +93 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pyproject.toml +2 -1
- pynsn-1.0.7.dev6/pynsn/analysis/__init__.py +0 -1
- pynsn-1.0.7.dev6/pynsn/analysis/_collection_stats.py +0 -51
- pynsn-1.0.7.dev6/pynsn/collections/__init__.py +0 -2
- pynsn-1.0.7.dev6/pynsn/matrix/__init__.py +0 -5
- pynsn-1.0.7.dev6/pynsn/matrix/matrix.py +0 -54
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/.gitignore +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/.readthedocs.yaml +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/LICENSE +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/Makefile +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/README.md +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/euro_flag_demo.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/index.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/make_object_arrays_demo.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/pynsn_demo.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/Makefile +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/_static/euro_flag_demo.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/_static/make_object_arrays_demo.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/_static/pynsn_demo.html +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/api.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/arrays.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/check_api_reference.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/colours.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/conf.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/distributions.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/examples.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/index.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/install.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/make.bat +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/misc.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/modules.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/random_arrays.rst +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/Makefile +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/euro_flag_demo.ipynb +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/eurostar.png +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/further_demo.ipynb +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/make_object_arrays_demo.ipynb +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/pynsn_demo.ipynb +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_misc.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/__init__.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/abc_shapes.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/circ_shapes.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/colour.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/ellipse_geometry.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/shapes.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/__init__.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/convex_hull.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/nsn_stimulus.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/shape_array.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/stimulus_colours.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/stimulus_pair.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/target_area.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/_abc_coll.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/defaults.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/exceptions.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/fit.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/__init__.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/_base.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/expyriment_stimulus.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/pil_image.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/pygame_surface.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/svg_file.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/__init__.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_distributions.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_distributions_2d.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_factory.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_random_shape.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_rng.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/typing.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/testing.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/Dockerfile-py310 +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/Dockerfile-py38 +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/__init__.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/shapes_test_picture.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_dot_array.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_images.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_rect_array.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_save_load.py +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tox.ini +0 -0
- {pynsn-1.0.7.dev6 → pynsn-1.0.9}/visual_testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pynsn
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Creating Non-Symbolic Number Displays
|
|
5
5
|
Author-email: Oliver Lindemann <lindemann@essb.eur.nl>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -8,12 +8,12 @@ Description-Content-Type: text/markdown
|
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Requires-Dist: numpy>=2.1.3
|
|
10
10
|
Requires-Dist: pandas>=2.2
|
|
11
|
+
Requires-Dist: scipy>=1.14
|
|
12
|
+
Requires-Dist: matplotlib>=3.9
|
|
11
13
|
Requires-Dist: shapely>=2.0
|
|
12
14
|
Requires-Dist: orjson>=3.10
|
|
13
15
|
Requires-Dist: svgwrite>=1.4
|
|
14
16
|
Requires-Dist: Pillow>=11.0
|
|
15
|
-
Requires-Dist: scipy>=1.14 ; extra == "analysis"
|
|
16
|
-
Requires-Dist: matplotlib>=3.9 ; extra == "analysis"
|
|
17
17
|
Requires-Dist: Sphinx>=5.1 ; extra == "doc"
|
|
18
18
|
Requires-Dist: sphinx-autodoc-typehints ; extra == "doc"
|
|
19
19
|
Requires-Dist: sphinx-rtd-theme ; extra == "doc"
|
|
@@ -21,7 +21,6 @@ Requires-Dist: pygame>=2.5 ; extra == "pygame"
|
|
|
21
21
|
Requires-Dist: pytest >=2.7.3 ; extra == "test"
|
|
22
22
|
Project-URL: Documentation, https://github.com/lindemann09/PyNSN
|
|
23
23
|
Project-URL: Source, https://github.com/lindemann09/PyNSN
|
|
24
|
-
Provides-Extra: analysis
|
|
25
24
|
Provides-Extra: doc
|
|
26
25
|
Provides-Extra: pygame
|
|
27
26
|
Provides-Extra: test
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# pylint: disable=C0413
|
|
3
3
|
|
|
4
4
|
__author__ = "Oliver Lindemann <lindemann@cognitive-psychology.eu>"
|
|
5
|
-
__version__ = '1.0.
|
|
5
|
+
__version__ = '1.0.9'
|
|
6
6
|
|
|
7
7
|
from sys import version_info as _python_version_info
|
|
8
8
|
from ._misc import is_interactive_mode as _is_interactive_mode
|
|
@@ -9,19 +9,17 @@ import pandas as pd
|
|
|
9
9
|
|
|
10
10
|
from .. import _misc, defaults
|
|
11
11
|
from .._stimulus import NSNStimulus
|
|
12
|
+
from .._stimulus.properties import VPList, ensure_vp
|
|
12
13
|
from ._abc_coll import AbstractCollection, ListNSNStimuli
|
|
13
14
|
|
|
14
|
-
# FIXME not yet tested
|
|
15
|
-
# TODO NSNStimuli have no names yet
|
|
16
|
-
|
|
17
15
|
|
|
18
16
|
class CollectionStimuli(AbstractCollection):
|
|
19
17
|
"""Collection of NSNNumPairs"""
|
|
20
18
|
|
|
21
|
-
def __init__(self, lst:
|
|
19
|
+
def __init__(self, lst: None | ListNSNStimuli = None) -> None:
|
|
22
20
|
|
|
23
21
|
if isinstance(lst, tp.List):
|
|
24
|
-
for x in lst:
|
|
22
|
+
for x in lst: # type check
|
|
25
23
|
if not isinstance(x, NSNStimulus):
|
|
26
24
|
raise RuntimeError(
|
|
27
25
|
f"lst must be a list of NSNStimulus and not {type(x)}")
|
|
@@ -35,7 +33,7 @@ class CollectionStimuli(AbstractCollection):
|
|
|
35
33
|
"""
|
|
36
34
|
|
|
37
35
|
self.stimuli.append(stim)
|
|
38
|
-
self.
|
|
36
|
+
self.reset_properties()
|
|
39
37
|
|
|
40
38
|
def save(self, path: tp.Union[str, Path], zipped: bool = True):
|
|
41
39
|
"""Save the collection as json files organized in subfolder"""
|
|
@@ -76,8 +74,8 @@ class CollectionStimuli(AbstractCollection):
|
|
|
76
74
|
rtn.append(NSNStimulus.from_dict(d))
|
|
77
75
|
return rtn
|
|
78
76
|
|
|
79
|
-
def
|
|
80
|
-
"""
|
|
77
|
+
def reset_properties(self):
|
|
78
|
+
"""resets visual properties
|
|
81
79
|
|
|
82
80
|
If the array `CollectionStimuli.stimuli` have been changed directly,
|
|
83
81
|
the method needs to be called to ensure get valid property data
|
|
@@ -103,7 +101,19 @@ class CollectionStimuli(AbstractCollection):
|
|
|
103
101
|
|
|
104
102
|
if len(self._prop_df) != len(self.stimuli):
|
|
105
103
|
self._calc_properties()
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return
|
|
104
|
+
|
|
105
|
+
rtn = self._prop_df.copy()
|
|
106
|
+
rtn["names"] = [x.name for x in self.stimuli]
|
|
107
|
+
return rtn
|
|
108
|
+
|
|
109
|
+
def corr(self, properties: None | VPList = None) -> pd.DataFrame:
|
|
110
|
+
"""Pairwise Pearson correlation between visual properties"""
|
|
111
|
+
if len(self._prop_df) != len(self.stimuli):
|
|
112
|
+
self._calc_properties()
|
|
113
|
+
|
|
114
|
+
df = self._prop_df
|
|
115
|
+
if properties is not None:
|
|
116
|
+
prop_names = [ensure_vp(p).name for p in properties]
|
|
117
|
+
df = df[prop_names]
|
|
118
|
+
|
|
119
|
+
return df.corr()
|
|
@@ -11,6 +11,7 @@ from .. import _misc, defaults
|
|
|
11
11
|
from .._stimulus import NSNStimulus, NSNStimulusPair
|
|
12
12
|
from .._stimulus.properties import ensure_vp, VPOrList
|
|
13
13
|
from ._abc_coll import AbstractCollection, ListNSNStimPairs
|
|
14
|
+
from ._coll_stim import CollectionStimuli
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class CollectionStimulusPairs(AbstractCollection):
|
|
@@ -19,7 +20,7 @@ class CollectionStimulusPairs(AbstractCollection):
|
|
|
19
20
|
def __init__(self, lst: tp.Union[None, ListNSNStimPairs] = None) -> None:
|
|
20
21
|
|
|
21
22
|
if isinstance(lst, tp.List):
|
|
22
|
-
for x in lst:
|
|
23
|
+
for x in lst: # type check
|
|
23
24
|
if not isinstance(x, NSNStimulusPair):
|
|
24
25
|
raise RuntimeError(
|
|
25
26
|
f"lst must be a list of NSNStimulusPairs and not {type(x)}")
|
|
@@ -35,7 +36,7 @@ class CollectionStimulusPairs(AbstractCollection):
|
|
|
35
36
|
"""
|
|
36
37
|
|
|
37
38
|
self.pairs.append(NSNStimulusPair(stim_a, stim_b, name))
|
|
38
|
-
self.
|
|
39
|
+
self.reset_properties()
|
|
39
40
|
|
|
40
41
|
def save(self, path: tp.Union[str, Path], zipped: bool = True):
|
|
41
42
|
"""Save the collection as json files organized in subfolder"""
|
|
@@ -81,7 +82,7 @@ class CollectionStimulusPairs(AbstractCollection):
|
|
|
81
82
|
name=c["name"])
|
|
82
83
|
return rtn
|
|
83
84
|
|
|
84
|
-
def
|
|
85
|
+
def reset_properties(self):
|
|
85
86
|
"""reset dataframe of visual properties
|
|
86
87
|
|
|
87
88
|
If the array `CollectionStimulusPairs.pairs` have been changed directly,
|
|
@@ -158,3 +159,18 @@ class CollectionStimulusPairs(AbstractCollection):
|
|
|
158
159
|
cols = ensure_vp(props).name
|
|
159
160
|
|
|
160
161
|
return self._prop_df_a[cols] / self._prop_df_b[cols]
|
|
162
|
+
|
|
163
|
+
def collection_stimuli(self, stim_id: str) -> CollectionStimuli:
|
|
164
|
+
"""
|
|
165
|
+
stim_id : {'a', 'b'}
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
if stim_id == "a":
|
|
169
|
+
rtn = [pair.stim_a for pair in self.pairs]
|
|
170
|
+
elif stim_id == "b":
|
|
171
|
+
rtn = [pair.stim_b for pair in self.pairs]
|
|
172
|
+
else:
|
|
173
|
+
raise ValueError(f"Unknown stimulus id '{stim_id}'. " +
|
|
174
|
+
"Must be either 'a' or 'b'")
|
|
175
|
+
|
|
176
|
+
return CollectionStimuli(rtn)
|
|
@@ -15,6 +15,7 @@ def property_ratio_correlation(collection: CollectionStimulusPairs,
|
|
|
15
15
|
prop_a: _tp.Union[str, _VP],
|
|
16
16
|
prop_b: _tp.Union[None, str, _VP] = None,
|
|
17
17
|
max_corr: float = 0.01,
|
|
18
|
+
adapt_stim: str = "both",
|
|
18
19
|
feedback: bool = True) -> _tp.Union[_tp.Tuple[float, float], float]:
|
|
19
20
|
|
|
20
21
|
prop_a = _ensure_vp(prop_a)
|
|
@@ -30,13 +31,17 @@ def property_ratio_correlation(collection: CollectionStimulusPairs,
|
|
|
30
31
|
if feedback:
|
|
31
32
|
_sys.stdout.write(
|
|
32
33
|
f"fitting {i+1}/{n} {sp.name} \r")
|
|
33
|
-
_stim_fit.property_ratio(
|
|
34
|
+
_stim_fit.property_ratio(
|
|
35
|
+
sp, prop_a, rnd_values[i, 0], adapt_stim=adapt_stim)
|
|
36
|
+
|
|
34
37
|
if isinstance(prop_b, _VP):
|
|
35
|
-
_stim_fit.property_ratio(
|
|
38
|
+
_stim_fit.property_ratio(
|
|
39
|
+
sp, prop_b, rnd_values[i, 1], adapt_stim=adapt_stim)
|
|
40
|
+
|
|
36
41
|
if feedback:
|
|
37
42
|
print(" "*70)
|
|
38
43
|
|
|
39
|
-
collection.
|
|
44
|
+
collection.reset_properties()
|
|
40
45
|
return target_correlations
|
|
41
46
|
|
|
42
47
|
|
|
@@ -68,7 +73,7 @@ def property_difference_correlation(collection: CollectionStimulusPairs,
|
|
|
68
73
|
if feedback:
|
|
69
74
|
print(" "*70)
|
|
70
75
|
|
|
71
|
-
collection.
|
|
76
|
+
collection.reset_properties()
|
|
72
77
|
|
|
73
78
|
return target_correlations
|
|
74
79
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
from copy import deepcopy as _deepcopy
|
|
3
|
+
|
|
4
|
+
import numpy as _np
|
|
5
|
+
from numpy.typing import NDArray as _NDArray
|
|
6
|
+
|
|
7
|
+
from ._stimulus.nsn_stimulus import NSNStimulus as _NSNStimulus
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def distances(shape_array: _NSNStimulus) -> _NDArray:
|
|
11
|
+
"""Matrix with the distances between all shapes in the array"""
|
|
12
|
+
return _relation_matrix(shape_array, what=0)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def dwithin(shape_array: _NSNStimulus, distance: float) -> _NDArray:
|
|
16
|
+
"""Matrix with the booleans indicates dwithin between all shapes in the array"""
|
|
17
|
+
return _relation_matrix(shape_array, what=1, para=distance)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def overlaps(shape_array: _NSNStimulus, min_distance: float | None = None) -> _NDArray:
|
|
21
|
+
if min_distance is None:
|
|
22
|
+
min_distance = shape_array.min_distance
|
|
23
|
+
else:
|
|
24
|
+
min_distance = 0
|
|
25
|
+
|
|
26
|
+
return dwithin(shape_array, distance=min_distance)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _relation_matrix(arr: _NSNStimulus, what: int, para: float = 0) -> _NDArray:
|
|
30
|
+
"""helper function returning the relation between polygons
|
|
31
|
+
0 = distance
|
|
32
|
+
1 = dwithin
|
|
33
|
+
"""
|
|
34
|
+
arr = _deepcopy(arr)
|
|
35
|
+
l = arr.n_shapes
|
|
36
|
+
rtn = _np.full((l, l), _np.nan)
|
|
37
|
+
for x in reversed(range(l)):
|
|
38
|
+
shape = arr.shape_pop(x)
|
|
39
|
+
if what == 0:
|
|
40
|
+
y = arr.distances(shape)
|
|
41
|
+
elif what == 1:
|
|
42
|
+
y = arr.dwithin(shape=shape, distance=para)
|
|
43
|
+
else:
|
|
44
|
+
raise RuntimeError("unknown function")
|
|
45
|
+
|
|
46
|
+
rtn[x, 0:x] = y
|
|
47
|
+
|
|
48
|
+
# make symmetric
|
|
49
|
+
i_lower = _np.triu_indices(l, 1)
|
|
50
|
+
rtn[i_lower] = rtn.T[i_lower]
|
|
51
|
+
return rtn
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from math import ceil
|
|
2
|
+
from typing import List, Tuple, Union
|
|
3
|
+
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from scipy.stats import linregress
|
|
7
|
+
|
|
8
|
+
from .._stimulus.properties import VP, ensure_vp, VPOrList
|
|
9
|
+
from ..collections._coll_stim_pairs import CollectionStimulusPairs, CollectionStimuli
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def property_regression(stimuli: CollectionStimuli,
|
|
13
|
+
dv: Union[str, VP],
|
|
14
|
+
iv: VPOrList,
|
|
15
|
+
figsize: Tuple[float, float] = (10, 8)):
|
|
16
|
+
|
|
17
|
+
if isinstance(iv, List):
|
|
18
|
+
iv_prop = [ensure_vp(p) for p in iv]
|
|
19
|
+
else:
|
|
20
|
+
iv_prop = [ensure_vp(iv)]
|
|
21
|
+
dv = ensure_vp(dv)
|
|
22
|
+
|
|
23
|
+
data = stimuli.property_dataframe()
|
|
24
|
+
cols = [p.name for p in iv_prop + [dv]]
|
|
25
|
+
|
|
26
|
+
return _regression_plots(data[cols], dv=dv,
|
|
27
|
+
ivs=iv_prop, figsize=figsize)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def property_difference_regression(stim_pairs: CollectionStimulusPairs,
|
|
31
|
+
dv: Union[str, VP],
|
|
32
|
+
iv: VPOrList,
|
|
33
|
+
figsize: Tuple[float, float] = (10, 8)):
|
|
34
|
+
|
|
35
|
+
if isinstance(iv, List):
|
|
36
|
+
iv_prop = [ensure_vp(p) for p in iv]
|
|
37
|
+
else:
|
|
38
|
+
iv_prop = [ensure_vp(iv)]
|
|
39
|
+
dv = ensure_vp(dv)
|
|
40
|
+
|
|
41
|
+
data = stim_pairs.property_differences(iv_prop + [dv])
|
|
42
|
+
return _regression_plots(data, dv=dv,
|
|
43
|
+
ivs=iv_prop, figsize=figsize)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def property_ratio_regression(stim_pairs: CollectionStimulusPairs,
|
|
47
|
+
dv: Union[str, VP],
|
|
48
|
+
iv: VPOrList,
|
|
49
|
+
figsize: Tuple[float, float] = (10, 8)):
|
|
50
|
+
|
|
51
|
+
if isinstance(iv, List):
|
|
52
|
+
iv_prop = [ensure_vp(p) for p in iv]
|
|
53
|
+
else:
|
|
54
|
+
iv_prop = [ensure_vp(iv)]
|
|
55
|
+
dv = ensure_vp(dv)
|
|
56
|
+
|
|
57
|
+
data = stim_pairs.property_ratios(iv_prop + [dv])
|
|
58
|
+
return _regression_plots(data, dv=dv,
|
|
59
|
+
ivs=iv_prop, figsize=figsize)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _regression_plots(data: pd.DataFrame,
|
|
63
|
+
dv: VP,
|
|
64
|
+
ivs: List[VP],
|
|
65
|
+
figsize: Tuple[float, float] = (10, 8)):
|
|
66
|
+
|
|
67
|
+
if dv in ivs:
|
|
68
|
+
raise ValueError(f"Dependent variable '{dv.name}' is also"
|
|
69
|
+
" in list of independent variables.")
|
|
70
|
+
if len(ivs) == 1:
|
|
71
|
+
n_col = 1
|
|
72
|
+
n_row = 1
|
|
73
|
+
else:
|
|
74
|
+
n_col = 2
|
|
75
|
+
n_row = ceil(len(ivs)/2)
|
|
76
|
+
|
|
77
|
+
fig, axs = plt.subplots(n_row, n_col, figsize=figsize)
|
|
78
|
+
for i, p in enumerate(ivs):
|
|
79
|
+
_reg_plot(axs.flat[i], data, dv.name, p.name)
|
|
80
|
+
|
|
81
|
+
plt.tight_layout()
|
|
82
|
+
return fig
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _reg_plot(ax: plt.Axes, df: pd.DataFrame, x: str, y: str): # type: ignore
|
|
86
|
+
slope, intercept, r_value, _, _ = linregress(df[x], df[y])
|
|
87
|
+
|
|
88
|
+
ax.scatter(df[x], df[y])
|
|
89
|
+
# Add regression line
|
|
90
|
+
reg_line = slope * df[x] + intercept # type: ignore
|
|
91
|
+
ax.plot(df[x], reg_line, color='green')
|
|
92
|
+
ax.set_title(f"{x}, {y} (r={r_value:.2f})")
|
|
93
|
+
return
|
|
@@ -12,13 +12,14 @@ dynamic = ["version", "description"]
|
|
|
12
12
|
requires-python = ">=3.10"
|
|
13
13
|
dependencies = ["numpy>=2.1.3",
|
|
14
14
|
"pandas>=2.2",
|
|
15
|
+
"scipy>=1.14",
|
|
16
|
+
"matplotlib>=3.9",
|
|
15
17
|
"shapely>=2.0",
|
|
16
18
|
"orjson>=3.10",
|
|
17
19
|
"svgwrite>=1.4",
|
|
18
20
|
"Pillow>=11.0"]
|
|
19
21
|
|
|
20
22
|
[project.optional-dependencies]
|
|
21
|
-
analysis = ["scipy>=1.14", "matplotlib>=3.9"]
|
|
22
23
|
pygame = ["pygame>=2.5"]
|
|
23
24
|
#expyriment = ["expyriment>=0.10"]
|
|
24
25
|
test = [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from ._collection_stats import property_regression_sheet
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from math import ceil
|
|
2
|
-
from typing import List, Tuple, Union
|
|
3
|
-
|
|
4
|
-
import matplotlib.pyplot as plt
|
|
5
|
-
import pandas as pd
|
|
6
|
-
from scipy.stats import linregress
|
|
7
|
-
|
|
8
|
-
from .._stimulus.properties import VP, ensure_vp, VPOrList
|
|
9
|
-
from ..collections._coll_stim_pairs import CollectionStimulusPairs
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _regression_plot(ax: plt.Axes, df: pd.DataFrame, x: str, y: str):
|
|
13
|
-
slope, intercept, r_value, p_value, std_err = linregress(df[x], df[y])
|
|
14
|
-
|
|
15
|
-
ax.scatter(df[x], df[y])
|
|
16
|
-
# Add regression line
|
|
17
|
-
reg_line = slope * df[x] + intercept # type: ignore
|
|
18
|
-
ax.plot(df[x], reg_line, color='green')
|
|
19
|
-
ax.set_title(f"{x}, {y} (r={r_value:.2f})")
|
|
20
|
-
return
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def property_regression_sheet(stim_set: CollectionStimulusPairs,
|
|
24
|
-
dv: Union[str, VP],
|
|
25
|
-
iv: VPOrList,
|
|
26
|
-
ratios: bool = False,
|
|
27
|
-
figsize: Tuple[float, float] = (10, 8)):
|
|
28
|
-
if isinstance(iv, List):
|
|
29
|
-
iv = [ensure_vp(p) for p in iv]
|
|
30
|
-
else:
|
|
31
|
-
iv = [ensure_vp(iv)]
|
|
32
|
-
dv = ensure_vp(dv)
|
|
33
|
-
|
|
34
|
-
if ratios:
|
|
35
|
-
r = stim_set.property_ratios(iv + [dv])
|
|
36
|
-
else:
|
|
37
|
-
r = stim_set.property_differences(iv + [dv])
|
|
38
|
-
|
|
39
|
-
if len(iv) == 1:
|
|
40
|
-
n_col = 1
|
|
41
|
-
n_row = 1
|
|
42
|
-
else:
|
|
43
|
-
n_col = 2
|
|
44
|
-
n_row = ceil(len(iv)/2)
|
|
45
|
-
|
|
46
|
-
fig, axs = plt.subplots(n_row, n_col, figsize=figsize)
|
|
47
|
-
for i, p in enumerate(iv):
|
|
48
|
-
_regression_plot(axs.flat[i], r, dv.name, p.name)
|
|
49
|
-
|
|
50
|
-
plt.tight_layout()
|
|
51
|
-
return fig
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
from copy import deepcopy
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
from numpy.typing import NDArray
|
|
7
|
-
|
|
8
|
-
from .._stimulus.nsn_stimulus import NSNStimulus
|
|
9
|
-
from .._stimulus.shape_array import ShapeArray
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def distances(shape_array: ShapeArray) -> NDArray:
|
|
13
|
-
"""Matrix with the distances between the shapes in the array"""
|
|
14
|
-
return _relation_matrix(shape_array, what=0)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def dwithin(shape_array: ShapeArray, distance: float) -> NDArray:
|
|
18
|
-
"""Matrix with the booleans indication dwithin between the shapes in the array"""
|
|
19
|
-
return _relation_matrix(shape_array, what=1, para=distance)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def overlaps(shape_array: ShapeArray, min_distance: Optional[float] = None) -> NDArray:
|
|
23
|
-
if min_distance is None:
|
|
24
|
-
if isinstance(shape_array, NSNStimulus):
|
|
25
|
-
min_distance = shape_array.min_distance
|
|
26
|
-
else:
|
|
27
|
-
min_distance = 0
|
|
28
|
-
|
|
29
|
-
return dwithin(shape_array, distance=min_distance)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _relation_matrix(arr: ShapeArray, what: int, para: float = 0) -> NDArray:
|
|
33
|
-
"""helper function returning the relation between polygons
|
|
34
|
-
0 = distance
|
|
35
|
-
1 = dwithin
|
|
36
|
-
"""
|
|
37
|
-
arr = deepcopy(arr)
|
|
38
|
-
l = arr.n_shapes
|
|
39
|
-
rtn = np.full((l, l), np.nan)
|
|
40
|
-
for x in reversed(range(l)):
|
|
41
|
-
shape = arr.shape_pop(x)
|
|
42
|
-
if what == 0:
|
|
43
|
-
y = arr.distances(shape)
|
|
44
|
-
elif what == 1:
|
|
45
|
-
y = arr.dwithin(shape=shape, distance=para)
|
|
46
|
-
else:
|
|
47
|
-
raise RuntimeError("unknown function")
|
|
48
|
-
|
|
49
|
-
rtn[x, 0:x] = y
|
|
50
|
-
|
|
51
|
-
# make symmetric
|
|
52
|
-
i_lower = np.triu_indices(l, 1)
|
|
53
|
-
rtn[i_lower] = rtn.T[i_lower]
|
|
54
|
-
return rtn
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|