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.
Files changed (90) hide show
  1. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/PKG-INFO +3 -4
  2. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/__init__.py +1 -1
  3. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/properties.py +1 -1
  4. pynsn-1.0.9/pynsn/collections/__init__.py +2 -0
  5. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/_coll_stim.py +22 -12
  6. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/_coll_stim_pairs.py +19 -3
  7. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/fit.py +9 -4
  8. pynsn-1.0.9/pynsn/geometry.py +51 -0
  9. pynsn-1.0.9/pynsn/plots/__init__.py +3 -0
  10. pynsn-1.0.9/pynsn/plots/_collection_plots.py +93 -0
  11. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pyproject.toml +2 -1
  12. pynsn-1.0.7.dev6/pynsn/analysis/__init__.py +0 -1
  13. pynsn-1.0.7.dev6/pynsn/analysis/_collection_stats.py +0 -51
  14. pynsn-1.0.7.dev6/pynsn/collections/__init__.py +0 -2
  15. pynsn-1.0.7.dev6/pynsn/matrix/__init__.py +0 -5
  16. pynsn-1.0.7.dev6/pynsn/matrix/matrix.py +0 -54
  17. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/.gitignore +0 -0
  18. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/.readthedocs.yaml +0 -0
  19. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/LICENSE +0 -0
  20. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/Makefile +0 -0
  21. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/README.md +0 -0
  22. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/euro_flag_demo.html +0 -0
  23. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/index.html +0 -0
  24. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/make_object_arrays_demo.html +0 -0
  25. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/docs/pynsn_demo.html +0 -0
  26. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/Makefile +0 -0
  27. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/_static/euro_flag_demo.html +0 -0
  28. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/_static/make_object_arrays_demo.html +0 -0
  29. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/_static/pynsn_demo.html +0 -0
  30. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/api.rst +0 -0
  31. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/arrays.rst +0 -0
  32. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/check_api_reference.py +0 -0
  33. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/colours.rst +0 -0
  34. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/conf.py +0 -0
  35. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/distributions.rst +0 -0
  36. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/examples.rst +0 -0
  37. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/index.rst +0 -0
  38. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/install.rst +0 -0
  39. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/make.bat +0 -0
  40. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/misc.rst +0 -0
  41. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/modules.rst +0 -0
  42. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/documentation/random_arrays.rst +0 -0
  43. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/Makefile +0 -0
  44. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/euro_flag_demo.ipynb +0 -0
  45. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/eurostar.png +0 -0
  46. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/further_demo.ipynb +0 -0
  47. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/make_object_arrays_demo.ipynb +0 -0
  48. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/examples/pynsn_demo.ipynb +0 -0
  49. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_misc.py +0 -0
  50. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/__init__.py +0 -0
  51. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/abc_shapes.py +0 -0
  52. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/circ_shapes.py +0 -0
  53. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/colour.py +0 -0
  54. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/ellipse_geometry.py +0 -0
  55. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_shapes/shapes.py +0 -0
  56. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/__init__.py +0 -0
  57. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/convex_hull.py +0 -0
  58. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/nsn_stimulus.py +0 -0
  59. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/shape_array.py +0 -0
  60. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/stimulus_colours.py +0 -0
  61. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/stimulus_pair.py +0 -0
  62. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/_stimulus/target_area.py +0 -0
  63. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/collections/_abc_coll.py +0 -0
  64. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/defaults.py +0 -0
  65. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/exceptions.py +0 -0
  66. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/fit.py +0 -0
  67. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/__init__.py +0 -0
  68. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/_base.py +0 -0
  69. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/expyriment_stimulus.py +0 -0
  70. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/pil_image.py +0 -0
  71. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/pygame_surface.py +0 -0
  72. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/image/svg_file.py +0 -0
  73. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/__init__.py +0 -0
  74. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_distributions.py +0 -0
  75. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_distributions_2d.py +0 -0
  76. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_factory.py +0 -0
  77. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_random_shape.py +0 -0
  78. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/rnd/_rng.py +0 -0
  79. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/pynsn/typing.py +0 -0
  80. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/testing.py +0 -0
  81. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/Dockerfile-py310 +0 -0
  82. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/Dockerfile-py38 +0 -0
  83. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/__init__.py +0 -0
  84. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/shapes_test_picture.py +0 -0
  85. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_dot_array.py +0 -0
  86. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_images.py +0 -0
  87. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_rect_array.py +0 -0
  88. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tests/test_save_load.py +0 -0
  89. {pynsn-1.0.7.dev6 → pynsn-1.0.9}/tox.ini +0 -0
  90. {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.7.dev6
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.7-dev6'
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
@@ -8,7 +8,7 @@ __author__ = "Oliver Lindemann <lindemann@cognitive-psychology.eu>"
8
8
 
9
9
  import enum
10
10
  from collections import OrderedDict
11
- from typing import Any, Dict, List, Tuple, Union
11
+ from typing import Any, List, Tuple, Union
12
12
 
13
13
  import numpy as np
14
14
  import shapely
@@ -0,0 +1,2 @@
1
+ from ._coll_stim import CollectionStimuli, AbstractCollection
2
+ from ._coll_stim_pairs import CollectionStimulusPairs
@@ -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: tp.Union[None, NSNStimulus] = None) -> None:
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.reset_properties_dataframe()
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 reset_properties_dataframe(self):
80
- """reset dataframe of visual properties
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
- # names = [x.name for x in self.stimuli]
107
- # rtn = self._prop_df.copy()
108
- # rtn["name"] = names
109
- return self._prop_df
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.reset_properties_dataframe()
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 reset_properties_dataframe(self):
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(sp, prop_a, rnd_values[i, 0])
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(sp, prop_b, rnd_values[i, 1])
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.reset_properties_dataframe()
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.reset_properties_dataframe()
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,3 @@
1
+ from ._collection_plots import (property_regression,
2
+ property_difference_regression,
3
+ property_ratio_regression)
@@ -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,2 +0,0 @@
1
- from ._coll_stim import CollectionStimuli, AbstractCollection, ListNSNStimuli
2
- from ._coll_stim_pairs import CollectionStimulusPairs, ListNSNStimPairs
@@ -1,5 +0,0 @@
1
- """calculate relation matrices """
2
-
3
- # pylint: disable=W0611
4
-
5
- from .matrix import distances, dwithin, overlaps
@@ -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