boris-behav-obs 9.7.7__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.

Potentially problematic release.


This version of boris-behav-obs might be problematic. Click here for more details.

Files changed (109) hide show
  1. boris/__init__.py +26 -0
  2. boris/__main__.py +25 -0
  3. boris/about.py +143 -0
  4. boris/add_modifier.py +635 -0
  5. boris/add_modifier_ui.py +303 -0
  6. boris/advanced_event_filtering.py +455 -0
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +1110 -0
  18. boris/behavior_binary_table.py +305 -0
  19. boris/behaviors_coding_map.py +239 -0
  20. boris/boris_cli.py +340 -0
  21. boris/cmd_arguments.py +49 -0
  22. boris/coding_pad.py +280 -0
  23. boris/config.py +785 -0
  24. boris/config_file.py +356 -0
  25. boris/connections.py +409 -0
  26. boris/converters.py +333 -0
  27. boris/converters_ui.py +225 -0
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +5901 -0
  30. boris/core_qrc.py +15958 -0
  31. boris/core_ui.py +1107 -0
  32. boris/db_functions.py +324 -0
  33. boris/dev.py +134 -0
  34. boris/dialog.py +1108 -0
  35. boris/duration_widget.py +238 -0
  36. boris/edit_event.py +245 -0
  37. boris/edit_event_ui.py +233 -0
  38. boris/event_operations.py +1040 -0
  39. boris/events_cursor.py +61 -0
  40. boris/events_snapshots.py +596 -0
  41. boris/exclusion_matrix.py +141 -0
  42. boris/export_events.py +1006 -0
  43. boris/export_observation.py +1203 -0
  44. boris/external_processes.py +332 -0
  45. boris/geometric_measurement.py +941 -0
  46. boris/gui_utilities.py +135 -0
  47. boris/image_overlay.py +72 -0
  48. boris/import_observations.py +242 -0
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +634 -0
  51. boris/latency.py +244 -0
  52. boris/measurement_widget.py +161 -0
  53. boris/media_file.py +115 -0
  54. boris/menu_options.py +213 -0
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +157 -0
  57. boris/mpv.py +2016 -0
  58. boris/mpv2.py +2193 -0
  59. boris/observation.py +1453 -0
  60. boris/observation_operations.py +2538 -0
  61. boris/observation_ui.py +679 -0
  62. boris/observations_list.py +337 -0
  63. boris/otx_parser.py +442 -0
  64. boris/param_panel.py +201 -0
  65. boris/param_panel_ui.py +305 -0
  66. boris/player_dock_widget.py +198 -0
  67. boris/plot_data_module.py +536 -0
  68. boris/plot_events.py +634 -0
  69. boris/plot_events_rt.py +237 -0
  70. boris/plot_spectrogram_rt.py +316 -0
  71. boris/plot_waveform_rt.py +230 -0
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +31 -0
  74. boris/portion/const.py +95 -0
  75. boris/portion/dict.py +365 -0
  76. boris/portion/func.py +52 -0
  77. boris/portion/interval.py +581 -0
  78. boris/portion/io.py +181 -0
  79. boris/preferences.py +510 -0
  80. boris/preferences_ui.py +770 -0
  81. boris/project.py +2007 -0
  82. boris/project_functions.py +2041 -0
  83. boris/project_import_export.py +1096 -0
  84. boris/project_ui.py +794 -0
  85. boris/qrc_boris.py +10389 -0
  86. boris/qrc_boris5.py +2579 -0
  87. boris/select_modifiers.py +312 -0
  88. boris/select_observations.py +210 -0
  89. boris/select_subj_behav.py +286 -0
  90. boris/state_events.py +197 -0
  91. boris/subjects_pad.py +106 -0
  92. boris/synthetic_time_budget.py +290 -0
  93. boris/time_budget_functions.py +1136 -0
  94. boris/time_budget_widget.py +1039 -0
  95. boris/transitions.py +365 -0
  96. boris/utilities.py +1810 -0
  97. boris/version.py +24 -0
  98. boris/video_equalizer.py +159 -0
  99. boris/video_equalizer_ui.py +248 -0
  100. boris/video_operations.py +310 -0
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.7.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
  106. boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
  107. boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
  108. boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
  109. boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
boris/portion/const.py ADDED
@@ -0,0 +1,95 @@
1
+ import enum
2
+
3
+
4
+ class Bound(enum.Enum):
5
+ """
6
+ Bound types, either CLOSED for inclusive, or OPEN for exclusive.
7
+ """
8
+
9
+ CLOSED = True
10
+ OPEN = False
11
+
12
+ def __bool__(self):
13
+ raise ValueError("The truth value of a bound is ambiguous.")
14
+
15
+ def __invert__(self):
16
+ return Bound.CLOSED if self is Bound.OPEN else Bound.OPEN
17
+
18
+ def __str__(self):
19
+ return self.name
20
+
21
+ def __repr__(self):
22
+ return self.name
23
+
24
+
25
+ class _Singleton:
26
+ __instance = None
27
+
28
+ def __new__(cls, *args, **kwargs):
29
+ if not cls.__instance:
30
+ cls.__instance = super(_Singleton, cls).__new__(cls)
31
+ return cls.__instance
32
+
33
+
34
+ class _PInf(_Singleton):
35
+ """
36
+ Represent positive infinity.
37
+ """
38
+
39
+ def __neg__(self):
40
+ return _NInf()
41
+
42
+ def __lt__(self, o):
43
+ return False
44
+
45
+ def __le__(self, o):
46
+ return isinstance(o, _PInf)
47
+
48
+ def __gt__(self, o):
49
+ return not isinstance(o, _PInf)
50
+
51
+ def __ge__(self, o):
52
+ return True
53
+
54
+ def __eq__(self, o):
55
+ return isinstance(o, _PInf)
56
+
57
+ def __repr__(self):
58
+ return "+inf"
59
+
60
+ def __hash__(self):
61
+ return hash(float("+inf"))
62
+
63
+
64
+ class _NInf(_Singleton):
65
+ """
66
+ Represent negative infinity.
67
+ """
68
+
69
+ def __neg__(self):
70
+ return _PInf()
71
+
72
+ def __lt__(self, o):
73
+ return not isinstance(o, _NInf)
74
+
75
+ def __le__(self, o):
76
+ return True
77
+
78
+ def __gt__(self, o):
79
+ return False
80
+
81
+ def __ge__(self, o):
82
+ return isinstance(o, _NInf)
83
+
84
+ def __eq__(self, o):
85
+ return isinstance(o, _NInf)
86
+
87
+ def __repr__(self):
88
+ return "-inf"
89
+
90
+ def __hash__(self):
91
+ return hash(float("-inf"))
92
+
93
+
94
+ # Positive infinity
95
+ inf = _PInf()
boris/portion/dict.py ADDED
@@ -0,0 +1,365 @@
1
+ from .const import Bound
2
+ from .interval import Interval, singleton
3
+
4
+ from collections.abc import MutableMapping, Mapping
5
+
6
+ from sortedcontainers import SortedDict
7
+
8
+
9
+ def _sort(i):
10
+ # Sort by lower bound, closed first
11
+ return (i[0].lower, i[0].left is Bound.OPEN)
12
+
13
+
14
+ class IntervalDict(MutableMapping):
15
+ """
16
+ An IntervalDict is a dict-like data structure that maps from intervals to data,
17
+ where keys can be single values or Interval instances.
18
+
19
+ When keys are Interval instances, its behaviour merely corresponds to
20
+ range queries and it returns IntervalDict instances corresponding to the
21
+ subset of values covered by the given interval. If no matching value is
22
+ found, an empty IntervalDict is returned.
23
+ When keys are "single values", its behaviour corresponds to the one of Python
24
+ built-in dict. When no matchin value is found, a KeyError is raised.
25
+
26
+ Note that this class does not aim to have the best performance, but is
27
+ provided mainly for convenience. Its performance mainly depends on the
28
+ number of distinct values (not keys) that are stored.
29
+ """
30
+
31
+ __slots__ = ("_storage",)
32
+
33
+ def __init__(self, mapping_or_iterable=None):
34
+ """
35
+ Return a new IntervalDict.
36
+
37
+ If no argument is given, an empty IntervalDict is created. If an argument
38
+ is given, and is a mapping object (e.g., another IntervalDict), an
39
+ new IntervalDict with the same key-value pairs is created. If an
40
+ iterable is provided, it has to be a list of (key, value) pairs.
41
+
42
+ :param mapping_or_iterable: optional mapping or iterable.
43
+ """
44
+ self._storage = SortedDict(_sort) # Mapping from intervals to values
45
+
46
+ if mapping_or_iterable is not None:
47
+ self.update(mapping_or_iterable)
48
+
49
+ @classmethod
50
+ def _from_items(cls, items):
51
+ """
52
+ Fast creation of an IntervalDict with the provided items.
53
+
54
+ The items have to satisfy the two following properties: (1) all keys
55
+ must be disjoint intervals and (2) all values must be distinct.
56
+
57
+ :param items: list of (key, value) pairs.
58
+ :return: an IntervalDict
59
+ """
60
+ d = cls()
61
+ for key, value in items:
62
+ d._storage[key] = value
63
+
64
+ return d
65
+
66
+ def clear(self):
67
+ """
68
+ Remove all items from the IntervalDict.
69
+ """
70
+ self._storage.clear()
71
+
72
+ def copy(self):
73
+ """
74
+ Return a shallow copy.
75
+
76
+ :return: a shallow copy.
77
+ """
78
+ return IntervalDict._from_items(self.items())
79
+
80
+ def get(self, key, default=None):
81
+ """
82
+ Return the values associated to given key.
83
+
84
+ If the key is a single value, it returns a single value (if it exists) or
85
+ the default value. If the key is an Interval, it returns a new IntervalDict
86
+ restricted to given interval. In that case, the default value is used to
87
+ "fill the gaps" (if any) w.r.t. given key.
88
+
89
+ :param key: a single value or an Interval instance.
90
+ :param default: default value (default to None).
91
+ :return: an IntervalDict, or a single value if key is not an Interval.
92
+ """
93
+ if isinstance(key, Interval):
94
+ d = self[key]
95
+ d[key - d.domain()] = default
96
+ return d
97
+ else:
98
+ try:
99
+ return self[key]
100
+ except KeyError:
101
+ return default
102
+
103
+ def find(self, value):
104
+ """
105
+ Return a (possibly empty) Interval i such that self[i] = value, and
106
+ self[~i] != value.
107
+
108
+ :param value: value to look for.
109
+ :return: an Interval instance.
110
+ """
111
+ return Interval(*(i for i, v in self._storage.items() if v == value))
112
+
113
+ def items(self):
114
+ """
115
+ Return a view object on the contained items sorted by key
116
+ (see https://docs.python.org/3/library/stdtypes.html#dict-views).
117
+
118
+ :return: a view object.
119
+ """
120
+ return self._storage.items()
121
+
122
+ def keys(self):
123
+ """
124
+ Return a view object on the contained keys (sorted)
125
+ (see https://docs.python.org/3/library/stdtypes.html#dict-views).
126
+
127
+ :return: a view object.
128
+ """
129
+ return self._storage.keys()
130
+
131
+ def values(self):
132
+ """
133
+ Return a view object on the contained values sorted by key
134
+ (see https://docs.python.org/3/library/stdtypes.html#dict-views).
135
+
136
+ :return: a view object.
137
+ """
138
+ return self._storage.values()
139
+
140
+ def domain(self):
141
+ """
142
+ Return an Interval corresponding to the domain of this IntervalDict.
143
+
144
+ :return: an Interval.
145
+ """
146
+ return Interval(*self._storage.keys())
147
+
148
+ def pop(self, key, default=None):
149
+ """
150
+ Remove key and return the corresponding value if key is not an Interval.
151
+ If key is an interval, it returns an IntervalDict instance.
152
+
153
+ This method combines self[key] and del self[key]. If a default value
154
+ is provided and is not None, it uses self.get(key, default) instead of
155
+ self[key].
156
+
157
+ :param key: a single value or an Interval instance.
158
+ :param default: optional default value.
159
+ :return: an IntervalDict, or a single value if key is not an Interval.
160
+ """
161
+ if default is None:
162
+ value = self[key]
163
+ del self[key]
164
+ return value
165
+ else:
166
+ value = self.get(key, default)
167
+ try:
168
+ del self[key]
169
+ except KeyError:
170
+ pass
171
+ return value
172
+
173
+ def popitem(self):
174
+ """
175
+ Remove and return some (key, value) pair as a 2-tuple.
176
+ Raise KeyError if D is empty.
177
+
178
+ :return: a (key, value) pair.
179
+ """
180
+ return self._storage.popitem()
181
+
182
+ def setdefault(self, key, default=None):
183
+ """
184
+ Return given key. If it does not exist, set its value to default and
185
+ return it.
186
+
187
+ :param key: a single value or an Interval instance.
188
+ :param default: default value (default to None).
189
+ :return: an IntervalDict, or a single value if key is not an Interval.
190
+ """
191
+ if isinstance(key, Interval):
192
+ value = self.get(key, default)
193
+ self.update(value)
194
+ return value
195
+ else:
196
+ try:
197
+ return self[key]
198
+ except KeyError:
199
+ self[key] = default
200
+ return default
201
+
202
+ def update(self, mapping_or_iterable):
203
+ """
204
+ Update current IntervalDict with provided values.
205
+
206
+ If a mapping is provided, it must map Interval instances to values (e.g., another
207
+ IntervalDict). If an iterable is provided, it must consist of a list of
208
+ (key, value) pairs.
209
+
210
+ :param mapping_or_iterable: mapping or iterable.
211
+ """
212
+ if isinstance(mapping_or_iterable, Mapping):
213
+ data = mapping_or_iterable.items()
214
+ else:
215
+ data = mapping_or_iterable
216
+
217
+ for i, v in data:
218
+ self[i] = v
219
+
220
+ def combine(self, other, how):
221
+ """
222
+ Return a new IntervalDict that combines the values from current and
223
+ provided ones.
224
+
225
+ If d = d1.combine(d2, f), then d contains (1) all values from d1 whose
226
+ keys do not intersect the ones of d2, (2) all values from d2 whose keys
227
+ do not intersect the ones of d1, and (3) f(x, y) for x in d1, y in d2 for
228
+ intersecting keys.
229
+
230
+ :param other: another IntervalDict instance.
231
+ :param how: a function of two parameters that combines values.
232
+ :return: a new IntervalDict instance.
233
+ """
234
+ new_items = []
235
+
236
+ dom1, dom2 = self.domain(), other.domain()
237
+
238
+ new_items.extend(self[dom1 - dom2].items())
239
+ new_items.extend(other[dom2 - dom1].items())
240
+
241
+ intersection = dom1 & dom2
242
+ d1, d2 = self[intersection], other[intersection]
243
+
244
+ for i1, v1 in d1.items():
245
+ for i2, v2 in d2.items():
246
+ if i1.overlaps(i2):
247
+ i = i1 & i2
248
+ v = how(v1, v2)
249
+ new_items.append((i, v))
250
+
251
+ return IntervalDict(new_items)
252
+
253
+ def as_dict(self):
254
+ """
255
+ Return the content as a classical Python dict.
256
+
257
+ :return: a Python dict.
258
+ """
259
+ return dict(self._storage)
260
+
261
+ def __getitem__(self, key):
262
+ if isinstance(key, Interval):
263
+ items = []
264
+ for i, v in self._storage.items():
265
+ intersection = key & i
266
+ if not intersection.empty:
267
+ items.append((intersection, v))
268
+ return IntervalDict._from_items(items)
269
+ else:
270
+ for i, v in self._storage.items():
271
+ if key in i:
272
+ return v
273
+ raise KeyError(key)
274
+
275
+ def __setitem__(self, key, value):
276
+ interval = key if isinstance(key, Interval) else singleton(key)
277
+
278
+ if interval.empty:
279
+ return
280
+
281
+ removed_keys = []
282
+ added_items = []
283
+
284
+ found = False
285
+ for i, v in self._storage.items():
286
+ if value == v:
287
+ found = True
288
+ # Extend existing key
289
+ removed_keys.append(i)
290
+ added_items.append((i | interval, v))
291
+ elif i.overlaps(interval):
292
+ # Reduce existing key
293
+ remaining = i - interval
294
+ removed_keys.append(i)
295
+ if not remaining.empty:
296
+ added_items.append((remaining, v))
297
+
298
+ if not found:
299
+ added_items.append((interval, value))
300
+
301
+ # Update storage accordingly
302
+ for key in removed_keys:
303
+ self._storage.pop(key)
304
+
305
+ for key, value in added_items:
306
+ self._storage[key] = value
307
+
308
+ def __delitem__(self, key):
309
+ interval = key if isinstance(key, Interval) else singleton(key)
310
+
311
+ if interval.empty:
312
+ return
313
+
314
+ removed_keys = []
315
+ added_items = []
316
+
317
+ found = False
318
+ for i, v in self._storage.items():
319
+ if i.overlaps(interval):
320
+ found = True
321
+ remaining = i - interval
322
+ removed_keys.append(i)
323
+ if not remaining.empty:
324
+ added_items.append((remaining, v))
325
+
326
+ if not found and not isinstance(key, Interval):
327
+ raise KeyError(key)
328
+
329
+ # Update storage accordingly
330
+ for key in removed_keys:
331
+ self._storage.pop(key)
332
+
333
+ for key, value in added_items:
334
+ self._storage[key] = value
335
+
336
+ def __or__(self, other):
337
+ d = self.copy()
338
+ d.update(other)
339
+ return d
340
+
341
+ def __ior__(self, other):
342
+ self.update(other)
343
+ return self
344
+
345
+ def __iter__(self):
346
+ return iter(self._storage)
347
+
348
+ def __len__(self):
349
+ return len(self._storage)
350
+
351
+ def __contains__(self, key):
352
+ return key in self.domain()
353
+
354
+ def __repr__(self):
355
+ return "{}{}{}".format(
356
+ "{",
357
+ ", ".join("{!r}: {!r}".format(i, v) for i, v in self.items()),
358
+ "}",
359
+ )
360
+
361
+ def __eq__(self, other):
362
+ if isinstance(other, IntervalDict):
363
+ return self.as_dict() == other.as_dict()
364
+ else:
365
+ return NotImplemented
boris/portion/func.py ADDED
@@ -0,0 +1,52 @@
1
+ import operator
2
+ from functools import partial
3
+
4
+ from .const import inf
5
+
6
+
7
+ def iterate(interval, step, *, base=None, reverse=False):
8
+ """
9
+ Iterate on the (discrete) values of given interval.
10
+
11
+ This function returns a (lazy) iterator over the values of given interval,
12
+ starting from its lower bound and ending on its upper bound (if interval is not open).
13
+ Each returned value merely corresponds to lower + i * step, where "step" defines
14
+ the step between consecutive values.
15
+ It also accepts a callable that is used to compute the next possible
16
+ value based on the current one.
17
+
18
+ When a non-atomic interval is provided, this function chains the iterators obtained
19
+ by calling itself on the underlying atomic intervals.
20
+
21
+ The values returned by the iterator can be aligned with a base value with the "base" parameter.
22
+ This parameter must be a callable that accepts the lower bound of the (atomic) interval as
23
+ input, and returns the first value that needs to be considered for the iteration.
24
+ By default, the identity function is used. If reverse=True, then the upper bound will be
25
+ passed instead of the lower one.
26
+
27
+ :param interval: an interval.
28
+ :param step: step between values, or a callable that returns the next value.
29
+ :param base: a callable that accepts a bound and returns an initial value to consider.
30
+ :param reverse: set to True for descending order.
31
+ :return: a lazy iterator.
32
+ """
33
+ if base is None:
34
+ base = lambda x: x
35
+
36
+ exclude = operator.lt if not reverse else operator.gt
37
+ include = operator.le if not reverse else operator.ge
38
+ step = step if callable(step) else partial(operator.add, step)
39
+
40
+ value = base(interval.lower if not reverse else interval.upper)
41
+ if (value == -inf and not reverse) or (value == inf and reverse):
42
+ raise ValueError("Cannot start iteration with infinity.")
43
+
44
+ for i in interval if not reverse else reversed(interval):
45
+ value = base(i.lower if not reverse else i.upper)
46
+
47
+ while exclude(value, i):
48
+ value = step(value)
49
+
50
+ while include(value, i):
51
+ yield value
52
+ value = step(value)