boris-behav-obs 8.9.16__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 (129) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +36 -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 +161 -77
  24. boris/config_file.py +63 -83
  25. boris/connections.py +112 -57
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2511 -1824
  30. boris/core_qrc.py +15895 -10185
  31. boris/core_ui.py +946 -792
  32. boris/db_functions.py +21 -41
  33. boris/dev.py +134 -0
  34. boris/dialog.py +505 -244
  35. boris/duration_widget.py +15 -20
  36. boris/edit_event.py +84 -28
  37. boris/edit_event_ui.py +214 -78
  38. boris/event_operations.py +517 -415
  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 +213 -583
  43. boris/export_observation.py +98 -611
  44. boris/external_processes.py +156 -97
  45. boris/geometric_measurement.py +652 -287
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +9 -9
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +26 -63
  51. boris/latency.py +34 -25
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +52 -84
  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 +655 -310
  60. boris/observation_operations.py +1036 -404
  61. boris/observation_ui.py +584 -356
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -80
  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 +43 -46
  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 +685 -228
  81. boris/project.py +448 -293
  82. boris/project_functions.py +689 -254
  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 -199
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +53 -37
  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 +766 -266
  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 +125 -28
  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.9.16.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/boris_ui.py +0 -886
  111. boris/converters.ui +0 -289
  112. boris/core.qrc +0 -35
  113. boris/core.ui +0 -1543
  114. boris/edit_event.ui +0 -175
  115. boris/icons/logo_eye.ico +0 -0
  116. boris/map_creator.py +0 -850
  117. boris/observation.ui +0 -773
  118. boris/param_panel.ui +0 -379
  119. boris/preferences.ui +0 -537
  120. boris/project.ui +0 -1069
  121. boris/project_server.py +0 -236
  122. boris/vlc.py +0 -10343
  123. boris/vlc_local.py +0 -90
  124. boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
  125. boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
  126. boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
  127. boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
  128. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  129. {boris_behav_obs-8.9.16.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
@@ -26,6 +26,7 @@ import logging
26
26
  from typing import Optional, Tuple
27
27
  from . import config as cfg
28
28
  from . import project_functions
29
+ from . import event_operations
29
30
 
30
31
 
31
32
  def load_events_in_db(
@@ -55,8 +56,7 @@ def load_events_in_db(
55
56
  state_behaviors_codes = [
56
57
  pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
57
58
  for x in pj[cfg.ETHOGRAM]
58
- if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
59
- 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
60
60
  ]
61
61
 
62
62
  # selected behaviors defined as point event
@@ -70,6 +70,7 @@ def load_events_in_db(
70
70
  """
71
71
 
72
72
  db = sqlite3.connect(":memory:", isolation_level=None)
73
+
73
74
  """
74
75
  import os
75
76
  os.system("rm /tmp/ramdisk/events.sqlite")
@@ -98,25 +99,19 @@ def load_events_in_db(
98
99
  cursor.execute("CREATE INDEX modifiers_idx ON events(modifiers)")
99
100
 
100
101
  for subject_to_analyze in selected_subjects:
101
-
102
102
  for obs_id in selected_observations:
103
-
104
103
  for event in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]:
105
-
106
104
  if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in selected_behaviors:
107
-
108
105
  # extract time, code, modifier and comment (time:0, subject:1, code:2, modifier:3, comment:4)
109
106
  if (subject_to_analyze == cfg.NO_FOCAL_SUBJECT and event[cfg.EVENT_SUBJECT_FIELD_IDX] == "") or (
110
107
  event[cfg.EVENT_SUBJECT_FIELD_IDX] == subject_to_analyze
111
108
  ):
112
-
113
109
  if pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
114
-
115
110
  cursor.execute(
116
111
  (
117
112
  "INSERT INTO events "
118
- "(observation, subject, code, type, modifiers, occurence, comment) "
119
- "VALUES (?,?,?,?,?,?,?)"
113
+ "(observation, subject, code, type, modifiers, occurence, comment, image_index) "
114
+ "VALUES (?,?,?,?,?,?,?,?)"
120
115
  ),
121
116
  (
122
117
  obs_id,
@@ -124,14 +119,12 @@ def load_events_in_db(
124
119
  if event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
125
120
  else event[cfg.EVENT_SUBJECT_FIELD_IDX],
126
121
  event[cfg.EVENT_BEHAVIOR_FIELD_IDX],
127
- cfg.STATE
128
- if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes
129
- else cfg.POINT,
122
+ cfg.STATE if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes else cfg.POINT,
130
123
  event[cfg.EVENT_MODIFIER_FIELD_IDX],
131
- float(event[cfg.EVENT_TIME_FIELD_IDX])
132
- if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan()
133
- else None,
124
+ float(event[cfg.EVENT_TIME_FIELD_IDX]) if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan() else None,
134
125
  event[cfg.EVENT_COMMENT_FIELD_IDX],
126
+ # frame index or NA
127
+ event_operations.read_event_field(event, pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE], cfg.FRAME_INDEX),
135
128
  ),
136
129
  )
137
130
 
@@ -148,13 +141,9 @@ def load_events_in_db(
148
141
  if event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
149
142
  else event[cfg.EVENT_SUBJECT_FIELD_IDX],
150
143
  event[cfg.EVENT_BEHAVIOR_FIELD_IDX],
151
- cfg.STATE
152
- if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes
153
- else cfg.POINT,
144
+ cfg.STATE if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes else cfg.POINT,
154
145
  event[cfg.EVENT_MODIFIER_FIELD_IDX],
155
- float(event[cfg.EVENT_TIME_FIELD_IDX])
156
- if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan()
157
- else None,
146
+ float(event[cfg.EVENT_TIME_FIELD_IDX]) if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan() else None,
158
147
  event[cfg.EVENT_COMMENT_FIELD_IDX],
159
148
  event[cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.IMAGE_INDEX]],
160
149
  event[cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.IMAGE_PATH]],
@@ -184,7 +173,7 @@ def load_aggregated_events_in_db(
184
173
 
185
174
  """
186
175
 
187
- logging.debug(f"function: load_aggregated_events_in_db")
176
+ logging.debug("function: load_aggregated_events_in_db")
188
177
 
189
178
  # if no observation selected select all
190
179
  if not selected_observations:
@@ -192,20 +181,16 @@ def load_aggregated_events_in_db(
192
181
 
193
182
  # if no subject selected select all
194
183
  if not selected_subjects:
195
- selected_subjects = sorted(
196
- [pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in pj[cfg.SUBJECTS]] + [cfg.NO_FOCAL_SUBJECT]
197
- )
184
+ selected_subjects = sorted([pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in pj[cfg.SUBJECTS]] + [cfg.NO_FOCAL_SUBJECT])
198
185
 
199
186
  # if no behavior selected select all
200
187
  if not selected_behaviors:
201
188
  selected_behaviors = sorted([pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in pj[cfg.ETHOGRAM]])
202
189
 
203
190
  # check if state events are paired
204
- out = ""
191
+ out: str = ""
205
192
  for obs_id in selected_observations:
206
- r, msg = project_functions.check_state_events_obs(
207
- obs_id, pj[cfg.ETHOGRAM], pj[cfg.OBSERVATIONS][obs_id], cfg.HHMMSS
208
- )
193
+ r, msg = project_functions.check_state_events_obs(obs_id, pj[cfg.ETHOGRAM], pj[cfg.OBSERVATIONS][obs_id], cfg.HHMMSS)
209
194
  if not r:
210
195
  out += f"Observation: <strong>{obs_id}</strong><br>{msg}<br>"
211
196
  if out:
@@ -215,24 +200,23 @@ def load_aggregated_events_in_db(
215
200
  state_behaviors_codes = [
216
201
  pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
217
202
  for x in pj[cfg.ETHOGRAM]
218
- if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
219
- 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
220
204
  ]
221
205
 
222
206
  # selected behaviors defined as point event
223
207
  point_behaviors_codes = [
224
208
  pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
225
209
  for x in pj[cfg.ETHOGRAM]
226
- if cfg.POINT in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
227
- 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
228
211
  ]
229
212
 
230
213
  db = sqlite3.connect(":memory:")
231
214
 
215
+ # only for debugging
232
216
  """
233
217
  import os
234
- os.system("rm /tmp/ramdisk/aggreg.sqlite")
235
- 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)
236
220
  """
237
221
 
238
222
  db.row_factory = sqlite3.Row
@@ -272,12 +256,10 @@ def load_aggregated_events_in_db(
272
256
  )
273
257
 
274
258
  for obs_id in selected_observations:
275
-
276
259
  cursor1 = load_events_in_db(pj, selected_subjects, [obs_id], selected_behaviors)
277
260
 
278
261
  for subject in selected_subjects:
279
262
  for behavior in selected_behaviors:
280
-
281
263
  cursor1.execute(
282
264
  "SELECT DISTINCT modifiers FROM events WHERE subject=? AND code=? ORDER BY modifiers",
283
265
  (
@@ -289,7 +271,6 @@ def load_aggregated_events_in_db(
289
271
  rows_distinct_modifiers = list(x[0] for x in cursor1.fetchall())
290
272
 
291
273
  for distinct_modifiers in rows_distinct_modifiers:
292
-
293
274
  cursor1.execute(
294
275
  (
295
276
  "SELECT occurence, comment, image_index, image_path FROM events "
@@ -300,7 +281,6 @@ def load_aggregated_events_in_db(
300
281
  rows = list(cursor1.fetchall())
301
282
 
302
283
  for idx, row in enumerate(rows):
303
-
304
284
  if behavior in point_behaviors_codes:
305
285
  data = (
306
286
  obs_id,
boris/dev.py ADDED
@@ -0,0 +1,134 @@
1
+ import sys
2
+ import time
3
+ import pprint
4
+ import pandas as pd
5
+
6
+ from . import project_functions
7
+ from . import config as cfg
8
+ from . import utilities as util
9
+
10
+
11
+ def event_val(event, field, obs_type: str = cfg.MEDIA):
12
+ try:
13
+ return event[cfg.PJ_OBS_FIELDS[obs_type][field]]
14
+ except:
15
+ return None
16
+
17
+
18
+ _, _, pj, _ = project_functions.open_project_json(sys.argv[1])
19
+
20
+ # pprint.pprint(list(pj[cfg.OBSERVATIONS].keys()))
21
+
22
+ # print()
23
+
24
+ """
25
+ obs_id = "images NO TIME"
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)
115
+
116
+
117
+ r = df.groupby(["observation id", "subject", "behavior"])
118
+ print(r["start"])
119
+
120
+
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
+ """
128
+
129
+
130
+ """
131
+ t1 = 1
132
+ t2 = 2
133
+ print(df.query(f"`start` <= {t1} & `stop` >= {t2} & `duration` != 0"))
134
+ """