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.
- boris/__init__.py +26 -0
- boris/__main__.py +25 -0
- boris/about.py +143 -0
- boris/add_modifier.py +635 -0
- boris/add_modifier_ui.py +303 -0
- boris/advanced_event_filtering.py +455 -0
- 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 +1110 -0
- boris/behavior_binary_table.py +305 -0
- boris/behaviors_coding_map.py +239 -0
- boris/boris_cli.py +340 -0
- boris/cmd_arguments.py +49 -0
- boris/coding_pad.py +280 -0
- boris/config.py +785 -0
- boris/config_file.py +356 -0
- boris/connections.py +409 -0
- boris/converters.py +333 -0
- boris/converters_ui.py +225 -0
- boris/cooccurence.py +250 -0
- boris/core.py +5901 -0
- boris/core_qrc.py +15958 -0
- boris/core_ui.py +1107 -0
- boris/db_functions.py +324 -0
- boris/dev.py +134 -0
- boris/dialog.py +1108 -0
- boris/duration_widget.py +238 -0
- boris/edit_event.py +245 -0
- boris/edit_event_ui.py +233 -0
- boris/event_operations.py +1040 -0
- boris/events_cursor.py +61 -0
- boris/events_snapshots.py +596 -0
- boris/exclusion_matrix.py +141 -0
- boris/export_events.py +1006 -0
- boris/export_observation.py +1203 -0
- boris/external_processes.py +332 -0
- boris/geometric_measurement.py +941 -0
- boris/gui_utilities.py +135 -0
- boris/image_overlay.py +72 -0
- boris/import_observations.py +242 -0
- boris/ipc_mpv.py +325 -0
- boris/irr.py +634 -0
- boris/latency.py +244 -0
- boris/measurement_widget.py +161 -0
- boris/media_file.py +115 -0
- boris/menu_options.py +213 -0
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +157 -0
- boris/mpv.py +2016 -0
- boris/mpv2.py +2193 -0
- boris/observation.py +1453 -0
- boris/observation_operations.py +2538 -0
- boris/observation_ui.py +679 -0
- boris/observations_list.py +337 -0
- boris/otx_parser.py +442 -0
- boris/param_panel.py +201 -0
- boris/param_panel_ui.py +305 -0
- boris/player_dock_widget.py +198 -0
- boris/plot_data_module.py +536 -0
- boris/plot_events.py +634 -0
- boris/plot_events_rt.py +237 -0
- boris/plot_spectrogram_rt.py +316 -0
- boris/plot_waveform_rt.py +230 -0
- boris/plugins.py +431 -0
- boris/portion/__init__.py +31 -0
- boris/portion/const.py +95 -0
- boris/portion/dict.py +365 -0
- boris/portion/func.py +52 -0
- boris/portion/interval.py +581 -0
- boris/portion/io.py +181 -0
- boris/preferences.py +510 -0
- boris/preferences_ui.py +770 -0
- boris/project.py +2007 -0
- boris/project_functions.py +2041 -0
- boris/project_import_export.py +1096 -0
- boris/project_ui.py +794 -0
- boris/qrc_boris.py +10389 -0
- boris/qrc_boris5.py +2579 -0
- boris/select_modifiers.py +312 -0
- boris/select_observations.py +210 -0
- boris/select_subj_behav.py +286 -0
- boris/state_events.py +197 -0
- boris/subjects_pad.py +106 -0
- boris/synthetic_time_budget.py +290 -0
- boris/time_budget_functions.py +1136 -0
- boris/time_budget_widget.py +1039 -0
- boris/transitions.py +365 -0
- boris/utilities.py +1810 -0
- boris/version.py +24 -0
- boris/video_equalizer.py +159 -0
- boris/video_equalizer_ui.py +248 -0
- boris/video_operations.py +310 -0
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.7.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
- boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
- boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
- boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
- boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS
|
|
3
|
+
Behavioral Observation Research Interactive Software
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
|
+
|
|
6
|
+
This file is part of BORIS.
|
|
7
|
+
|
|
8
|
+
BORIS is free software; you can redistribute it and/or modify
|
|
9
|
+
it under the terms of the GNU General Public License as published by
|
|
10
|
+
the Free Software Foundation; either version 3 of the License, or
|
|
11
|
+
any later version.
|
|
12
|
+
|
|
13
|
+
BORIS is distributed in the hope that it will be useful,
|
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
GNU General Public License for more details.
|
|
17
|
+
|
|
18
|
+
You should have received a copy of the GNU General Public License
|
|
19
|
+
along with this program; if not see <http://www.gnu.org/licenses/>.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import pathlib as pl
|
|
25
|
+
|
|
26
|
+
from PySide6.QtWidgets import QFileDialog, QMessageBox
|
|
27
|
+
from PySide6.QtGui import QFont, QTextOption, QTextCursor
|
|
28
|
+
|
|
29
|
+
from . import config as cfg
|
|
30
|
+
from . import (
|
|
31
|
+
dialog,
|
|
32
|
+
project_functions,
|
|
33
|
+
select_observations,
|
|
34
|
+
select_subj_behav,
|
|
35
|
+
time_budget_functions,
|
|
36
|
+
observation_operations,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def synthetic_time_budget(self) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Synthetic time budget
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
logging.debug("synthetic time budget function")
|
|
46
|
+
|
|
47
|
+
_, selected_observations = select_observations.select_observations2(
|
|
48
|
+
self, mode=cfg.MULTIPLE, windows_title="Select observations for synthetic time budget"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if not selected_observations:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
# check if coded behaviors are defined in ethogram
|
|
55
|
+
if project_functions.check_coded_behaviors_in_obs_list(self.pj, selected_observations):
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
# check if state events are paired
|
|
59
|
+
not_ok, selected_observations = project_functions.check_state_events(self.pj, selected_observations)
|
|
60
|
+
if not_ok or not selected_observations:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
64
|
+
|
|
65
|
+
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
66
|
+
|
|
67
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
68
|
+
|
|
69
|
+
synth_tb_param = select_subj_behav.choose_obs_subj_behav_category(
|
|
70
|
+
self,
|
|
71
|
+
selected_observations,
|
|
72
|
+
start_coding=start_coding,
|
|
73
|
+
end_coding=end_coding,
|
|
74
|
+
# start_interval=start_interval,
|
|
75
|
+
# end_interval=end_interval,
|
|
76
|
+
start_interval=None,
|
|
77
|
+
end_interval=None,
|
|
78
|
+
maxTime=max_media_duration_all_obs,
|
|
79
|
+
show_exclude_non_coded_behaviors=False,
|
|
80
|
+
by_category=False,
|
|
81
|
+
n_observations=len(selected_observations),
|
|
82
|
+
show_exclude_non_coded_modifiers=True,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if synth_tb_param == {}:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
if not synth_tb_param[cfg.SELECTED_SUBJECTS] or not synth_tb_param[cfg.SELECTED_BEHAVIORS]:
|
|
89
|
+
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
# ask for excluding behaviors durations from total time
|
|
93
|
+
if not start_coding.is_nan():
|
|
94
|
+
cancel_pressed, synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = self.filter_behaviors(
|
|
95
|
+
title="Select behaviors to exclude from the total time",
|
|
96
|
+
text="The duration of the selected behaviors will be subtracted from the total time",
|
|
97
|
+
table="",
|
|
98
|
+
behavior_type=cfg.STATE_EVENT_TYPES,
|
|
99
|
+
)
|
|
100
|
+
if cancel_pressed:
|
|
101
|
+
return
|
|
102
|
+
else:
|
|
103
|
+
synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = []
|
|
104
|
+
|
|
105
|
+
ok, msg, data_report = time_budget_functions.synthetic_time_budget(self.pj, selected_observations, synth_tb_param)
|
|
106
|
+
|
|
107
|
+
results = dialog.Results_dialog()
|
|
108
|
+
results.setWindowTitle("Synthetic time budget")
|
|
109
|
+
if not ok:
|
|
110
|
+
results.ptText.clear()
|
|
111
|
+
results.ptText.setReadOnly(True)
|
|
112
|
+
results.ptText.appendHtml(msg.replace("\n", "<br>"))
|
|
113
|
+
results.exec_()
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
results.dataset = True
|
|
117
|
+
font = QFont("Courier", 12)
|
|
118
|
+
results.ptText.setFont(font)
|
|
119
|
+
results.ptText.setWordWrapMode(QTextOption.NoWrap)
|
|
120
|
+
results.ptText.setReadOnly(True)
|
|
121
|
+
results.ptText.appendPlainText(data_report.export("cli", tablefmt="grid")) # other available format: github
|
|
122
|
+
results.ptText.moveCursor(QTextCursor.Start)
|
|
123
|
+
results.resize(960, 640)
|
|
124
|
+
|
|
125
|
+
if results.exec_() == cfg.SAVE_DATASET:
|
|
126
|
+
file_formats = [
|
|
127
|
+
cfg.TSV,
|
|
128
|
+
cfg.CSV,
|
|
129
|
+
cfg.ODS,
|
|
130
|
+
cfg.XLSX,
|
|
131
|
+
cfg.XLS,
|
|
132
|
+
cfg.HTML,
|
|
133
|
+
cfg.TEXT_FILE, # tablib format: cli
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
file_name, filter_ = QFileDialog().getSaveFileName(self, "Synthetic time budget", "", ";;".join(file_formats))
|
|
137
|
+
if not file_name:
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
output_format = cfg.FILE_NAME_SUFFIX[filter_]
|
|
141
|
+
|
|
142
|
+
if pl.Path(file_name).suffix != "." + output_format:
|
|
143
|
+
if filter_ != cfg.TEXT_FILE:
|
|
144
|
+
file_name = str(pl.Path(file_name)) + "." + output_format
|
|
145
|
+
else:
|
|
146
|
+
file_name = str(pl.Path(file_name))
|
|
147
|
+
if pl.Path(file_name).is_file():
|
|
148
|
+
if (
|
|
149
|
+
dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE])
|
|
150
|
+
== cfg.CANCEL
|
|
151
|
+
):
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
with open(file_name, "wb") as f:
|
|
155
|
+
if filter_ in (cfg.TSV, cfg.CSV, cfg.HTML, cfg.TEXT_FILE):
|
|
156
|
+
f.write(str.encode(data_report.export(output_format)))
|
|
157
|
+
if filter_ in (cfg.ODS, cfg.XLSX, cfg.XLS):
|
|
158
|
+
f.write(data_report.export(output_format))
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def synthetic_binned_time_budget(self) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Synthetic time budget with time bin
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
_, selected_observations = select_observations.select_observations2(
|
|
167
|
+
self, mode=cfg.MULTIPLE, windows_title="Select observations for synthetic binned time budget"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if not selected_observations:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# check if coded behaviors are defined in ethogram
|
|
174
|
+
if project_functions.check_coded_behaviors_in_obs_list(self.pj, selected_observations):
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
# check if state events are paired
|
|
178
|
+
not_ok, selected_observations = project_functions.check_state_events(self.pj, selected_observations)
|
|
179
|
+
if not_ok or not selected_observations:
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
max_media_duration_all_obs, total_media_duration_all_obs = observation_operations.media_duration(
|
|
183
|
+
self.pj[cfg.OBSERVATIONS], selected_observations
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
187
|
+
|
|
188
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
189
|
+
|
|
190
|
+
# exit with message if events do not have timestamp
|
|
191
|
+
if start_coding.is_nan():
|
|
192
|
+
QMessageBox.critical(
|
|
193
|
+
None,
|
|
194
|
+
cfg.programName,
|
|
195
|
+
("This function is not available for observations with events that do not have timestamp"),
|
|
196
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
197
|
+
QMessageBox.NoButton,
|
|
198
|
+
)
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
synth_tb_param = select_subj_behav.choose_obs_subj_behav_category(
|
|
202
|
+
self,
|
|
203
|
+
selected_observations,
|
|
204
|
+
start_coding=start_coding,
|
|
205
|
+
end_coding=end_coding,
|
|
206
|
+
# start_interval=start_interval,
|
|
207
|
+
# end_interval=end_interval,
|
|
208
|
+
start_interval=None,
|
|
209
|
+
end_interval=None,
|
|
210
|
+
maxTime=max_media_duration_all_obs,
|
|
211
|
+
show_exclude_non_coded_behaviors=False,
|
|
212
|
+
by_category=False,
|
|
213
|
+
n_observations=len(selected_observations),
|
|
214
|
+
show_time_bin_size=True,
|
|
215
|
+
show_exclude_non_coded_modifiers=True,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if synth_tb_param == {}:
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
if not synth_tb_param[cfg.SELECTED_SUBJECTS] or not synth_tb_param[cfg.SELECTED_BEHAVIORS]:
|
|
222
|
+
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
# ask for excluding behaviors durations from total time
|
|
226
|
+
cancel_pressed, synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = self.filter_behaviors(
|
|
227
|
+
title="Select behaviors to exclude",
|
|
228
|
+
text=("The duration of the selected behaviors will be subtracted from the total time"),
|
|
229
|
+
table="",
|
|
230
|
+
behavior_type=cfg.STATE_EVENT_TYPES,
|
|
231
|
+
)
|
|
232
|
+
if cancel_pressed:
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
ok, data_report = time_budget_functions.synthetic_time_budget_bin(self.pj, selected_observations, synth_tb_param)
|
|
236
|
+
|
|
237
|
+
if not ok:
|
|
238
|
+
results = dialog.Results_dialog()
|
|
239
|
+
results.setWindowTitle("Synthetic time budget with time bin")
|
|
240
|
+
results.ptText.appendHtml("Error during the creation of the synthetic time budget with time bin")
|
|
241
|
+
results.exec_()
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
results = dialog.Results_dialog()
|
|
245
|
+
results.dataset = True
|
|
246
|
+
results.setWindowTitle("Synthetic time budget by time bin")
|
|
247
|
+
font = QFont("Courier", 12)
|
|
248
|
+
results.ptText.setFont(font)
|
|
249
|
+
results.ptText.setWordWrapMode(QTextOption.NoWrap)
|
|
250
|
+
results.ptText.setReadOnly(True)
|
|
251
|
+
results.ptText.appendPlainText(data_report.export("cli", tablefmt="grid")) # other available format: github
|
|
252
|
+
results.ptText.moveCursor(QTextCursor.Start)
|
|
253
|
+
results.resize(960, 640)
|
|
254
|
+
|
|
255
|
+
if results.exec_() == cfg.SAVE_DATASET:
|
|
256
|
+
file_formats = [
|
|
257
|
+
cfg.TSV,
|
|
258
|
+
cfg.CSV,
|
|
259
|
+
cfg.ODS,
|
|
260
|
+
cfg.XLSX,
|
|
261
|
+
cfg.XLS,
|
|
262
|
+
cfg.HTML,
|
|
263
|
+
cfg.TEXT_FILE,
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
file_name, filter_ = QFileDialog().getSaveFileName(
|
|
267
|
+
self, "Save the Synthetic time budget with time bin", "", ";;".join(file_formats)
|
|
268
|
+
)
|
|
269
|
+
if not file_name:
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
output_format = cfg.FILE_NAME_SUFFIX[filter_]
|
|
273
|
+
|
|
274
|
+
if pl.Path(file_name).suffix != "." + output_format:
|
|
275
|
+
if filter_ != cfg.TEXT_FILE:
|
|
276
|
+
file_name = str(pl.Path(file_name)) + "." + output_format
|
|
277
|
+
else:
|
|
278
|
+
file_name = str(pl.Path(file_name))
|
|
279
|
+
if pl.Path(file_name).is_file():
|
|
280
|
+
if (
|
|
281
|
+
dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", (cfg.CANCEL, cfg.OVERWRITE))
|
|
282
|
+
== cfg.CANCEL
|
|
283
|
+
):
|
|
284
|
+
return
|
|
285
|
+
|
|
286
|
+
with open(file_name, "wb") as f:
|
|
287
|
+
if filter_ in (cfg.TSV, cfg.CSV, cfg.HTML, cfg.TEXT_FILE):
|
|
288
|
+
f.write(str.encode(data_report.export(output_format)))
|
|
289
|
+
if filter_ in (cfg.ODS, cfg.XLSX, cfg.XLS):
|
|
290
|
+
f.write(data_report.export(output_format))
|