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/ipc_mpv.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
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 socket
|
|
24
|
+
import json
|
|
25
|
+
import subprocess
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
import config as cfg
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class IPC_MPV:
|
|
34
|
+
"""
|
|
35
|
+
class for managing mpv through Inter Process Communication (IPC)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
media_durations: list = []
|
|
39
|
+
cumul_media_durations: list = []
|
|
40
|
+
fps: list = []
|
|
41
|
+
_pause: bool = False
|
|
42
|
+
|
|
43
|
+
def __init__(self, socket_path: str = cfg.MPV_SOCKET, parent=None):
|
|
44
|
+
# print(f"{parent=}")
|
|
45
|
+
self.socket_path = socket_path
|
|
46
|
+
self.process = None
|
|
47
|
+
# self.sock = None
|
|
48
|
+
self.init_mpv()
|
|
49
|
+
# self.init_socket()
|
|
50
|
+
|
|
51
|
+
def init_mpv(self):
|
|
52
|
+
"""
|
|
53
|
+
Start mpv process and embed it in the PySide6 application.
|
|
54
|
+
"""
|
|
55
|
+
logger.info("Start mpv ipc process")
|
|
56
|
+
# print(f"{self.winId()=}")
|
|
57
|
+
self.process = subprocess.Popen(
|
|
58
|
+
[
|
|
59
|
+
"mpv",
|
|
60
|
+
"--ontop",
|
|
61
|
+
"--no-border",
|
|
62
|
+
"--osc=no", # no on screen commands
|
|
63
|
+
"--input-ipc-server=" + self.socket_path,
|
|
64
|
+
# "--wid=" + str(int(self.winId())), # Embed in the widget
|
|
65
|
+
"--idle", # Keeps mpv running with no video
|
|
66
|
+
"--input-default-bindings=no",
|
|
67
|
+
"--input-vo-keyboard=no",
|
|
68
|
+
],
|
|
69
|
+
stdout=subprocess.PIPE,
|
|
70
|
+
stderr=subprocess.PIPE,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def send_command(self, command):
|
|
74
|
+
"""
|
|
75
|
+
Send a JSON command to the mpv IPC server.
|
|
76
|
+
"""
|
|
77
|
+
# print(f"send command: {command}")
|
|
78
|
+
try:
|
|
79
|
+
# Create a Unix socket
|
|
80
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
|
|
81
|
+
# Connect to the MPV IPC server
|
|
82
|
+
client.connect(self.socket_path)
|
|
83
|
+
# Send the JSON command
|
|
84
|
+
# print(f"{json.dumps(command).encode('utf-8')=}")
|
|
85
|
+
client.sendall(json.dumps(command).encode("utf-8") + b"\n")
|
|
86
|
+
# Receive the response
|
|
87
|
+
response = client.recv(2000)
|
|
88
|
+
|
|
89
|
+
# print(f"{response=}")
|
|
90
|
+
# Parse the response as JSON
|
|
91
|
+
response_data = json.loads(response.decode("utf-8"))
|
|
92
|
+
if response_data["error"] != "success":
|
|
93
|
+
logging.warning(f"send command: {command} response data: {response_data}")
|
|
94
|
+
# Return the 'data' field which contains the playback position
|
|
95
|
+
return response_data.get("data")
|
|
96
|
+
except FileNotFoundError:
|
|
97
|
+
logger.critical("Error: Socket file not found.")
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.critical(f"An error occurred: {e}")
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def time_pos(self):
|
|
104
|
+
time_pos = self.send_command({"command": ["get_property", "time-pos"]})
|
|
105
|
+
return time_pos
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def duration(self):
|
|
109
|
+
duration_ = self.send_command({"command": ["get_property", "duration"]})
|
|
110
|
+
return duration_
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def video_zoom(self):
|
|
114
|
+
return self.send_command({"command": ["get_property", "video-zoom"]})
|
|
115
|
+
|
|
116
|
+
@video_zoom.setter
|
|
117
|
+
def video_zoom(self, value):
|
|
118
|
+
self.send_command({"command": ["set_property", "video-zoom", value]})
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def pause(self):
|
|
123
|
+
return self.send_command({"command": ["get_property", "pause"]})
|
|
124
|
+
|
|
125
|
+
@pause.setter
|
|
126
|
+
def pause(self, value):
|
|
127
|
+
return self.send_command({"command": ["set_property", "pause", value]})
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def estimated_frame_number(self):
|
|
131
|
+
return self.send_command({"command": ["get_property", "estimated-frame-number"]})
|
|
132
|
+
|
|
133
|
+
def stop(self):
|
|
134
|
+
self.send_command({"command": ["stop"]})
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def playlist(self):
|
|
139
|
+
return self.send_command({"command": ["get_property", "playlist"]})
|
|
140
|
+
|
|
141
|
+
def playlist_next(self):
|
|
142
|
+
self.send_command({"command": ["playlist-next"]})
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
def playlist_prev(self):
|
|
146
|
+
self.send_command({"command": ["playlist-prev"]})
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def playlist_pos(self):
|
|
151
|
+
return self.send_command({"command": ["get_property", "playlist-pos"]})
|
|
152
|
+
|
|
153
|
+
@playlist_pos.setter
|
|
154
|
+
def playlist_pos(self, value):
|
|
155
|
+
return self.send_command({"command": ["set_property", "playlist-pos", value]})
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def playlist_count(self):
|
|
159
|
+
return self.send_command({"command": ["get_property", "playlist-count"]})
|
|
160
|
+
|
|
161
|
+
def playlist_append(self, media):
|
|
162
|
+
return self.send_command({"command": ["loadfile", media, "append"]})
|
|
163
|
+
|
|
164
|
+
def wait_until_playing(self):
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
def seek(self, value, mode: str):
|
|
168
|
+
self.send_command({"command": ["seek", value, mode]})
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def playback_time(self):
|
|
173
|
+
playback_time_ = self.send_command({"command": ["get_property", "playback-time"]})
|
|
174
|
+
return playback_time_
|
|
175
|
+
|
|
176
|
+
def frame_step(self):
|
|
177
|
+
self.send_command({"command": ["frame-step"]})
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
def frame_back_step(self):
|
|
181
|
+
self.send_command({"command": ["frame-back-step"]})
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
def screenshot_to_file(self, value):
|
|
185
|
+
self.send_command({"command": ["screenshot-to-file", value, "video"]})
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def speed(self):
|
|
190
|
+
return self.send_command({"command": ["get_property", "speed"]})
|
|
191
|
+
|
|
192
|
+
@speed.setter
|
|
193
|
+
def speed(self, value):
|
|
194
|
+
self.send_command({"command": ["set_property", "speed", value]})
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def video_rotate(self):
|
|
199
|
+
return self.send_command({"command": ["get_property", "video-rotate"]})
|
|
200
|
+
|
|
201
|
+
@video_rotate.setter
|
|
202
|
+
def video_rotate(self, value):
|
|
203
|
+
self.send_command({"command": ["set_property", "video-rotate", value]})
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def sub_visibility(self):
|
|
208
|
+
return self.send_command({"command": ["get_property", "sub-visibility"]})
|
|
209
|
+
|
|
210
|
+
@sub_visibility.setter
|
|
211
|
+
def sub_visibility(self, value):
|
|
212
|
+
self.send_command({"command": ["set_property", "sub-visibility", value]})
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def brightness(self):
|
|
217
|
+
return self.send_command({"command": ["get_property", "brightness"]})
|
|
218
|
+
|
|
219
|
+
@brightness.setter
|
|
220
|
+
def brightness(self, value):
|
|
221
|
+
self.send_command({"command": ["set_property", "brightness", value]})
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def contrast(self):
|
|
226
|
+
return self.send_command({"command": ["get_property", "contrast"]})
|
|
227
|
+
|
|
228
|
+
@contrast.setter
|
|
229
|
+
def contrast(self, value):
|
|
230
|
+
self.send_command({"command": ["set_property", "contrast", value]})
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def saturation(self):
|
|
235
|
+
return self.send_command({"command": ["get_property", "saturation"]})
|
|
236
|
+
|
|
237
|
+
@saturation.setter
|
|
238
|
+
def saturation(self, value):
|
|
239
|
+
self.send_command({"command": ["set_property", "saturation", value]})
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def gamma(self):
|
|
244
|
+
return self.send_command({"command": ["get_property", "gamma"]})
|
|
245
|
+
|
|
246
|
+
@gamma.setter
|
|
247
|
+
def gamma(self, value):
|
|
248
|
+
self.send_command({"command": ["set_property", "gamma", value]})
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def hue(self):
|
|
253
|
+
return self.send_command({"command": ["get_property", "hue"]})
|
|
254
|
+
|
|
255
|
+
@hue.setter
|
|
256
|
+
def hue(self, value):
|
|
257
|
+
self.send_command({"command": ["set_property", "hue", value]})
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def container_fps(self):
|
|
262
|
+
return self.send_command({"command": ["get_property", "container-fps"]})
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def width(self):
|
|
266
|
+
return self.send_command({"command": ["get_property", "width"]})
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def height(self):
|
|
270
|
+
return self.send_command({"command": ["get_property", "height"]})
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def video_format(self):
|
|
274
|
+
return self.send_command({"command": ["get_property", "video-format"]})
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def deinterlace(self):
|
|
278
|
+
return self.send_command({"command": ["get_property", "deinterlace"]})
|
|
279
|
+
|
|
280
|
+
@deinterlace.setter
|
|
281
|
+
def deinterlace(self, value):
|
|
282
|
+
self.send_command({"command": ["set_property", "deinterlace", value]})
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def audio_bitrate(self):
|
|
287
|
+
return self.send_command({"command": ["get_property", "audio-bitrate"]})
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def eof_reached(self):
|
|
291
|
+
return self.send_command({"command": ["get_property", "eof-reached"]})
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def core_idle(self):
|
|
295
|
+
return self.send_command({"command": ["get_property", "core-idle"]})
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def video_pan_x(self):
|
|
299
|
+
return self.send_command({"command": ["get_property", "video-pan-x"]})
|
|
300
|
+
|
|
301
|
+
@video_pan_x.setter
|
|
302
|
+
def video_pan_x(self, value):
|
|
303
|
+
self.send_command({"command": ["set_property", "video-pan-x", value]})
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def video_pan_y(self):
|
|
308
|
+
return self.send_command({"command": ["get_property", "video-pan-y"]})
|
|
309
|
+
|
|
310
|
+
@video_pan_y.setter
|
|
311
|
+
def video_pan_y(self, value):
|
|
312
|
+
self.send_command({"command": ["set_property", "video-pan-y", value]})
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
"""
|
|
317
|
+
@property
|
|
318
|
+
def xxx(self):
|
|
319
|
+
return self.send_command({"command": ["get_property", "xxx"]})
|
|
320
|
+
|
|
321
|
+
@xxx.setter
|
|
322
|
+
def xxx(self, value):
|
|
323
|
+
self.send_command({"command": ["set_property", "xxx", value]})
|
|
324
|
+
return
|
|
325
|
+
"""
|
boris/irr.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
|
|
|
7
7
|
This program is free software; you can redistribute it and/or modify
|
|
@@ -25,7 +25,7 @@ import logging
|
|
|
25
25
|
from decimal import Decimal as dec
|
|
26
26
|
|
|
27
27
|
import numpy as np
|
|
28
|
-
from
|
|
28
|
+
from PySide6.QtWidgets import QInputDialog, QMessageBox
|
|
29
29
|
|
|
30
30
|
from . import config as cfg
|
|
31
31
|
from . import db_functions, dialog, project_functions, select_subj_behav
|
|
@@ -126,7 +126,7 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
|
|
|
126
126
|
first_event = cursor.execute(
|
|
127
127
|
(
|
|
128
128
|
"SELECT min(start) FROM aggregated_events "
|
|
129
|
-
f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
129
|
+
f"WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "
|
|
130
130
|
),
|
|
131
131
|
(obsid1, obsid2) + tuple(selected_subjects),
|
|
132
132
|
).fetchone()[0]
|
|
@@ -134,27 +134,18 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
|
|
|
134
134
|
logging.debug(f"first_event: {first_event}")
|
|
135
135
|
|
|
136
136
|
last_event = cursor.execute(
|
|
137
|
-
(
|
|
138
|
-
"SELECT max(stop) FROM aggregated_events "
|
|
139
|
-
f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
140
|
-
),
|
|
137
|
+
(f"SELECT max(stop) FROM aggregated_events WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
141
138
|
(obsid1, obsid2) + tuple(selected_subjects),
|
|
142
139
|
).fetchone()[0]
|
|
143
140
|
|
|
144
141
|
logging.debug(f"last_event: {last_event}")
|
|
145
142
|
|
|
146
143
|
nb_events1 = cursor.execute(
|
|
147
|
-
(
|
|
148
|
-
"SELECT COUNT(*) FROM aggregated_events "
|
|
149
|
-
f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
150
|
-
),
|
|
144
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
151
145
|
(obsid1,) + tuple(selected_subjects),
|
|
152
146
|
).fetchone()[0]
|
|
153
147
|
nb_events2 = cursor.execute(
|
|
154
|
-
(
|
|
155
|
-
"SELECT COUNT(*) FROM aggregated_events "
|
|
156
|
-
f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
157
|
-
),
|
|
148
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
158
149
|
(obsid2,) + tuple(selected_subjects),
|
|
159
150
|
).fetchone()[0]
|
|
160
151
|
|
|
@@ -162,10 +153,8 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
|
|
|
162
153
|
|
|
163
154
|
currentTime = dec(str(first_event))
|
|
164
155
|
while currentTime <= last_event:
|
|
165
|
-
|
|
166
156
|
for obsid in [obsid1, obsid2]:
|
|
167
157
|
for subject in selected_subjects:
|
|
168
|
-
|
|
169
158
|
s = subj_behav_modif(cursor, obsid, subject, currentTime, interval, include_modifiers)
|
|
170
159
|
|
|
171
160
|
if s not in total_states:
|
|
@@ -185,11 +174,9 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
|
|
|
185
174
|
seq2 = {}
|
|
186
175
|
currentTime = dec(str(first_event))
|
|
187
176
|
while currentTime <= last_event:
|
|
188
|
-
|
|
189
177
|
seq1[currentTime] = []
|
|
190
178
|
seq2[currentTime] = []
|
|
191
179
|
for subject in selected_subjects:
|
|
192
|
-
|
|
193
180
|
s1 = subj_behav_modif(cursor, obsid1, subject, currentTime, interval, include_modifiers)
|
|
194
181
|
s2 = subj_behav_modif(cursor, obsid2, subject, currentTime, interval, include_modifiers)
|
|
195
182
|
|
|
@@ -211,11 +198,7 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
|
|
|
211
198
|
logging.debug(f"contingency_table:\n {contingency_table}")
|
|
212
199
|
|
|
213
200
|
template = (
|
|
214
|
-
"Observation: {obsid1}\n"
|
|
215
|
-
"number of events: {nb_events1}\n\n"
|
|
216
|
-
"Observation: {obsid2}\n"
|
|
217
|
-
"number of events: {nb_events2:.0f}\n\n"
|
|
218
|
-
"K = {K:.3f}"
|
|
201
|
+
"Observation: {obsid1}\nnumber of events: {nb_events1}\n\nObservation: {obsid2}\nnumber of events: {nb_events2:.0f}\n\nK = {K:.3f}"
|
|
219
202
|
)
|
|
220
203
|
|
|
221
204
|
# out += "Observation length: <b>{:.3f} s</b><br>".format(self.observationTotalMediaLength(obsid1))
|
|
@@ -298,8 +281,8 @@ def irr_cohen_kappa(self):
|
|
|
298
281
|
selected_observations,
|
|
299
282
|
start_coding=dec("NaN"),
|
|
300
283
|
end_coding=dec("NaN"),
|
|
301
|
-
|
|
302
|
-
|
|
284
|
+
show_include_modifiers=True,
|
|
285
|
+
show_exclude_non_coded_behaviors=False,
|
|
303
286
|
n_observations=len(selected_observations),
|
|
304
287
|
)
|
|
305
288
|
if parameters == {}:
|
|
@@ -309,9 +292,7 @@ def irr_cohen_kappa(self):
|
|
|
309
292
|
return
|
|
310
293
|
|
|
311
294
|
# ask for time slice
|
|
312
|
-
i, ok = QInputDialog.getDouble(
|
|
313
|
-
self, "IRR - Cohen's Kappa (time-unit)", "Time unit (in seconds):", 1.0, 0.001, 86400, 3
|
|
314
|
-
)
|
|
295
|
+
i, ok = QInputDialog.getDouble(self, "IRR - Cohen's Kappa (time-unit)", "Time unit (in seconds):", 1.0, 0.001, 86400, 3)
|
|
315
296
|
if not ok:
|
|
316
297
|
return
|
|
317
298
|
interval = util.float2decimal(i)
|
|
@@ -354,7 +335,7 @@ def irr_cohen_kappa(self):
|
|
|
354
335
|
out2 += "\t".join(["%8.6f" % x for x in irr_results[r, :]]) + "\n"
|
|
355
336
|
|
|
356
337
|
self.results = dialog.Results_dialog()
|
|
357
|
-
self.results.setWindowTitle(
|
|
338
|
+
self.results.setWindowTitle("BORIS - IRR - Cohen's Kappa (time-unit) analysis results")
|
|
358
339
|
self.results.ptText.setReadOnly(True)
|
|
359
340
|
if len(selected_observations) == 2:
|
|
360
341
|
self.results.ptText.appendPlainText(out)
|
|
@@ -363,9 +344,7 @@ def irr_cohen_kappa(self):
|
|
|
363
344
|
self.results.show()
|
|
364
345
|
|
|
365
346
|
|
|
366
|
-
def needleman_wunsch_identity(
|
|
367
|
-
cursor, obsid1: str, obsid2: str, interval, selected_subjects: list, include_modifiers: bool
|
|
368
|
-
):
|
|
347
|
+
def needleman_wunsch_identity(cursor, obsid1: str, obsid2: str, interval, selected_subjects: list, include_modifiers: bool):
|
|
369
348
|
"""
|
|
370
349
|
Needleman - Wunsch identity between 2 observations
|
|
371
350
|
|
|
@@ -408,22 +387,19 @@ def needleman_wunsch_identity(
|
|
|
408
387
|
align1 = align1[::-1]
|
|
409
388
|
align2 = align2[::-1]
|
|
410
389
|
|
|
411
|
-
i
|
|
412
|
-
|
|
390
|
+
i = 0
|
|
413
391
|
symbol = []
|
|
414
|
-
found = 0
|
|
415
392
|
score = 0
|
|
416
393
|
identity = 0
|
|
417
394
|
for i in range(0, len(align1)):
|
|
418
395
|
if align1[i] == align2[i]:
|
|
419
396
|
symbol.append(align1[i])
|
|
420
|
-
identity
|
|
397
|
+
identity += 1
|
|
421
398
|
score += match_score(align1[i], align2[i])
|
|
422
399
|
|
|
423
400
|
elif align1[i] != align2[i] and align1[i] != "-" and align2[i] != "-":
|
|
424
401
|
score += match_score(align1[i], align2[i])
|
|
425
402
|
symbol.append(" ")
|
|
426
|
-
found = 0
|
|
427
403
|
|
|
428
404
|
# if one of them is a gap, output a space
|
|
429
405
|
elif align1[i] == "-" or align2[i] == "-":
|
|
@@ -487,52 +463,43 @@ def needleman_wunsch_identity(
|
|
|
487
463
|
first_event = cursor.execute(
|
|
488
464
|
(
|
|
489
465
|
"SELECT min(start) FROM aggregated_events "
|
|
490
|
-
f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
466
|
+
f"WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "
|
|
491
467
|
),
|
|
492
468
|
(obsid1, obsid2) + tuple(selected_subjects),
|
|
493
469
|
).fetchone()[0]
|
|
494
470
|
|
|
495
471
|
if first_event is None:
|
|
496
472
|
logging.debug(f"An observation has no recorded events: {obsid1} or {obsid2}")
|
|
473
|
+
|
|
497
474
|
return -100, f"An observation has no recorded events: {obsid1} {obsid2}"
|
|
498
475
|
|
|
499
476
|
logging.debug(f"first_event: {first_event}")
|
|
500
477
|
|
|
501
478
|
last_event = cursor.execute(
|
|
502
|
-
(
|
|
503
|
-
"SELECT max(start) FROM aggregated_events "
|
|
504
|
-
f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
505
|
-
),
|
|
479
|
+
(f"SELECT max(stop) FROM aggregated_events WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
506
480
|
(obsid1, obsid2) + tuple(selected_subjects),
|
|
507
481
|
).fetchone()[0]
|
|
508
482
|
|
|
509
483
|
logging.debug(f"last_event: {last_event}")
|
|
510
484
|
|
|
511
485
|
nb_events1 = cursor.execute(
|
|
512
|
-
(
|
|
513
|
-
"SELECT COUNT(*) FROM aggregated_events "
|
|
514
|
-
f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
515
|
-
),
|
|
486
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
516
487
|
(obsid1,) + tuple(selected_subjects),
|
|
517
488
|
).fetchone()[0]
|
|
518
489
|
|
|
519
490
|
nb_events2 = cursor.execute(
|
|
520
|
-
(
|
|
521
|
-
"SELECT COUNT(*) FROM aggregated_events "
|
|
522
|
-
f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
523
|
-
),
|
|
491
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
524
492
|
(obsid2,) + tuple(selected_subjects),
|
|
525
493
|
).fetchone()[0]
|
|
526
494
|
|
|
527
|
-
seq1
|
|
495
|
+
seq1: dict = {}
|
|
496
|
+
seq2: dict = {}
|
|
528
497
|
|
|
529
498
|
currentTime = dec(str(first_event))
|
|
530
499
|
while currentTime <= last_event:
|
|
531
|
-
|
|
532
500
|
seq1[currentTime], seq2[currentTime] = [], []
|
|
533
501
|
|
|
534
502
|
for subject in selected_subjects:
|
|
535
|
-
|
|
536
503
|
s1 = subj_behav_modif(cursor, obsid1, subject, currentTime, interval, include_modifiers)
|
|
537
504
|
s2 = subj_behav_modif(cursor, obsid2, subject, currentTime, interval, include_modifiers)
|
|
538
505
|
|
|
@@ -574,9 +541,7 @@ def needleman_wunch(self):
|
|
|
574
541
|
if not selected_observations:
|
|
575
542
|
return
|
|
576
543
|
if len(selected_observations) < 2:
|
|
577
|
-
QMessageBox.information(
|
|
578
|
-
self, cfg.programName, "You have to select at least 2 observations for Needleman-Wunsch similarity"
|
|
579
|
-
)
|
|
544
|
+
QMessageBox.information(self, cfg.programName, "You have to select at least 2 observations for Needleman-Wunsch similarity")
|
|
580
545
|
return
|
|
581
546
|
|
|
582
547
|
# check if coded behaviors are defined in ethogram
|
|
@@ -606,8 +571,8 @@ def needleman_wunch(self):
|
|
|
606
571
|
selected_observations,
|
|
607
572
|
start_coding=dec("NaN"),
|
|
608
573
|
end_coding=dec("NaN"),
|
|
609
|
-
|
|
610
|
-
|
|
574
|
+
show_include_modifiers=True,
|
|
575
|
+
show_exclude_non_coded_behaviors=False,
|
|
611
576
|
n_observations=len(selected_observations),
|
|
612
577
|
)
|
|
613
578
|
|
|
@@ -631,9 +596,7 @@ def needleman_wunch(self):
|
|
|
631
596
|
|
|
632
597
|
cursor = db_connector.cursor()
|
|
633
598
|
out = (
|
|
634
|
-
"Needleman-Wunsch similarity\n\n"
|
|
635
|
-
f"Time unit: {interval:.3f} s\n"
|
|
636
|
-
f"Selected subjects: {', '.join(parameters[cfg.SELECTED_SUBJECTS])}\n\n"
|
|
599
|
+
f"Needleman-Wunsch similarity\n\nTime unit: {interval:.3f} s\nSelected subjects: {', '.join(parameters[cfg.SELECTED_SUBJECTS])}\n\n"
|
|
637
600
|
)
|
|
638
601
|
mem_done = []
|
|
639
602
|
nws_results = np.ones((len(selected_observations), len(selected_observations)))
|
|
@@ -662,7 +625,7 @@ def needleman_wunch(self):
|
|
|
662
625
|
out2 += "\t".join([f"{x:8.6f}" for x in nws_results[r, :]]) + "\n"
|
|
663
626
|
|
|
664
627
|
self.results = dialog.Results_dialog()
|
|
665
|
-
self.results.setWindowTitle(cfg.programName
|
|
628
|
+
self.results.setWindowTitle(f"{cfg.programName} - Needleman-Wunsch similarity")
|
|
666
629
|
self.results.ptText.setReadOnly(True)
|
|
667
630
|
if len(selected_observations) == 2:
|
|
668
631
|
self.results.ptText.appendPlainText(out)
|