boris-behav-obs 9.6.1__tar.gz → 9.6.3__tar.gz
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.
- {boris_behav_obs-9.6.1/boris_behav_obs.egg-info → boris_behav_obs-9.6.3}/PKG-INFO +12 -8
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/README.md +9 -6
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/add_modifier.py +1 -5
- boris_behav_obs-9.6.3/boris/analysis_plugins/irr_cohen_kappa.py +72 -0
- boris_behav_obs-9.6.3/boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +77 -0
- boris_behav_obs-9.6.3/boris/analysis_plugins/irr_weighted_cohen_kappa.py +120 -0
- boris_behav_obs-9.6.3/boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +125 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/analysis_plugins/time_budget.py +0 -4
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/behav_coding_map_creator.py +0 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/boris_cli.py +1 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/coding_pad.py +0 -2
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/core.py +11 -28
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/core_qrc.py +3 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/db_functions.py +4 -4
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/edit_event.py +0 -9
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/exclusion_matrix.py +1 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/export_events.py +63 -71
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/gui_utilities.py +0 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/irr.py +10 -22
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/menu_options.py +2 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/modifier_coding_map_creator.py +0 -2
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/observation_operations.py +2 -4
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/param_panel.py +0 -4
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/plot_spectrogram_rt.py +2 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/plot_waveform_rt.py +2 -1
- boris_behav_obs-9.6.3/boris/portion/__init__.py +31 -0
- boris_behav_obs-9.6.3/boris/portion/const.py +95 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/portion/dict.py +5 -5
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/portion/func.py +2 -2
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/portion/interval.py +21 -41
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/portion/io.py +41 -32
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/project_functions.py +2 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/state_events.py +1 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/time_budget_functions.py +0 -9
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/transitions.py +1 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/version.py +2 -2
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/video_equalizer.py +0 -2
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/view_df.py +0 -2
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/write_event.py +4 -13
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3/boris_behav_obs.egg-info}/PKG-INFO +12 -8
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris_behav_obs.egg-info/SOURCES.txt +4 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris_behav_obs.egg-info/requires.txt +2 -1
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/pyproject.toml +4 -3
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_db_functions.py +1 -6
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_export_observation.py +3 -21
- boris_behav_obs-9.6.3/tests/test_irr.py +300 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_observation_gui.py +29 -27
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_otx_parser.py +6 -7
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_preferences_gui.py +8 -12
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_project_functions.py +37 -54
- boris_behav_obs-9.6.1/boris/portion/__init__.py +0 -21
- boris_behav_obs-9.6.1/boris/portion/const.py +0 -78
- boris_behav_obs-9.6.1/tests/test_irr.py +0 -342
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/LICENSE.TXT +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/MANIFEST.in +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/README.TXT +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/__init__.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/__main__.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/about.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/add_modifier_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/advanced_event_filtering.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/analysis_plugins/__init__.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/analysis_plugins/_latency.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/analysis_plugins/list_of_dataframe_columns.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/analysis_plugins/number_of_occurences.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/behavior_binary_table.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/behaviors_coding_map.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/cmd_arguments.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/config.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/config_file.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/connections.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/converters.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/converters_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/cooccurence.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/core_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/dev.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/dialog.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/duration_widget.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/edit_event_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/event_operations.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/events_cursor.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/events_snapshots.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/export_observation.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/external_processes.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/geometric_measurement.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/image_overlay.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/import_observations.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/latency.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/measurement_widget.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/media_file.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/modifiers_coding_map.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/mpv-1.0.3.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/mpv.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/mpv2.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/observation.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/observation_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/observations_list.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/otx_parser.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/param_panel_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/player_dock_widget.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/plot_data_module.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/plot_events.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/plot_events_rt.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/plugins.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/preferences.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/preferences_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/project.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/project_import_export.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/project_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/qrc_boris.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/qrc_boris5.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/select_modifiers.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/select_observations.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/select_subj_behav.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/subjects_pad.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/synthetic_time_budget.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/time_budget_widget.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/utilities.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/video_equalizer_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/video_operations.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris/view_df_ui.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris_behav_obs.egg-info/entry_points.txt +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/boris_behav_obs.egg-info/top_level.txt +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/setup.cfg +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_time_budget.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_utilities.py +0 -0
- {boris_behav_obs-9.6.1 → boris_behav_obs-9.6.3}/tests/test_utilities2.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: boris-behav-obs
|
|
3
|
-
Version: 9.6.
|
|
3
|
+
Version: 9.6.3
|
|
4
4
|
Summary: BORIS - Behavioral Observation Research Interactive Software
|
|
5
5
|
Author-email: Olivier Friard <olivier.friard@unito.it>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.12
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.TXT
|
|
21
21
|
Requires-Dist: exifread>=3.0.0
|
|
22
|
-
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: numpy==2.3.2
|
|
23
23
|
Requires-Dist: matplotlib>=3.3.3
|
|
24
24
|
Requires-Dist: pandas>=2.2.2
|
|
25
25
|
Requires-Dist: tablib[cli,html,ods,pandas,xls,xlsx]>=3
|
|
@@ -27,6 +27,7 @@ Requires-Dist: pyreadr
|
|
|
27
27
|
Requires-Dist: pyside6==6.9
|
|
28
28
|
Requires-Dist: hachoir>=3.3.0
|
|
29
29
|
Requires-Dist: scipy>=1.15.3
|
|
30
|
+
Requires-Dist: scikit-learn>=1.7.1
|
|
30
31
|
Provides-Extra: dev
|
|
31
32
|
Requires-Dist: ruff; extra == "dev"
|
|
32
33
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -48,7 +49,7 @@ You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to
|
|
|
48
49
|
|
|
49
50
|
It provides also some analysis tools like time budget and some plotting functions.
|
|
50
51
|
|
|
51
|
-
The BORIS paper has more than [ citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications.
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
|
|
@@ -58,13 +59,17 @@ See the official [BORIS web site](https://www.boris.unito.it).
|
|
|
58
59
|
[](https://www.python.org)
|
|
59
60
|

|
|
60
61
|

|
|
62
|
+
[](https://pypi.org/project/boris-behav-obs/)
|
|
63
|
+
|
|
61
64
|
[](https://pepy.tech/project/boris-behav-obs)
|
|
62
65
|

|
|
63
|
-
|
|
64
|
-

|
|
66
|
+

|
|
65
67
|
|
|
68
|
+

|
|
66
69
|
|
|
67
70
|
|
|
71
|
+

|
|
72
|
+
[](https://github.com/olivierfriard/BORIS/stargazers)
|
|
68
73
|
|
|
69
74
|
# Documentation
|
|
70
75
|
|
|
@@ -81,8 +86,7 @@ Some [video tutorials](https://www.boris.unito.it/video_tutorials/) are availabl
|
|
|
81
86
|
# Bug reports and feature requests
|
|
82
87
|
|
|
83
88
|
|
|
84
|
-
To search for bugs, report them or request a feature, please use the
|
|
85
|
-
https://github.com/olivierfriard/BORIS/issues
|
|
89
|
+
To search for bugs, report them or request a feature, please use the [GitHub issues tracker](https://github.com/olivierfriard/BORIS/issues)
|
|
86
90
|
|
|
87
91
|
|
|
88
92
|
|
|
@@ -128,7 +132,7 @@ GNU General Public License for more details.
|
|
|
128
132
|
|
|
129
133
|
Distributed with a [GPL v.3 license](LICENSE.TXT).
|
|
130
134
|
|
|
131
|
-
Copyright (C) 2012-
|
|
135
|
+
Copyright (C) 2012-2025 Olivier Friard
|
|
132
136
|
|
|
133
137
|
|
|
134
138
|
|
|
@@ -11,7 +11,7 @@ You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to
|
|
|
11
11
|
|
|
12
12
|
It provides also some analysis tools like time budget and some plotting functions.
|
|
13
13
|
|
|
14
|
-
The BORIS paper has more than [ citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications.
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
|
|
@@ -21,13 +21,17 @@ See the official [BORIS web site](https://www.boris.unito.it).
|
|
|
21
21
|
[](https://www.python.org)
|
|
22
22
|

|
|
23
23
|

|
|
24
|
+
[](https://pypi.org/project/boris-behav-obs/)
|
|
25
|
+
|
|
24
26
|
[](https://pepy.tech/project/boris-behav-obs)
|
|
25
27
|

|
|
26
|
-
|
|
27
|
-

|
|
28
|
+

|
|
28
29
|
|
|
30
|
+

|
|
29
31
|
|
|
30
32
|
|
|
33
|
+

|
|
34
|
+
[](https://github.com/olivierfriard/BORIS/stargazers)
|
|
31
35
|
|
|
32
36
|
# Documentation
|
|
33
37
|
|
|
@@ -44,8 +48,7 @@ Some [video tutorials](https://www.boris.unito.it/video_tutorials/) are availabl
|
|
|
44
48
|
# Bug reports and feature requests
|
|
45
49
|
|
|
46
50
|
|
|
47
|
-
To search for bugs, report them or request a feature, please use the
|
|
48
|
-
https://github.com/olivierfriard/BORIS/issues
|
|
51
|
+
To search for bugs, report them or request a feature, please use the [GitHub issues tracker](https://github.com/olivierfriard/BORIS/issues)
|
|
49
52
|
|
|
50
53
|
|
|
51
54
|
|
|
@@ -91,7 +94,7 @@ GNU General Public License for more details.
|
|
|
91
94
|
|
|
92
95
|
Distributed with a [GPL v.3 license](LICENSE.TXT).
|
|
93
96
|
|
|
94
|
-
Copyright (C) 2012-
|
|
97
|
+
Copyright (C) 2012-2025 Olivier Friard
|
|
95
98
|
|
|
96
99
|
|
|
97
100
|
|
|
@@ -123,11 +123,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
123
123
|
if (
|
|
124
124
|
dialog.MessageDialog(
|
|
125
125
|
cfg.programName,
|
|
126
|
-
(
|
|
127
|
-
"You are working on a behavior.<br>"
|
|
128
|
-
"If you close the window it will be lost.<br>"
|
|
129
|
-
"Do you want to change modifiers set"
|
|
130
|
-
),
|
|
126
|
+
("You are working on a behavior.<br>If you close the window it will be lost.<br>Do you want to change modifiers set"),
|
|
131
127
|
[cfg.CLOSE, cfg.CANCEL],
|
|
132
128
|
)
|
|
133
129
|
== cfg.CANCEL
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS plugin
|
|
3
|
+
|
|
4
|
+
Inter Rater Reliability (IRR) Unweighted Cohen Kappa
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from sklearn.metrics import cohen_kappa_score
|
|
9
|
+
|
|
10
|
+
__version__ = "0.0.1"
|
|
11
|
+
__version_date__ = "2025-08-25"
|
|
12
|
+
__plugin_name__ = "Inter Rater Reliability - Unweighted Cohen Kappa"
|
|
13
|
+
__author__ = "Olivier Friard - University of Torino - Italy"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(df: pd.DataFrame):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the Inter Rater Reliability - Unweighted Cohen Kappa
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# attribute a code for each interval
|
|
22
|
+
def get_code(t_start, obs):
|
|
23
|
+
for seg in obs:
|
|
24
|
+
if t_start >= seg[0] and t_start < seg[1]:
|
|
25
|
+
return seg[2]
|
|
26
|
+
return ""
|
|
27
|
+
|
|
28
|
+
# Get unique values as a numpy array
|
|
29
|
+
unique_obs = df["Observation id"].unique()
|
|
30
|
+
|
|
31
|
+
# Convert to a list
|
|
32
|
+
unique_obs_list = unique_obs.tolist()
|
|
33
|
+
|
|
34
|
+
# Convert to tuples grouped by observation
|
|
35
|
+
grouped = {
|
|
36
|
+
obs: [
|
|
37
|
+
(row[0], row[1], row[2] + "|" + row[3]) # concatenate subject and behavior with |
|
|
38
|
+
for row in group[["Start (s)", "Stop (s)", "Subject", "Behavior"]].itertuples(index=False, name=None)
|
|
39
|
+
]
|
|
40
|
+
for obs, group in df.groupby("Observation id")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ck_results: dict = {}
|
|
44
|
+
for idx1, obs_id1 in enumerate(unique_obs_list):
|
|
45
|
+
obs1 = grouped[obs_id1]
|
|
46
|
+
|
|
47
|
+
ck_results[(obs_id1, obs_id1)] = "1.000"
|
|
48
|
+
|
|
49
|
+
for obs_id2 in unique_obs_list[idx1 + 1 :]:
|
|
50
|
+
obs2 = grouped[obs_id2]
|
|
51
|
+
|
|
52
|
+
# get all the break points
|
|
53
|
+
time_points = sorted(set([t for seg in obs1 for t in seg[:2]] + [t for seg in obs2 for t in seg[:2]]))
|
|
54
|
+
|
|
55
|
+
# elementary intervals
|
|
56
|
+
elementary_intervals = [(time_points[i], time_points[i + 1]) for i in range(len(time_points) - 1)]
|
|
57
|
+
|
|
58
|
+
obs1_codes = [get_code(t[0], obs1) for t in elementary_intervals]
|
|
59
|
+
|
|
60
|
+
obs2_codes = [get_code(t[0], obs2) for t in elementary_intervals]
|
|
61
|
+
|
|
62
|
+
# Cohen's Kappa
|
|
63
|
+
kappa = cohen_kappa_score(obs1_codes, obs2_codes)
|
|
64
|
+
print(f"{obs_id1} - {obs_id2}: Cohen's Kappa : {kappa:.3f}")
|
|
65
|
+
|
|
66
|
+
ck_results[(obs_id1, obs_id2)] = f"{kappa:.3f}"
|
|
67
|
+
ck_results[(obs_id2, obs_id1)] = f"{kappa:.3f}"
|
|
68
|
+
|
|
69
|
+
# DataFrame conversion
|
|
70
|
+
df_results = pd.Series(ck_results).unstack()
|
|
71
|
+
|
|
72
|
+
return df_results
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS plugin
|
|
3
|
+
|
|
4
|
+
Inter Rater Reliability (IRR) Unweighted Cohen Kappa with modifiers
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from sklearn.metrics import cohen_kappa_score
|
|
9
|
+
|
|
10
|
+
__version__ = "0.0.1"
|
|
11
|
+
__version_date__ = "2025-08-25"
|
|
12
|
+
__plugin_name__ = "Inter Rater Reliability - Unweighted Cohen Kappa with modifiers"
|
|
13
|
+
__author__ = "Olivier Friard - University of Torino - Italy"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(df: pd.DataFrame):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the Inter Rater Reliability - Unweighted Cohen Kappa with modifiers
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# attribute a code for each interval
|
|
22
|
+
def get_code(t_start, obs):
|
|
23
|
+
for seg in obs:
|
|
24
|
+
if t_start >= seg[0] and t_start < seg[1]:
|
|
25
|
+
return seg[2]
|
|
26
|
+
return ""
|
|
27
|
+
|
|
28
|
+
# Get unique values as a numpy array
|
|
29
|
+
unique_obs = df["Observation id"].unique()
|
|
30
|
+
|
|
31
|
+
# Convert to a list
|
|
32
|
+
unique_obs_list = unique_obs.tolist()
|
|
33
|
+
|
|
34
|
+
# Convert to tuples grouped by observation
|
|
35
|
+
grouped: dict = {}
|
|
36
|
+
modifiers: list = []
|
|
37
|
+
for col in df.columns:
|
|
38
|
+
if isinstance(col, tuple):
|
|
39
|
+
modifiers.append(col)
|
|
40
|
+
|
|
41
|
+
for obs, group in df.groupby("Observation id"):
|
|
42
|
+
o: list = []
|
|
43
|
+
for row in group[["Start (s)", "Stop (s)", "Subject", "Behavior"] + modifiers].itertuples(index=False, name=None):
|
|
44
|
+
modif_list = [row[i] for idx, i in enumerate(range(4, 4 + len(modifiers))) if modifiers[idx][0] == row[3]]
|
|
45
|
+
o.append((row[0], row[1], row[2] + "|" + row[3] + "|" + ",".join(modif_list)))
|
|
46
|
+
grouped[obs] = o
|
|
47
|
+
|
|
48
|
+
ck_results: dict = {}
|
|
49
|
+
for idx1, obs_id1 in enumerate(unique_obs_list):
|
|
50
|
+
obs1 = grouped[obs_id1]
|
|
51
|
+
|
|
52
|
+
ck_results[(obs_id1, obs_id1)] = "1.000"
|
|
53
|
+
|
|
54
|
+
for obs_id2 in unique_obs_list[idx1 + 1 :]:
|
|
55
|
+
obs2 = grouped[obs_id2]
|
|
56
|
+
|
|
57
|
+
# get all the break points
|
|
58
|
+
time_points = sorted(set([t for seg in obs1 for t in seg[:2]] + [t for seg in obs2 for t in seg[:2]]))
|
|
59
|
+
|
|
60
|
+
# elementary intervals
|
|
61
|
+
elementary_intervals = [(time_points[i], time_points[i + 1]) for i in range(len(time_points) - 1)]
|
|
62
|
+
|
|
63
|
+
obs1_codes = [get_code(t[0], obs1) for t in elementary_intervals]
|
|
64
|
+
|
|
65
|
+
obs2_codes = [get_code(t[0], obs2) for t in elementary_intervals]
|
|
66
|
+
|
|
67
|
+
# Cohen's Kappa
|
|
68
|
+
kappa = cohen_kappa_score(obs1_codes, obs2_codes)
|
|
69
|
+
print(f"{obs_id1} - {obs_id2}: Cohen's Kappa : {kappa:.3f}")
|
|
70
|
+
|
|
71
|
+
ck_results[(obs_id1, obs_id2)] = f"{kappa:.3f}"
|
|
72
|
+
ck_results[(obs_id2, obs_id1)] = f"{kappa:.3f}"
|
|
73
|
+
|
|
74
|
+
# DataFrame conversion
|
|
75
|
+
df_results = pd.Series(ck_results).unstack()
|
|
76
|
+
|
|
77
|
+
return df_results
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS plugin
|
|
3
|
+
|
|
4
|
+
Inter Rater Reliability (IRR) Weighted Cohen Kappa
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from typing import List, Tuple, Dict, Optional
|
|
9
|
+
|
|
10
|
+
__version__ = "0.0.1"
|
|
11
|
+
__version_date__ = "2025-08-25"
|
|
12
|
+
__plugin_name__ = "Inter Rater Reliability - Weighted Cohen Kappa"
|
|
13
|
+
__author__ = "Olivier Friard - University of Torino - Italy"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(df: pd.DataFrame):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the Inter Rater Reliability - Weighted Cohen Kappa
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def cohen_kappa_weighted_by_time(
|
|
22
|
+
obs1: List[Tuple[float, float, str]], obs2: List[Tuple[float, float, str]]
|
|
23
|
+
) -> Tuple[float, float, float, Dict[Tuple[Optional[str], Optional[str]], float]]:
|
|
24
|
+
"""
|
|
25
|
+
Compute Cohen's Kappa weighted by time duration.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
obs1: List of (start_time, end_time, code) for observer 1
|
|
29
|
+
obs2: List of (start_time, end_time, code) for observer 2
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
kappa (float): Cohen's Kappa weighted by duration
|
|
33
|
+
po (float): Observed agreement proportion (weighted)
|
|
34
|
+
pe (float): Expected agreement proportion by chance (weighted)
|
|
35
|
+
contingency (dict): Contingency table {(code1, code2): total_duration}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# 1. Collect all time boundaries from both observers
|
|
39
|
+
time_points = sorted(set([t for seg in obs1 for t in seg[:2]] + [t for seg in obs2 for t in seg[:2]]))
|
|
40
|
+
|
|
41
|
+
# 2. Build elementary intervals (non-overlapping time bins)
|
|
42
|
+
elementary_intervals = [(time_points[i], time_points[i + 1]) for i in range(len(time_points) - 1)]
|
|
43
|
+
|
|
44
|
+
# 3. Helper: get the active code for an observer at a given time
|
|
45
|
+
def get_code(t: float, obs: List[Tuple[float, float, str]]) -> Optional[str]:
|
|
46
|
+
for seg in obs:
|
|
47
|
+
if seg[0] <= t < seg[1]:
|
|
48
|
+
return seg[2]
|
|
49
|
+
return None # in case no segment covers this time
|
|
50
|
+
|
|
51
|
+
# 4. Build weighted contingency table (durations instead of counts)
|
|
52
|
+
contingency: Dict[Tuple[Optional[str], Optional[str]], float] = {}
|
|
53
|
+
total_time = 0.0
|
|
54
|
+
|
|
55
|
+
for start, end in elementary_intervals:
|
|
56
|
+
c1 = get_code(start, obs1)
|
|
57
|
+
c2 = get_code(start, obs2)
|
|
58
|
+
duration = end - start
|
|
59
|
+
total_time += duration
|
|
60
|
+
contingency[(c1, c2)] = contingency.get((c1, c2), 0.0) + duration
|
|
61
|
+
|
|
62
|
+
# 5. Observed agreement (po)
|
|
63
|
+
po = sum(duration for (c1, c2), duration in contingency.items() if c1 == c2) / total_time
|
|
64
|
+
|
|
65
|
+
# Marginal distributions for each observer
|
|
66
|
+
codes1: Dict[Optional[str], float] = {}
|
|
67
|
+
codes2: Dict[Optional[str], float] = {}
|
|
68
|
+
for (c1, c2), duration in contingency.items():
|
|
69
|
+
codes1[c1] = codes1.get(c1, 0.0) + duration
|
|
70
|
+
codes2[c2] = codes2.get(c2, 0.0) + duration
|
|
71
|
+
|
|
72
|
+
# 6. Expected agreement (pe), using marginal proportions
|
|
73
|
+
all_codes = set(codes1) | set(codes2)
|
|
74
|
+
pe = sum((codes1.get(c, 0.0) / total_time) * (codes2.get(c, 0.0) / total_time) for c in all_codes)
|
|
75
|
+
|
|
76
|
+
# 7. Kappa calculation
|
|
77
|
+
kappa = (po - pe) / (1 - pe) if (1 - pe) != 0 else 0.0
|
|
78
|
+
|
|
79
|
+
return kappa, po, pe, contingency
|
|
80
|
+
|
|
81
|
+
# Get unique values as a numpy array
|
|
82
|
+
unique_obs = df["Observation id"].unique()
|
|
83
|
+
|
|
84
|
+
# Convert to a list
|
|
85
|
+
unique_obs_list = unique_obs.tolist()
|
|
86
|
+
|
|
87
|
+
# Convert to tuples grouped by observation
|
|
88
|
+
grouped = {
|
|
89
|
+
obs: [
|
|
90
|
+
(row[0], row[1], row[2] + "|" + row[3]) # concatenate subject and behavior with |
|
|
91
|
+
for row in group[["Start (s)", "Stop (s)", "Subject", "Behavior"]].itertuples(index=False, name=None)
|
|
92
|
+
]
|
|
93
|
+
for obs, group in df.groupby("Observation id")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
ck_results: dict = {}
|
|
97
|
+
str_results: str = ""
|
|
98
|
+
for idx1, obs_id1 in enumerate(unique_obs_list):
|
|
99
|
+
obs1 = grouped[obs_id1]
|
|
100
|
+
|
|
101
|
+
ck_results[(obs_id1, obs_id1)] = "1.000"
|
|
102
|
+
|
|
103
|
+
for obs_id2 in unique_obs_list[idx1 + 1 :]:
|
|
104
|
+
obs2 = grouped[obs_id2]
|
|
105
|
+
|
|
106
|
+
# Cohen's Kappa
|
|
107
|
+
kappa, po, pe, table = cohen_kappa_weighted_by_time(obs1, obs2)
|
|
108
|
+
|
|
109
|
+
print(f"{obs_id1} - {obs_id2}: Cohen's Kappa: {kappa:.3f} Expected agreement: {pe:.3f} Observed agreement: {po:.3f}")
|
|
110
|
+
str_results += (
|
|
111
|
+
f"{obs_id1} - {obs_id2}: Cohen's Kappa: {kappa:.3f} Expected agreement: {pe:.3f} Observed agreement: {po:.3f}\n"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
ck_results[(obs_id1, obs_id2)] = f"{kappa:.3f}"
|
|
115
|
+
ck_results[(obs_id2, obs_id1)] = f"{kappa:.3f}"
|
|
116
|
+
|
|
117
|
+
# DataFrame conversion
|
|
118
|
+
df_results = pd.Series(ck_results).unstack()
|
|
119
|
+
|
|
120
|
+
return df_results, str_results
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS plugin
|
|
3
|
+
|
|
4
|
+
Inter Rater Reliability (IRR) Weighted Cohen Kappa with modifiers
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from typing import List, Tuple, Dict, Optional
|
|
9
|
+
|
|
10
|
+
__version__ = "0.0.1"
|
|
11
|
+
__version_date__ = "2025-08-25"
|
|
12
|
+
__plugin_name__ = "Inter Rater Reliability - Weighted Cohen Kappa with modifiers"
|
|
13
|
+
__author__ = "Olivier Friard - University of Torino - Italy"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(df: pd.DataFrame):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the Inter Rater Reliability - Weighted Cohen Kappa with modifiers
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def cohen_kappa_weighted_by_time(
|
|
22
|
+
obs1: List[Tuple[float, float, str]], obs2: List[Tuple[float, float, str]]
|
|
23
|
+
) -> Tuple[float, float, float, Dict[Tuple[Optional[str], Optional[str]], float]]:
|
|
24
|
+
"""
|
|
25
|
+
Compute Cohen's Kappa weighted by time duration with modifiers.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
obs1: List of (start_time, end_time, code) for observer 1
|
|
29
|
+
obs2: List of (start_time, end_time, code) for observer 2
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
kappa (float): Cohen's Kappa weighted by duration
|
|
33
|
+
po (float): Observed agreement proportion (weighted)
|
|
34
|
+
pe (float): Expected agreement proportion by chance (weighted)
|
|
35
|
+
contingency (dict): Contingency table {(code1, code2): total_duration}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# 1. Collect all time boundaries from both observers
|
|
39
|
+
time_points = sorted(set([t for seg in obs1 for t in seg[:2]] + [t for seg in obs2 for t in seg[:2]]))
|
|
40
|
+
|
|
41
|
+
# 2. Build elementary intervals (non-overlapping time bins)
|
|
42
|
+
elementary_intervals = [(time_points[i], time_points[i + 1]) for i in range(len(time_points) - 1)]
|
|
43
|
+
|
|
44
|
+
# 3. Helper: get the active code for an observer at a given time
|
|
45
|
+
def get_code(t: float, obs: List[Tuple[float, float, str]]) -> Optional[str]:
|
|
46
|
+
for seg in obs:
|
|
47
|
+
if seg[0] <= t < seg[1]:
|
|
48
|
+
return seg[2]
|
|
49
|
+
return None # in case no segment covers this time
|
|
50
|
+
|
|
51
|
+
# 4. Build weighted contingency table (durations instead of counts)
|
|
52
|
+
contingency: Dict[Tuple[Optional[str], Optional[str]], float] = {}
|
|
53
|
+
total_time = 0.0
|
|
54
|
+
|
|
55
|
+
for start, end in elementary_intervals:
|
|
56
|
+
c1 = get_code(start, obs1)
|
|
57
|
+
c2 = get_code(start, obs2)
|
|
58
|
+
duration = end - start
|
|
59
|
+
total_time += duration
|
|
60
|
+
contingency[(c1, c2)] = contingency.get((c1, c2), 0.0) + duration
|
|
61
|
+
|
|
62
|
+
# 5. Observed agreement (po)
|
|
63
|
+
po = sum(duration for (c1, c2), duration in contingency.items() if c1 == c2) / total_time
|
|
64
|
+
|
|
65
|
+
# Marginal distributions for each observer
|
|
66
|
+
codes1: Dict[Optional[str], float] = {}
|
|
67
|
+
codes2: Dict[Optional[str], float] = {}
|
|
68
|
+
for (c1, c2), duration in contingency.items():
|
|
69
|
+
codes1[c1] = codes1.get(c1, 0.0) + duration
|
|
70
|
+
codes2[c2] = codes2.get(c2, 0.0) + duration
|
|
71
|
+
|
|
72
|
+
# 6. Expected agreement (pe), using marginal proportions
|
|
73
|
+
all_codes = set(codes1) | set(codes2)
|
|
74
|
+
pe = sum((codes1.get(c, 0.0) / total_time) * (codes2.get(c, 0.0) / total_time) for c in all_codes)
|
|
75
|
+
|
|
76
|
+
# 7. Kappa calculation
|
|
77
|
+
kappa = (po - pe) / (1 - pe) if (1 - pe) != 0 else 0.0
|
|
78
|
+
|
|
79
|
+
return kappa, po, pe, contingency
|
|
80
|
+
|
|
81
|
+
# Get unique values as a numpy array
|
|
82
|
+
unique_obs = df["Observation id"].unique()
|
|
83
|
+
|
|
84
|
+
# Convert to a list
|
|
85
|
+
unique_obs_list = unique_obs.tolist()
|
|
86
|
+
|
|
87
|
+
# Convert to tuples grouped by observation
|
|
88
|
+
grouped: dict = {}
|
|
89
|
+
modifiers: list = []
|
|
90
|
+
for col in df.columns:
|
|
91
|
+
if isinstance(col, tuple):
|
|
92
|
+
modifiers.append(col)
|
|
93
|
+
|
|
94
|
+
for obs, group in df.groupby("Observation id"):
|
|
95
|
+
o = []
|
|
96
|
+
for row in group[["Start (s)", "Stop (s)", "Subject", "Behavior"] + modifiers].itertuples(index=False, name=None):
|
|
97
|
+
modif_list = [row[i] for idx, i in enumerate(range(4, 4 + len(modifiers))) if modifiers[idx][0] == row[3]]
|
|
98
|
+
o.append((row[0], row[1], row[2] + "|" + row[3] + "|" + ",".join(modif_list)))
|
|
99
|
+
grouped[obs] = o
|
|
100
|
+
|
|
101
|
+
ck_results: dict = {}
|
|
102
|
+
str_results: str = ""
|
|
103
|
+
for idx1, obs_id1 in enumerate(unique_obs_list):
|
|
104
|
+
obs1 = grouped[obs_id1]
|
|
105
|
+
|
|
106
|
+
ck_results[(obs_id1, obs_id1)] = "1.000"
|
|
107
|
+
|
|
108
|
+
for obs_id2 in unique_obs_list[idx1 + 1 :]:
|
|
109
|
+
obs2 = grouped[obs_id2]
|
|
110
|
+
|
|
111
|
+
# Cohen's Kappa
|
|
112
|
+
kappa, po, pe, table = cohen_kappa_weighted_by_time(obs1, obs2)
|
|
113
|
+
|
|
114
|
+
print(f"{obs_id1} - {obs_id2}: Cohen's Kappa: {kappa:.3f} Expected agreement: {pe:.3f} Observed agreement: {po:.3f}")
|
|
115
|
+
str_results += (
|
|
116
|
+
f"{obs_id1} - {obs_id2}: Cohen's Kappa: {kappa:.3f} Expected agreement: {pe:.3f} Observed agreement: {po:.3f}\n"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
ck_results[(obs_id1, obs_id2)] = f"{kappa:.3f}"
|
|
120
|
+
ck_results[(obs_id2, obs_id1)] = f"{kappa:.3f}"
|
|
121
|
+
|
|
122
|
+
# DataFrame conversion
|
|
123
|
+
df_results = pd.Series(ck_results).unstack()
|
|
124
|
+
|
|
125
|
+
return df_results, str_results
|
|
@@ -252,7 +252,7 @@ if args.command:
|
|
|
252
252
|
|
|
253
253
|
K, out = irr.cohen_kappa(cursor, observations_id_list[0], observations_id_list[1], interval, subjects, include_modifiers)
|
|
254
254
|
|
|
255
|
-
print(("Cohen's Kappa - Index of Inter-Rater Reliability\n\
|
|
255
|
+
print(("Cohen's Kappa - Index of Inter-Rater Reliability\n\nInterval time: {interval:.3f} s\n").format(interval=interval))
|
|
256
256
|
|
|
257
257
|
print(out)
|
|
258
258
|
sys.exit()
|
|
@@ -261,8 +261,6 @@ def show_coding_pad(self):
|
|
|
261
261
|
self.codingpad.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
262
262
|
self.codingpad.sendEventSignal.connect(self.signal_from_widget)
|
|
263
263
|
|
|
264
|
-
print(f"{self.signal_from_widget=}")
|
|
265
|
-
|
|
266
264
|
self.codingpad.clickSignal.connect(self.click_signal_from_coding_pad)
|
|
267
265
|
self.codingpad.close_signal.connect(self.close_signal_from_coding_pad)
|
|
268
266
|
self.codingpad.show()
|