boris-behav-obs 8.12__py3-none-any.whl → 9.7.6__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 (128) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +28 -39
  4. boris/add_modifier.py +122 -109
  5. boris/add_modifier_ui.py +239 -135
  6. boris/advanced_event_filtering.py +81 -45
  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 +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +42 -49
  23. boris/config.py +141 -65
  24. boris/config_file.py +58 -67
  25. boris/connections.py +107 -61
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2373 -1786
  30. boris/core_qrc.py +15895 -10743
  31. boris/core_ui.py +943 -798
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +109 -8
  34. boris/dialog.py +482 -236
  35. boris/duration_widget.py +9 -14
  36. boris/edit_event.py +61 -31
  37. boris/edit_event_ui.py +208 -97
  38. boris/event_operations.py +408 -293
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +184 -223
  43. boris/export_observation.py +74 -100
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +644 -290
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +4 -4
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +20 -57
  51. boris/latency.py +31 -24
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +17 -19
  54. boris/menu_options.py +17 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv.py +1 -0
  58. boris/mpv2.py +732 -705
  59. boris/observation.py +533 -221
  60. boris/observation_operations.py +1025 -390
  61. boris/observation_ui.py +572 -362
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -68
  64. boris/param_panel.py +31 -16
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +90 -60
  67. boris/plot_data_module.py +25 -33
  68. boris/plot_events.py +127 -90
  69. boris/plot_events_rt.py +17 -31
  70. boris/plot_spectrogram_rt.py +95 -30
  71. boris/plot_waveform_rt.py +32 -21
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +18 -8
  74. boris/portion/const.py +35 -18
  75. boris/portion/dict.py +5 -5
  76. boris/portion/func.py +2 -2
  77. boris/portion/interval.py +21 -41
  78. boris/portion/io.py +41 -32
  79. boris/preferences.py +306 -83
  80. boris/preferences_ui.py +684 -227
  81. boris/project.py +448 -293
  82. boris/project_functions.py +671 -238
  83. boris/project_import_export.py +213 -222
  84. boris/project_ui.py +674 -438
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +74 -48
  88. boris/select_observations.py +20 -198
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +52 -35
  91. boris/subjects_pad.py +6 -9
  92. boris/synthetic_time_budget.py +45 -28
  93. boris/time_budget_functions.py +171 -171
  94. boris/time_budget_widget.py +84 -114
  95. boris/transitions.py +41 -47
  96. boris/utilities.py +627 -236
  97. boris/version.py +3 -3
  98. boris/video_equalizer.py +16 -14
  99. boris/video_equalizer_ui.py +199 -130
  100. boris/video_operations.py +95 -29
  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.6.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
  106. {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/converters.ui +0 -289
  111. boris/core.qrc +0 -36
  112. boris/core.ui +0 -1556
  113. boris/edit_event.ui +0 -233
  114. boris/icons/logo_eye.ico +0 -0
  115. boris/map_creator.py +0 -850
  116. boris/observation.ui +0 -814
  117. boris/param_panel.ui +0 -379
  118. boris/preferences.ui +0 -537
  119. boris/project.ui +0 -1069
  120. boris/project_server.py +0 -236
  121. boris/vlc.py +0 -10343
  122. boris/vlc_local.py +0 -90
  123. boris_behav_obs-8.12.dist-info/LICENSE.TXT +0 -674
  124. boris_behav_obs-8.12.dist-info/METADATA +0 -128
  125. boris_behav_obs-8.12.dist-info/RECORD +0 -108
  126. boris_behav_obs-8.12.dist-info/entry_points.txt +0 -3
  127. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  128. {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/db_functions.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
 
7
7
  This program is free software; you can redistribute it and/or modify
@@ -56,8 +56,7 @@ def load_events_in_db(
56
56
  state_behaviors_codes = [
57
57
  pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
58
58
  for x in pj[cfg.ETHOGRAM]
59
- if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
60
- and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
59
+ if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
61
60
  ]
62
61
 
63
62
  # selected behaviors defined as point event
@@ -71,6 +70,7 @@ def load_events_in_db(
71
70
  """
72
71
 
73
72
  db = sqlite3.connect(":memory:", isolation_level=None)
73
+
74
74
  """
75
75
  import os
76
76
  os.system("rm /tmp/ramdisk/events.sqlite")
@@ -99,20 +99,14 @@ def load_events_in_db(
99
99
  cursor.execute("CREATE INDEX modifiers_idx ON events(modifiers)")
100
100
 
101
101
  for subject_to_analyze in selected_subjects:
102
-
103
102
  for obs_id in selected_observations:
104
-
105
103
  for event in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]:
106
-
107
104
  if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in selected_behaviors:
108
-
109
105
  # extract time, code, modifier and comment (time:0, subject:1, code:2, modifier:3, comment:4)
110
106
  if (subject_to_analyze == cfg.NO_FOCAL_SUBJECT and event[cfg.EVENT_SUBJECT_FIELD_IDX] == "") or (
111
107
  event[cfg.EVENT_SUBJECT_FIELD_IDX] == subject_to_analyze
112
108
  ):
113
-
114
109
  if pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
115
-
116
110
  cursor.execute(
117
111
  (
118
112
  "INSERT INTO events "
@@ -125,18 +119,12 @@ def load_events_in_db(
125
119
  if event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
126
120
  else event[cfg.EVENT_SUBJECT_FIELD_IDX],
127
121
  event[cfg.EVENT_BEHAVIOR_FIELD_IDX],
128
- cfg.STATE
129
- if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes
130
- else cfg.POINT,
122
+ cfg.STATE if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes else cfg.POINT,
131
123
  event[cfg.EVENT_MODIFIER_FIELD_IDX],
132
- float(event[cfg.EVENT_TIME_FIELD_IDX])
133
- if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan()
134
- else None,
124
+ float(event[cfg.EVENT_TIME_FIELD_IDX]) if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan() else None,
135
125
  event[cfg.EVENT_COMMENT_FIELD_IDX],
136
126
  # frame index or NA
137
- event_operations.read_event_field(
138
- event, pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE], cfg.FRAME_INDEX
139
- ),
127
+ event_operations.read_event_field(event, pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE], cfg.FRAME_INDEX),
140
128
  ),
141
129
  )
142
130
 
@@ -153,13 +141,9 @@ def load_events_in_db(
153
141
  if event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
154
142
  else event[cfg.EVENT_SUBJECT_FIELD_IDX],
155
143
  event[cfg.EVENT_BEHAVIOR_FIELD_IDX],
156
- cfg.STATE
157
- if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes
158
- else cfg.POINT,
144
+ cfg.STATE if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes else cfg.POINT,
159
145
  event[cfg.EVENT_MODIFIER_FIELD_IDX],
160
- float(event[cfg.EVENT_TIME_FIELD_IDX])
161
- if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan()
162
- else None,
146
+ float(event[cfg.EVENT_TIME_FIELD_IDX]) if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan() else None,
163
147
  event[cfg.EVENT_COMMENT_FIELD_IDX],
164
148
  event[cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.IMAGE_INDEX]],
165
149
  event[cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.IMAGE_PATH]],
@@ -189,7 +173,7 @@ def load_aggregated_events_in_db(
189
173
 
190
174
  """
191
175
 
192
- logging.debug(f"function: load_aggregated_events_in_db")
176
+ logging.debug("function: load_aggregated_events_in_db")
193
177
 
194
178
  # if no observation selected select all
195
179
  if not selected_observations:
@@ -197,20 +181,16 @@ def load_aggregated_events_in_db(
197
181
 
198
182
  # if no subject selected select all
199
183
  if not selected_subjects:
200
- selected_subjects = sorted(
201
- [pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in pj[cfg.SUBJECTS]] + [cfg.NO_FOCAL_SUBJECT]
202
- )
184
+ selected_subjects = sorted([pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in pj[cfg.SUBJECTS]] + [cfg.NO_FOCAL_SUBJECT])
203
185
 
204
186
  # if no behavior selected select all
205
187
  if not selected_behaviors:
206
188
  selected_behaviors = sorted([pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in pj[cfg.ETHOGRAM]])
207
189
 
208
190
  # check if state events are paired
209
- out = ""
191
+ out: str = ""
210
192
  for obs_id in selected_observations:
211
- r, msg = project_functions.check_state_events_obs(
212
- obs_id, pj[cfg.ETHOGRAM], pj[cfg.OBSERVATIONS][obs_id], cfg.HHMMSS
213
- )
193
+ r, msg = project_functions.check_state_events_obs(obs_id, pj[cfg.ETHOGRAM], pj[cfg.OBSERVATIONS][obs_id], cfg.HHMMSS)
214
194
  if not r:
215
195
  out += f"Observation: <strong>{obs_id}</strong><br>{msg}<br>"
216
196
  if out:
@@ -220,24 +200,23 @@ def load_aggregated_events_in_db(
220
200
  state_behaviors_codes = [
221
201
  pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
222
202
  for x in pj[cfg.ETHOGRAM]
223
- if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
224
- and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
203
+ if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
225
204
  ]
226
205
 
227
206
  # selected behaviors defined as point event
228
207
  point_behaviors_codes = [
229
208
  pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
230
209
  for x in pj[cfg.ETHOGRAM]
231
- if cfg.POINT in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
232
- and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
210
+ if cfg.POINT in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
233
211
  ]
234
212
 
235
213
  db = sqlite3.connect(":memory:")
236
214
 
215
+ # only for debugging
237
216
  """
238
217
  import os
239
- os.system("rm /tmp/ramdisk/aggreg.sqlite")
240
- db = sqlite3.connect("/tmp/ramdisk/aggreg.sqlite", isolation_level=None)
218
+ os.system("rm /tmp/ramdisk/aggregated_events.sqlite")
219
+ db = sqlite3.connect("/tmp/ramdisk/aggregated_events.sqlite", isolation_level=None)
241
220
  """
242
221
 
243
222
  db.row_factory = sqlite3.Row
@@ -277,12 +256,10 @@ def load_aggregated_events_in_db(
277
256
  )
278
257
 
279
258
  for obs_id in selected_observations:
280
-
281
259
  cursor1 = load_events_in_db(pj, selected_subjects, [obs_id], selected_behaviors)
282
260
 
283
261
  for subject in selected_subjects:
284
262
  for behavior in selected_behaviors:
285
-
286
263
  cursor1.execute(
287
264
  "SELECT DISTINCT modifiers FROM events WHERE subject=? AND code=? ORDER BY modifiers",
288
265
  (
@@ -294,7 +271,6 @@ def load_aggregated_events_in_db(
294
271
  rows_distinct_modifiers = list(x[0] for x in cursor1.fetchall())
295
272
 
296
273
  for distinct_modifiers in rows_distinct_modifiers:
297
-
298
274
  cursor1.execute(
299
275
  (
300
276
  "SELECT occurence, comment, image_index, image_path FROM events "
@@ -305,7 +281,6 @@ def load_aggregated_events_in_db(
305
281
  rows = list(cursor1.fetchall())
306
282
 
307
283
  for idx, row in enumerate(rows):
308
-
309
284
  if behavior in point_behaviors_codes:
310
285
  data = (
311
286
  obs_id,
boris/dev.py CHANGED
@@ -1,7 +1,11 @@
1
1
  import sys
2
+ import time
2
3
  import pprint
4
+ import pandas as pd
5
+
3
6
  from . import project_functions
4
7
  from . import config as cfg
8
+ from . import utilities as util
5
9
 
6
10
 
7
11
  def event_val(event, field, obs_type: str = cfg.MEDIA):
@@ -13,21 +17,118 @@ def event_val(event, field, obs_type: str = cfg.MEDIA):
13
17
 
14
18
  _, _, pj, _ = project_functions.open_project_json(sys.argv[1])
15
19
 
16
- pprint.pprint(list(pj[cfg.OBSERVATIONS].keys()))
20
+ # pprint.pprint(list(pj[cfg.OBSERVATIONS].keys()))
17
21
 
18
- print()
22
+ # print()
19
23
 
24
+ """
20
25
  obs_id = "images NO TIME"
21
-
22
26
  pprint.pprint(pj[cfg.OBSERVATIONS][obs_id])
27
+ """
28
+
29
+ state_events_list = util.state_behavior_codes(pj[cfg.ETHOGRAM])
30
+
31
+ df_def = {
32
+ "observation id": pd.Series(dtype="str"),
33
+ "observation type": pd.Series(dtype="str"),
34
+ "observation description": pd.Series(dtype="str"),
35
+ "subject": pd.Series(dtype="str"),
36
+ "behavior": pd.Series(dtype="str"),
37
+ "modifier": pd.Series(dtype="str"),
38
+ "start": pd.Series(dtype="float"),
39
+ "stop": pd.Series(dtype="float"),
40
+ "duration": pd.Series(dtype="float"),
41
+ }
42
+ df = pd.DataFrame(df_def)
43
+ l = []
44
+ # print(df.info)
45
+
46
+ for obs_id in pj[cfg.OBSERVATIONS]:
47
+ # print(obs_id)
48
+ obs_type = pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE]
49
+ obs_descr = pj[cfg.OBSERVATIONS][obs_id][cfg.DESCRIPTION]
50
+ mem_idx = []
51
+ for idx, event in enumerate(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]):
52
+ if idx in mem_idx:
53
+ continue
54
+ start = event_val(event, cfg.TIME)
55
+ subject = event_val(event, cfg.SUBJECT)
56
+ behavior = event_val(event, cfg.BEHAVIOR_CODE)
57
+ modifier = event_val(event, cfg.MODIFIER)
58
+ if behavior in state_events_list:
59
+ stop = None
60
+ for idx2, event2 in enumerate(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][idx + 1 :], start=idx + 1):
61
+ subject2 = event_val(event2, cfg.SUBJECT)
62
+ behavior2 = event_val(event2, cfg.BEHAVIOR_CODE)
63
+ modifier2 = event_val(event2, cfg.MODIFIER)
64
+ if subject == subject2 and behavior == behavior2 and modifier == modifier2:
65
+ stop = event_val(event2, cfg.TIME)
66
+ mem_idx.append(idx2)
67
+ l.append([obs_id, obs_type, obs_descr, subject, behavior, modifier, start, stop, stop - start])
68
+ break
69
+ if stop is None:
70
+ print(obs_id, " not paired")
71
+ # print(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS])
72
+ # print(f"{l=}")
73
+ # print(f"{mem_idx=}")
74
+
75
+ sys.exit()
76
+ else:
77
+ l.append([obs_id, obs_type, obs_descr, subject, behavior, modifier, start, start, 0])
78
+
79
+ # print(pd.DataFrame(l))
80
+ df = pd.concat(
81
+ [
82
+ df,
83
+ pd.DataFrame(
84
+ l,
85
+ columns=[
86
+ "observation id",
87
+ "observation type",
88
+ "observation description",
89
+ "subject",
90
+ "behavior",
91
+ "modifier",
92
+ "start",
93
+ "stop",
94
+ "duration",
95
+ ],
96
+ ),
97
+ ]
98
+ )
99
+ del l
100
+ print("=" * 30)
101
+ print("describe")
102
+ print(df.describe())
103
+ print("=" * 30)
104
+
105
+ # print(f'{df["subject"].value_counts()=}')
106
+ # print(f'{df["subject"].nunique()=}')
107
+
108
+ pd.set_option("display.max_rows", None, "display.max_columns", None)
109
+
110
+ print("=" * 30)
111
+ print("mean")
112
+ r = df.groupby(["subject", "behavior"])["duration"].mean()
113
+ print(r)
114
+ print("=" * 30)
23
115
 
24
- print(cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.COMMENT])
25
116
 
26
- print()
117
+ r = df.groupby(["observation id", "subject", "behavior"])
118
+ print(r["start"])
27
119
 
28
- print(f"{pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.BEHAVIOR_CODE]]=}")
29
120
 
30
- print()
121
+ """
122
+ # replace value (for selecting a time interval)
123
+ t1 = time.time()
124
+ df.loc[df["stop"] > 10, "stop"] = 10
125
+ print(time.time() - t1)
126
+ print(df)
127
+ """
31
128
 
32
129
 
33
- print(f"{event_val(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0], cfg.BEHAVIOR_CODE, 18)=}")
130
+ """
131
+ t1 = 1
132
+ t2 = 2
133
+ print(df.query(f"`start` <= {t1} & `stop` >= {t2} & `duration` != 0"))
134
+ """