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.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +36 -39
- boris/add_modifier.py +122 -109
- boris/add_modifier_ui.py +239 -135
- boris/advanced_event_filtering.py +81 -45
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +42 -49
- boris/config.py +161 -77
- boris/config_file.py +63 -83
- boris/connections.py +112 -57
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2511 -1824
- boris/core_qrc.py +15895 -10185
- boris/core_ui.py +946 -792
- boris/db_functions.py +21 -41
- boris/dev.py +134 -0
- boris/dialog.py +505 -244
- boris/duration_widget.py +15 -20
- boris/edit_event.py +84 -28
- boris/edit_event_ui.py +214 -78
- boris/event_operations.py +517 -415
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +213 -583
- boris/export_observation.py +98 -611
- boris/external_processes.py +156 -97
- boris/geometric_measurement.py +652 -287
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +9 -9
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +26 -63
- boris/latency.py +34 -25
- boris/measurement_widget.py +14 -18
- boris/media_file.py +52 -84
- boris/menu_options.py +17 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv.py +1 -0
- boris/mpv2.py +732 -705
- boris/observation.py +655 -310
- boris/observation_operations.py +1036 -404
- boris/observation_ui.py +584 -356
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -80
- boris/param_panel.py +31 -16
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +90 -60
- boris/plot_data_module.py +43 -46
- boris/plot_events.py +127 -90
- boris/plot_events_rt.py +17 -31
- boris/plot_spectrogram_rt.py +95 -30
- boris/plot_waveform_rt.py +32 -21
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +306 -83
- boris/preferences_ui.py +685 -228
- boris/project.py +448 -293
- boris/project_functions.py +689 -254
- boris/project_import_export.py +213 -222
- boris/project_ui.py +674 -438
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +74 -48
- boris/select_observations.py +20 -199
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +53 -37
- boris/subjects_pad.py +6 -9
- boris/synthetic_time_budget.py +45 -28
- boris/time_budget_functions.py +171 -171
- boris/time_budget_widget.py +84 -114
- boris/transitions.py +41 -47
- boris/utilities.py +766 -266
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +125 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/boris_ui.py +0 -886
- boris/converters.ui +0 -289
- boris/core.qrc +0 -35
- boris/core.ui +0 -1543
- boris/edit_event.ui +0 -175
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -773
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1069
- boris/project_server.py +0 -236
- boris/vlc.py +0 -10343
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
- boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
- boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/plot_events.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -25,42 +25,39 @@ import pathlib as pl
|
|
|
25
25
|
|
|
26
26
|
import matplotlib
|
|
27
27
|
|
|
28
|
-
matplotlib.use("
|
|
28
|
+
matplotlib.use("QtAgg")
|
|
29
|
+
|
|
29
30
|
import matplotlib.dates
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
import matplotlib.pyplot as plt
|
|
32
|
-
import matplotlib.transforms as mtransforms
|
|
33
33
|
import numpy as np
|
|
34
|
-
from matplotlib import colors as mcolors
|
|
35
34
|
from matplotlib.dates import (
|
|
36
|
-
HOURLY,
|
|
37
|
-
MICROSECONDLY,
|
|
38
|
-
MINUTELY,
|
|
39
|
-
MONTHLY,
|
|
40
|
-
SECONDLY,
|
|
41
|
-
WEEKLY,
|
|
42
35
|
DateFormatter,
|
|
43
|
-
RRuleLocator,
|
|
44
|
-
rrulewrapper,
|
|
45
36
|
)
|
|
46
37
|
|
|
47
38
|
from . import config as cfg
|
|
48
39
|
from . import db_functions, project_functions, observation_operations
|
|
49
40
|
from . import utilities as util
|
|
50
41
|
|
|
42
|
+
# matplotlib.pyplot.switch_backend("Qt5Agg")
|
|
43
|
+
|
|
51
44
|
|
|
52
45
|
def default_value(ethogram, behavior, parameter):
|
|
53
46
|
"""
|
|
54
47
|
return value for duration in case of point event
|
|
55
48
|
"""
|
|
56
49
|
default_value_ = 0
|
|
57
|
-
if project_functions.event_type(behavior, ethogram)
|
|
50
|
+
if project_functions.event_type(behavior, ethogram) in cfg.POINT_EVENT_TYPES and parameter in ["duration"]:
|
|
58
51
|
default_value_ = "NA"
|
|
59
52
|
return default_value_
|
|
60
53
|
|
|
61
54
|
|
|
62
55
|
def init_behav_modif(
|
|
63
|
-
ethogram: dict,
|
|
56
|
+
ethogram: dict,
|
|
57
|
+
selected_subjects: list,
|
|
58
|
+
distinct_behav_modif,
|
|
59
|
+
include_modifiers,
|
|
60
|
+
parameters,
|
|
64
61
|
) -> dict:
|
|
65
62
|
"""
|
|
66
63
|
initialize dictionary with subject, behaviors and modifiers
|
|
@@ -70,7 +67,6 @@ def init_behav_modif(
|
|
|
70
67
|
for subj in selected_subjects:
|
|
71
68
|
behaviors[subj] = {}
|
|
72
69
|
for behav_modif in distinct_behav_modif:
|
|
73
|
-
|
|
74
70
|
behav, modif = behav_modif
|
|
75
71
|
behav_modif_str = "|".join(behav_modif) if modif else behav
|
|
76
72
|
|
|
@@ -88,7 +84,7 @@ def init_behav(ethogram: dict, selected_subjects: list, distinct_behaviors, para
|
|
|
88
84
|
initialize dictionary with subject, behaviors and modifiers
|
|
89
85
|
"""
|
|
90
86
|
|
|
91
|
-
behaviors = {}
|
|
87
|
+
behaviors: dict = {}
|
|
92
88
|
for subj in selected_subjects:
|
|
93
89
|
behaviors[subj] = {}
|
|
94
90
|
for behavior in distinct_behaviors:
|
|
@@ -122,15 +118,12 @@ def create_behaviors_bar_plot(
|
|
|
122
118
|
|
|
123
119
|
selected_subjects = param[cfg.SELECTED_SUBJECTS]
|
|
124
120
|
selected_behaviors = param[cfg.SELECTED_BEHAVIORS]
|
|
125
|
-
time_interval = param["time"]
|
|
126
121
|
start_time = param[cfg.START_TIME]
|
|
127
122
|
end_time = param[cfg.END_TIME]
|
|
128
123
|
|
|
129
124
|
parameters = ["duration", "number of occurences"]
|
|
130
125
|
|
|
131
|
-
ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
|
|
132
|
-
pj, selected_subjects, selected_observations, selected_behaviors
|
|
133
|
-
)
|
|
126
|
+
ok, msg, db_connector = db_functions.load_aggregated_events_in_db(pj, selected_subjects, selected_observations, selected_behaviors)
|
|
134
127
|
|
|
135
128
|
if not ok:
|
|
136
129
|
return {"error": True, "message": msg}
|
|
@@ -139,10 +132,12 @@ def create_behaviors_bar_plot(
|
|
|
139
132
|
all_behaviors = util.all_behaviors(pj[cfg.ETHOGRAM])
|
|
140
133
|
|
|
141
134
|
for obs_id in selected_observations:
|
|
142
|
-
|
|
143
135
|
cursor = db_connector.cursor()
|
|
144
136
|
# distinct behaviors
|
|
145
|
-
cursor.execute(
|
|
137
|
+
cursor.execute(
|
|
138
|
+
"SELECT distinct behavior FROM aggregated_events WHERE observation = ?",
|
|
139
|
+
(obs_id,),
|
|
140
|
+
)
|
|
146
141
|
distinct_behav = [rows["behavior"] for rows in cursor.fetchall()]
|
|
147
142
|
|
|
148
143
|
# add selected behaviors that are not observed
|
|
@@ -154,7 +149,10 @@ def create_behaviors_bar_plot(
|
|
|
154
149
|
"""
|
|
155
150
|
|
|
156
151
|
# distinct subjects
|
|
157
|
-
cursor.execute(
|
|
152
|
+
cursor.execute(
|
|
153
|
+
"SELECT distinct subject FROM aggregated_events WHERE observation = ?",
|
|
154
|
+
(obs_id,),
|
|
155
|
+
)
|
|
158
156
|
distinct_subjects = [rows["subject"] for rows in cursor.fetchall()]
|
|
159
157
|
|
|
160
158
|
behaviors = init_behav(pj[cfg.ETHOGRAM], distinct_subjects, distinct_behav, parameters)
|
|
@@ -198,7 +196,7 @@ def create_behaviors_bar_plot(
|
|
|
198
196
|
except Exception:
|
|
199
197
|
max_time = float(obs_length)
|
|
200
198
|
|
|
201
|
-
if param["time"]
|
|
199
|
+
if param["time"] in (cfg.TIME_ARBITRARY_INTERVAL, cfg.TIME_OBS_INTERVAL):
|
|
202
200
|
min_time = float(start_time)
|
|
203
201
|
max_time = float(end_time)
|
|
204
202
|
|
|
@@ -234,15 +232,10 @@ def create_behaviors_bar_plot(
|
|
|
234
232
|
)
|
|
235
233
|
|
|
236
234
|
for ax_idx, subject in enumerate(sorted(distinct_subjects)):
|
|
237
|
-
|
|
238
235
|
for behavior in distinct_behav:
|
|
239
|
-
|
|
240
236
|
# number of occurences
|
|
241
237
|
cursor.execute(
|
|
242
|
-
(
|
|
243
|
-
"SELECT COUNT(*) AS count FROM aggregated_events "
|
|
244
|
-
"WHERE observation = ? AND subject = ? AND behavior = ?"
|
|
245
|
-
),
|
|
238
|
+
("SELECT COUNT(*) AS count FROM aggregated_events WHERE observation = ? AND subject = ? AND behavior = ?"),
|
|
246
239
|
(
|
|
247
240
|
obs_id,
|
|
248
241
|
subject,
|
|
@@ -253,7 +246,7 @@ def create_behaviors_bar_plot(
|
|
|
253
246
|
behaviors[subject][behavior]["number of occurences"] = 0 if row["count"] is None else row["count"]
|
|
254
247
|
|
|
255
248
|
# total duration
|
|
256
|
-
if
|
|
249
|
+
if project_functions.event_type(behavior, pj[cfg.ETHOGRAM]) in cfg.STATE_EVENT_TYPES:
|
|
257
250
|
cursor.execute(
|
|
258
251
|
(
|
|
259
252
|
"SELECT SUM(stop - start) AS duration FROM aggregated_events "
|
|
@@ -268,27 +261,49 @@ def create_behaviors_bar_plot(
|
|
|
268
261
|
for row in cursor.fetchall():
|
|
269
262
|
behaviors[subject][behavior]["duration"] = 0 if row["duration"] is None else row["duration"]
|
|
270
263
|
|
|
271
|
-
|
|
264
|
+
(
|
|
265
|
+
durations,
|
|
266
|
+
n_occurences,
|
|
267
|
+
colors,
|
|
268
|
+
x_labels,
|
|
269
|
+
colors_duration,
|
|
270
|
+
x_labels_duration,
|
|
271
|
+
) = ([], [], [], [], [], [])
|
|
272
272
|
|
|
273
273
|
for behavior in sorted(distinct_behav):
|
|
274
|
-
|
|
275
274
|
if param[cfg.EXCLUDE_BEHAVIORS] and behaviors[subject][behavior]["number of occurences"] == 0:
|
|
276
275
|
continue
|
|
277
276
|
|
|
278
277
|
n_occurences.append(behaviors[subject][behavior]["number of occurences"])
|
|
279
278
|
x_labels.append(behavior)
|
|
280
|
-
try:
|
|
281
|
-
colors.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
|
|
282
|
-
except Exception:
|
|
283
|
-
colors.append("darkgray")
|
|
284
279
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
280
|
+
# color
|
|
281
|
+
behav_idx = [k for k in pj[cfg.ETHOGRAM] if pj[cfg.ETHOGRAM][k]["code"] == behavior][0]
|
|
282
|
+
col = None
|
|
283
|
+
if cfg.COLOR in pj[cfg.ETHOGRAM][behav_idx]:
|
|
284
|
+
col = util.behavior_user_color(pj[cfg.ETHOGRAM], behavior)
|
|
285
|
+
if col is not None:
|
|
286
|
+
colors.append(col)
|
|
287
|
+
else:
|
|
288
288
|
try:
|
|
289
|
-
|
|
289
|
+
colors.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
|
|
290
290
|
except Exception:
|
|
291
|
-
|
|
291
|
+
colors.append("darkgray")
|
|
292
|
+
|
|
293
|
+
if project_functions.event_type(behavior, pj[cfg.ETHOGRAM]) in cfg.STATE_EVENT_TYPES:
|
|
294
|
+
durations.append(behaviors[subject][behavior]["duration"])
|
|
295
|
+
x_labels_duration.append(behavior)
|
|
296
|
+
|
|
297
|
+
col = None
|
|
298
|
+
if cfg.COLOR in pj[cfg.ETHOGRAM][behav_idx]:
|
|
299
|
+
col = util.behavior_user_color(pj[cfg.ETHOGRAM], behavior)
|
|
300
|
+
if col is not None:
|
|
301
|
+
colors_duration.append(col)
|
|
302
|
+
else:
|
|
303
|
+
try:
|
|
304
|
+
colors_duration.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
|
|
305
|
+
except Exception:
|
|
306
|
+
colors_duration.append("darkgray")
|
|
292
307
|
|
|
293
308
|
# width = 0.35 # the width of the bars: can also be len(x) sequence
|
|
294
309
|
|
|
@@ -341,10 +356,15 @@ def create_behaviors_bar_plot(
|
|
|
341
356
|
|
|
342
357
|
|
|
343
358
|
def create_events_plot(
|
|
344
|
-
|
|
359
|
+
self,
|
|
360
|
+
selected_observations,
|
|
361
|
+
parameters,
|
|
362
|
+
plot_colors=cfg.BEHAVIORS_PLOT_COLORS,
|
|
363
|
+
plot_directory="",
|
|
364
|
+
file_format="png",
|
|
345
365
|
):
|
|
346
366
|
"""
|
|
347
|
-
create a time diagram plot (
|
|
367
|
+
create a time diagram plot (like a gantt chart)
|
|
348
368
|
with matplotlib barh function (https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.pyplot.barh.html)
|
|
349
369
|
"""
|
|
350
370
|
|
|
@@ -355,9 +375,7 @@ def create_events_plot(
|
|
|
355
375
|
start_time = parameters[cfg.START_TIME]
|
|
356
376
|
end_time = parameters[cfg.END_TIME]
|
|
357
377
|
|
|
358
|
-
ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
|
|
359
|
-
pj, selected_subjects, selected_observations, selected_behaviors
|
|
360
|
-
)
|
|
378
|
+
ok, msg, db_connector = db_functions.load_aggregated_events_in_db(self.pj, selected_subjects, selected_observations, selected_behaviors)
|
|
361
379
|
|
|
362
380
|
if not ok:
|
|
363
381
|
return False, msg, None
|
|
@@ -379,14 +397,13 @@ def create_events_plot(
|
|
|
379
397
|
distinct_behav_modif = sorted(distinct_behav_modif)
|
|
380
398
|
max_len = len(distinct_behav_modif)
|
|
381
399
|
|
|
382
|
-
all_behaviors = [pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(pj[cfg.ETHOGRAM])]
|
|
400
|
+
all_behaviors = [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]
|
|
383
401
|
|
|
384
402
|
par1 = 1
|
|
385
403
|
bar_height = 0.5
|
|
386
|
-
|
|
404
|
+
epoch_date = dt.datetime(2017, 1, 1)
|
|
387
405
|
|
|
388
406
|
for obs_id in selected_observations:
|
|
389
|
-
|
|
390
407
|
if len(selected_subjects) > 1:
|
|
391
408
|
fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True)
|
|
392
409
|
else:
|
|
@@ -394,9 +411,7 @@ def create_events_plot(
|
|
|
394
411
|
axs = np.ndarray(shape=(1), dtype=type(ax))
|
|
395
412
|
axs[0] = ax
|
|
396
413
|
|
|
397
|
-
ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
|
|
398
|
-
pj, selected_subjects, [obs_id], selected_behaviors
|
|
399
|
-
)
|
|
414
|
+
ok, msg, db_connector = db_functions.load_aggregated_events_in_db(self.pj, selected_subjects, [obs_id], selected_behaviors)
|
|
400
415
|
|
|
401
416
|
cursor = db_connector.cursor()
|
|
402
417
|
# if modifiers not to be included set modifiers to ""
|
|
@@ -417,7 +432,7 @@ def create_events_plot(
|
|
|
417
432
|
max_len = len(distinct_behav_modif)
|
|
418
433
|
|
|
419
434
|
# time
|
|
420
|
-
obs_length = observation_operations.observation_total_length(pj[cfg.OBSERVATIONS][obs_id])
|
|
435
|
+
obs_length = observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id])
|
|
421
436
|
if obs_length == -1: # media length not available
|
|
422
437
|
interval = cfg.TIME_EVENTS
|
|
423
438
|
|
|
@@ -425,13 +440,20 @@ def create_events_plot(
|
|
|
425
440
|
min_time = 0.0
|
|
426
441
|
max_time = float(obs_length)
|
|
427
442
|
|
|
443
|
+
if interval == cfg.TIME_OBS_INTERVAL:
|
|
444
|
+
obs_interval = self.pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
|
|
445
|
+
offset = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET])
|
|
446
|
+
min_time = float(obs_interval[0]) + offset
|
|
447
|
+
# Use max media duration for max time if no interval is defined (=0)
|
|
448
|
+
max_time = float(obs_interval[1]) + offset if obs_interval[1] != 0 else float(obs_length)
|
|
449
|
+
|
|
428
450
|
if interval == cfg.TIME_EVENTS:
|
|
429
451
|
try:
|
|
430
|
-
min_time = float(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0]) # first event
|
|
452
|
+
min_time = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0]) # first event
|
|
431
453
|
except Exception:
|
|
432
454
|
min_time = 0.0
|
|
433
455
|
try:
|
|
434
|
-
max_time = float(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][-1][0]) # last event
|
|
456
|
+
max_time = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][-1][0]) # last event
|
|
435
457
|
except Exception:
|
|
436
458
|
max_time = float(obs_length)
|
|
437
459
|
|
|
@@ -478,10 +500,10 @@ def create_events_plot(
|
|
|
478
500
|
|
|
479
501
|
ylabels = [" ".join(x) for x in distinct_behav_modif]
|
|
480
502
|
for ax_idx, subject in enumerate(selected_subjects):
|
|
481
|
-
|
|
482
503
|
if parameters["exclude behaviors"]:
|
|
483
504
|
cursor.execute(
|
|
484
|
-
"SELECT DISTINCT behavior, modifiers FROM aggregated_events WHERE subject = ?",
|
|
505
|
+
"SELECT DISTINCT behavior, modifiers FROM aggregated_events WHERE subject = ?",
|
|
506
|
+
(subject,),
|
|
485
507
|
)
|
|
486
508
|
distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()]
|
|
487
509
|
|
|
@@ -508,10 +530,7 @@ def create_events_plot(
|
|
|
508
530
|
|
|
509
531
|
# total duration
|
|
510
532
|
cursor.execute(
|
|
511
|
-
(
|
|
512
|
-
"SELECT start, stop FROM aggregated_events "
|
|
513
|
-
"WHERE subject = ? AND behavior = ? AND modifiers = ?"
|
|
514
|
-
),
|
|
533
|
+
("SELECT start, stop FROM aggregated_events WHERE subject = ? AND behavior = ? AND modifiers = ?"),
|
|
515
534
|
(
|
|
516
535
|
subject,
|
|
517
536
|
behavior,
|
|
@@ -521,22 +540,36 @@ def create_events_plot(
|
|
|
521
540
|
for row in cursor.fetchall():
|
|
522
541
|
bars[behavior_modifiers_str].append((row["start"], row["stop"]))
|
|
523
542
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
seconds=row["stop"] + cfg.POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"])
|
|
543
|
+
if self.timeFormat == cfg.HHMMSS:
|
|
544
|
+
start_date = matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=row["start"]))
|
|
545
|
+
end_date = matplotlib.dates.date2num(
|
|
546
|
+
epoch_date + dt.timedelta(seconds=row["stop"] + cfg.POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))
|
|
529
547
|
)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
548
|
+
if self.timeFormat == cfg.S:
|
|
549
|
+
start_date = row["start"]
|
|
550
|
+
end_date = row["stop"]
|
|
551
|
+
|
|
552
|
+
# color
|
|
553
|
+
behav_idx = [k for k in self.pj[cfg.ETHOGRAM] if self.pj[cfg.ETHOGRAM][k]["code"] == behavior][0]
|
|
554
|
+
col = None
|
|
555
|
+
if cfg.COLOR in self.pj[cfg.ETHOGRAM][behav_idx]:
|
|
556
|
+
col = util.behavior_user_color(self.pj[cfg.ETHOGRAM], behavior)
|
|
557
|
+
if col is not None:
|
|
558
|
+
bar_color = col
|
|
559
|
+
else:
|
|
560
|
+
try:
|
|
561
|
+
bar_color = util.behavior_color(plot_colors, all_behaviors.index(behavior))
|
|
562
|
+
except Exception:
|
|
563
|
+
bar_color = "darkgray"
|
|
535
564
|
bar_color = cfg.POINT_EVENT_PLOT_COLOR if row["stop"] == row["start"] else bar_color
|
|
536
565
|
|
|
537
566
|
# sage colors removed from matplotlib colors list
|
|
538
|
-
if bar_color in
|
|
539
|
-
bar_color = {
|
|
567
|
+
if bar_color in ("sage", "darksage", "lightsage"):
|
|
568
|
+
bar_color = {
|
|
569
|
+
"darksage": "#598556",
|
|
570
|
+
"lightsage": "#bcecac",
|
|
571
|
+
"sage": "#87ae73",
|
|
572
|
+
}[bar_color]
|
|
540
573
|
|
|
541
574
|
try:
|
|
542
575
|
axs[ax_idx].barh(
|
|
@@ -569,26 +602,30 @@ def create_events_plot(
|
|
|
569
602
|
|
|
570
603
|
axs[ax_idx].set_yticklabels(ylabels, fontdict={"fontsize": 10})
|
|
571
604
|
|
|
572
|
-
axs[ax_idx].set_ylabel(
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
axs[ax_idx].set_xlim(
|
|
576
|
-
left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)),
|
|
577
|
-
right=matplotlib.dates.date2num(init + dt.timedelta(seconds=max_time + 1)),
|
|
578
|
-
)
|
|
579
|
-
"""
|
|
580
|
-
axs[ax_idx].set_xlim(
|
|
581
|
-
left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)),
|
|
582
|
-
right=matplotlib.dates.date2num(init + dt.timedelta(seconds=max_time)),
|
|
605
|
+
axs[ax_idx].set_ylabel(
|
|
606
|
+
"Behaviors" + " (modifiers)" * include_modifiers,
|
|
607
|
+
fontdict={"fontsize": 10},
|
|
583
608
|
)
|
|
584
609
|
|
|
610
|
+
if self.timeFormat == cfg.HHMMSS:
|
|
611
|
+
axs[ax_idx].set_xlim(
|
|
612
|
+
left=matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=min_time)),
|
|
613
|
+
right=matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=max_time)),
|
|
614
|
+
)
|
|
615
|
+
|
|
585
616
|
axs[ax_idx].grid(color="g", linestyle=":")
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
617
|
+
if self.timeFormat == cfg.HHMMSS:
|
|
618
|
+
axs[ax_idx].xaxis_date()
|
|
619
|
+
axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S"))
|
|
620
|
+
axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12})
|
|
621
|
+
if self.timeFormat == cfg.S:
|
|
622
|
+
axs[ax_idx].set_xlabel("Time (s)", fontdict={"fontsize": 12})
|
|
623
|
+
|
|
589
624
|
axs[ax_idx].invert_yaxis()
|
|
590
625
|
|
|
591
|
-
|
|
626
|
+
if self.timeFormat == cfg.HHMMSS:
|
|
627
|
+
fig.autofmt_xdate()
|
|
628
|
+
|
|
592
629
|
plt.tight_layout()
|
|
593
630
|
|
|
594
631
|
if len(selected_observations) > 1:
|
boris/plot_events_rt.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -25,33 +25,34 @@ Plot events in real time
|
|
|
25
25
|
|
|
26
26
|
import matplotlib
|
|
27
27
|
|
|
28
|
-
matplotlib.use("
|
|
28
|
+
matplotlib.use("QtAgg")
|
|
29
|
+
|
|
29
30
|
import numpy as np
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
from
|
|
33
|
-
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
31
|
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
|
|
32
|
+
from PySide6.QtCore import Signal, QEvent, Qt
|
|
33
|
+
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
|
34
34
|
|
|
35
35
|
from matplotlib.figure import Figure
|
|
36
36
|
|
|
37
37
|
from . import config as cfg
|
|
38
38
|
|
|
39
|
+
# matplotlib.pyplot.switch_backend("Qt5Agg")
|
|
39
40
|
|
|
40
|
-
class Plot_events_RT(QWidget):
|
|
41
41
|
|
|
42
|
+
class Plot_events_RT(QWidget):
|
|
42
43
|
# send keypress event to mainwindow
|
|
43
|
-
sendEvent =
|
|
44
|
+
sendEvent = Signal(QEvent)
|
|
44
45
|
|
|
45
46
|
def __init__(self):
|
|
46
47
|
super().__init__()
|
|
47
|
-
self.setWindowTitle(
|
|
48
|
+
self.setWindowTitle("Events plot")
|
|
48
49
|
|
|
49
50
|
self.interval = 60 # default interval of visualization (in seconds)
|
|
50
51
|
self.time_mem = -1
|
|
51
52
|
|
|
52
53
|
self.events_mem = {"init": 0}
|
|
53
54
|
|
|
54
|
-
self.cursor_color =
|
|
55
|
+
self.cursor_color = cfg.REALTIME_PLOT_CURSOR_COLOR # default cursor color
|
|
55
56
|
self.observation_type = cfg.MEDIA
|
|
56
57
|
self.groupby = "behaviors" # group results by "behaviors" or "modifiers"
|
|
57
58
|
|
|
@@ -65,13 +66,9 @@ class Plot_events_RT(QWidget):
|
|
|
65
66
|
|
|
66
67
|
hlayout1 = QHBoxLayout()
|
|
67
68
|
hlayout1.addWidget(QLabel("Time interval"))
|
|
68
|
-
hlayout1.addWidget(
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
hlayout1.addWidget(
|
|
72
|
-
QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.Qt.NoFocus)
|
|
73
|
-
)
|
|
74
|
-
self.pb_mode = QPushButton("Include modifiers", self, clicked=self.change_mode, focusPolicy=Qt.Qt.NoFocus)
|
|
69
|
+
hlayout1.addWidget(QPushButton("+", self, clicked=lambda: self.time_interval_changed(1), focusPolicy=Qt.NoFocus))
|
|
70
|
+
hlayout1.addWidget(QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.NoFocus))
|
|
71
|
+
self.pb_mode = QPushButton("Include modifiers", self, clicked=self.change_mode, focusPolicy=Qt.NoFocus)
|
|
75
72
|
hlayout1.addWidget(self.pb_mode)
|
|
76
73
|
layout.addLayout(hlayout1)
|
|
77
74
|
|
|
@@ -147,13 +144,11 @@ class Plot_events_RT(QWidget):
|
|
|
147
144
|
intervals_behav[group(event[1], event[2], event[3])] = [(0, 0)]
|
|
148
145
|
|
|
149
146
|
for event in events:
|
|
150
|
-
|
|
151
147
|
time_, subject, code, modifier = event[:4]
|
|
152
148
|
key = group(subject, code, modifier)
|
|
153
149
|
|
|
154
150
|
# check if code is state
|
|
155
151
|
if code in self.state_events_list:
|
|
156
|
-
|
|
157
152
|
if key in mem_behav and mem_behav[key] is not None:
|
|
158
153
|
# stop interval
|
|
159
154
|
|
|
@@ -172,22 +167,16 @@ class Plot_events_RT(QWidget):
|
|
|
172
167
|
mem_behav[key] = time_
|
|
173
168
|
|
|
174
169
|
else: # point event
|
|
175
|
-
|
|
176
170
|
if start <= time_ <= end:
|
|
177
|
-
intervals_behav[key].append(
|
|
178
|
-
(float(time_), float(time_) + self.point_event_plot_duration * 50)
|
|
179
|
-
) # point event -> 1 s
|
|
171
|
+
intervals_behav[key].append((float(time_), float(time_) + self.point_event_plot_duration * 50)) # point event -> 1 s
|
|
180
172
|
|
|
181
173
|
# check if intervals are closed
|
|
182
174
|
for k in mem_behav:
|
|
183
175
|
if mem_behav[k] is not None: # interval open
|
|
184
176
|
if self.observation_type == cfg.LIVE:
|
|
185
|
-
intervals_behav[k].append(
|
|
186
|
-
(float(mem_behav[k]), float((end + start) / 2))
|
|
187
|
-
) # close interval with current time
|
|
177
|
+
intervals_behav[k].append((float(mem_behav[k]), float((end + start) / 2))) # close interval with current time
|
|
188
178
|
|
|
189
179
|
elif self.observation_type == cfg.MEDIA:
|
|
190
|
-
|
|
191
180
|
intervals_behav[k].append((float(mem_behav[k]), float(end))) # close interval with end value
|
|
192
181
|
|
|
193
182
|
return intervals_behav
|
|
@@ -201,9 +190,7 @@ class Plot_events_RT(QWidget):
|
|
|
201
190
|
force_plot (bool): force plot even if media paused
|
|
202
191
|
"""
|
|
203
192
|
|
|
204
|
-
self.events = self.aggregate_events(
|
|
205
|
-
self.events_list, current_time - self.interval / 2, current_time + self.interval / 2
|
|
206
|
-
)
|
|
193
|
+
self.events = self.aggregate_events(self.events_list, current_time - self.interval / 2, current_time + self.interval / 2)
|
|
207
194
|
|
|
208
195
|
if not force_plot and current_time == self.time_mem:
|
|
209
196
|
return
|
|
@@ -211,7 +198,6 @@ class Plot_events_RT(QWidget):
|
|
|
211
198
|
self.time_mem = current_time
|
|
212
199
|
|
|
213
200
|
if self.events != self.events_mem:
|
|
214
|
-
|
|
215
201
|
left, duration = {}, {}
|
|
216
202
|
for k in self.events:
|
|
217
203
|
left[k] = []
|