boris-behav-obs 9.6.1__py2.py3-none-any.whl → 9.6.2__py2.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.
- boris/add_modifier.py +1 -5
- boris/analysis_plugins/irr_cohen_kappa.py +72 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +77 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +120 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +125 -0
- boris/boris_cli.py +1 -1
- boris/core.py +2 -4
- boris/core_qrc.py +3 -0
- boris/exclusion_matrix.py +1 -1
- boris/irr.py +10 -22
- boris/menu_options.py +2 -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/project_functions.py +2 -1
- boris/state_events.py +1 -1
- boris/transitions.py +1 -1
- boris/version.py +2 -2
- boris/write_event.py +4 -4
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.2.dist-info}/METADATA +2 -1
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.2.dist-info}/RECORD +28 -24
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.2.dist-info}/WHEEL +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.2.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.2.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.2.dist-info}/top_level.txt +0 -0
boris/add_modifier.py
CHANGED
|
@@ -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
|
boris/boris_cli.py
CHANGED
|
@@ -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()
|
boris/core.py
CHANGED
|
@@ -4263,7 +4263,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4263
4263
|
|
|
4264
4264
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].extend(events_to_add)
|
|
4265
4265
|
self.project_changed()
|
|
4266
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
4266
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
4267
4267
|
|
|
4268
4268
|
self.load_tw_events(self.observationId)
|
|
4269
4269
|
|
|
@@ -4361,14 +4361,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4361
4361
|
|
|
4362
4362
|
events_to_add = project_functions.fix_unpaired_state_events2(self.pj[cfg.ETHOGRAM], events, time_to_stop)
|
|
4363
4363
|
|
|
4364
|
-
# print(f"{events_to_add=}")
|
|
4365
|
-
|
|
4366
4364
|
if events_to_add:
|
|
4367
4365
|
self.statusbar.showMessage("The playlist has finished. Some ongoing state events were stopped automatically", 0)
|
|
4368
4366
|
|
|
4369
4367
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].extend(events_to_add)
|
|
4370
4368
|
self.project_changed()
|
|
4371
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
4369
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
4372
4370
|
|
|
4373
4371
|
self.load_tw_events(self.observationId)
|
|
4374
4372
|
|
boris/core_qrc.py
CHANGED
|
@@ -15946,10 +15946,13 @@ qt_resource_struct = b"\
|
|
|
15946
15946
|
\x00\x00\x01\x95k&\xa4B\
|
|
15947
15947
|
"
|
|
15948
15948
|
|
|
15949
|
+
|
|
15949
15950
|
def qInitResources():
|
|
15950
15951
|
QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
|
|
15951
15952
|
|
|
15953
|
+
|
|
15952
15954
|
def qCleanupResources():
|
|
15953
15955
|
QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
|
|
15954
15956
|
|
|
15957
|
+
|
|
15955
15958
|
qInitResources()
|
boris/exclusion_matrix.py
CHANGED
|
@@ -42,7 +42,7 @@ class ExclusionMatrix(QDialog):
|
|
|
42
42
|
|
|
43
43
|
self.label = QLabel()
|
|
44
44
|
self.label.setText(
|
|
45
|
-
("Check if behaviors are mutually exclusive.\
|
|
45
|
+
("Check if behaviors are mutually exclusive.\nThe Point events (displayed on blue background) cannot be excluded)")
|
|
46
46
|
)
|
|
47
47
|
hbox.addWidget(self.label)
|
|
48
48
|
|
boris/irr.py
CHANGED
|
@@ -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,21 +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
|
-
("SELECT COUNT(*) FROM aggregated_events
|
|
144
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
148
145
|
(obsid1,) + tuple(selected_subjects),
|
|
149
146
|
).fetchone()[0]
|
|
150
147
|
nb_events2 = cursor.execute(
|
|
151
|
-
("SELECT COUNT(*) FROM aggregated_events
|
|
148
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
152
149
|
(obsid2,) + tuple(selected_subjects),
|
|
153
150
|
).fetchone()[0]
|
|
154
151
|
|
|
@@ -201,11 +198,7 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
|
|
|
201
198
|
logging.debug(f"contingency_table:\n {contingency_table}")
|
|
202
199
|
|
|
203
200
|
template = (
|
|
204
|
-
"Observation: {obsid1}\n"
|
|
205
|
-
"number of events: {nb_events1}\n\n"
|
|
206
|
-
"Observation: {obsid2}\n"
|
|
207
|
-
"number of events: {nb_events2:.0f}\n\n"
|
|
208
|
-
"K = {K:.3f}"
|
|
201
|
+
"Observation: {obsid1}\nnumber of events: {nb_events1}\n\nObservation: {obsid2}\nnumber of events: {nb_events2:.0f}\n\nK = {K:.3f}"
|
|
209
202
|
)
|
|
210
203
|
|
|
211
204
|
# out += "Observation length: <b>{:.3f} s</b><br>".format(self.observationTotalMediaLength(obsid1))
|
|
@@ -470,7 +463,7 @@ def needleman_wunsch_identity(cursor, obsid1: str, obsid2: str, interval, select
|
|
|
470
463
|
first_event = cursor.execute(
|
|
471
464
|
(
|
|
472
465
|
"SELECT min(start) FROM aggregated_events "
|
|
473
|
-
f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
466
|
+
f"WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "
|
|
474
467
|
),
|
|
475
468
|
(obsid1, obsid2) + tuple(selected_subjects),
|
|
476
469
|
).fetchone()[0]
|
|
@@ -483,22 +476,19 @@ def needleman_wunsch_identity(cursor, obsid1: str, obsid2: str, interval, select
|
|
|
483
476
|
logging.debug(f"first_event: {first_event}")
|
|
484
477
|
|
|
485
478
|
last_event = cursor.execute(
|
|
486
|
-
(
|
|
487
|
-
"SELECT max(stop) FROM aggregated_events "
|
|
488
|
-
f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
|
|
489
|
-
),
|
|
479
|
+
(f"SELECT max(stop) FROM aggregated_events WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
490
480
|
(obsid1, obsid2) + tuple(selected_subjects),
|
|
491
481
|
).fetchone()[0]
|
|
492
482
|
|
|
493
483
|
logging.debug(f"last_event: {last_event}")
|
|
494
484
|
|
|
495
485
|
nb_events1 = cursor.execute(
|
|
496
|
-
("SELECT COUNT(*) FROM aggregated_events
|
|
486
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
497
487
|
(obsid1,) + tuple(selected_subjects),
|
|
498
488
|
).fetchone()[0]
|
|
499
489
|
|
|
500
490
|
nb_events2 = cursor.execute(
|
|
501
|
-
("SELECT COUNT(*) FROM aggregated_events
|
|
491
|
+
(f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
|
|
502
492
|
(obsid2,) + tuple(selected_subjects),
|
|
503
493
|
).fetchone()[0]
|
|
504
494
|
|
|
@@ -606,9 +596,7 @@ def needleman_wunch(self):
|
|
|
606
596
|
|
|
607
597
|
cursor = db_connector.cursor()
|
|
608
598
|
out = (
|
|
609
|
-
"Needleman-Wunsch similarity\n\n"
|
|
610
|
-
f"Time unit: {interval:.3f} s\n"
|
|
611
|
-
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"
|
|
612
600
|
)
|
|
613
601
|
mem_done = []
|
|
614
602
|
nws_results = np.ones((len(selected_observations), len(selected_observations)))
|
boris/menu_options.py
CHANGED
|
@@ -143,6 +143,8 @@ def update_menu(self):
|
|
|
143
143
|
self.actionJumpForward,
|
|
144
144
|
self.actionJumpBackward,
|
|
145
145
|
self.actionJumpTo,
|
|
146
|
+
self.action_change_time_offset_of_players,
|
|
147
|
+
self.action_deinterlace,
|
|
146
148
|
self.actionZoom_level,
|
|
147
149
|
self.actionRotate_current_video,
|
|
148
150
|
self.actionDisplay_subtitles,
|
boris/portion/__init__.py
CHANGED
|
@@ -3,18 +3,28 @@ from .interval import Interval, open, closed, openclosed, closedopen, empty, sin
|
|
|
3
3
|
from .func import iterate
|
|
4
4
|
from .io import from_string, to_string, from_data, to_data
|
|
5
5
|
|
|
6
|
-
# disabled because BORIS does not need IntervalDict
|
|
6
|
+
# disabled because BORIS does not need IntervalDict
|
|
7
7
|
# so the sortedcontainers module is not required
|
|
8
|
-
#from .dict import IntervalDict
|
|
8
|
+
# from .dict import IntervalDict
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
__all__ = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
"inf",
|
|
13
|
+
"CLOSED",
|
|
14
|
+
"OPEN",
|
|
15
|
+
"Interval",
|
|
16
|
+
"open",
|
|
17
|
+
"closed",
|
|
18
|
+
"openclosed",
|
|
19
|
+
"closedopen",
|
|
20
|
+
"singleton",
|
|
21
|
+
"empty",
|
|
22
|
+
"iterate",
|
|
23
|
+
"from_string",
|
|
24
|
+
"to_string",
|
|
25
|
+
"from_data",
|
|
26
|
+
"to_data",
|
|
27
|
+
"IntervalDict",
|
|
18
28
|
]
|
|
19
29
|
|
|
20
30
|
CLOSED = Bound.CLOSED
|
boris/portion/const.py
CHANGED
|
@@ -5,11 +5,12 @@ class Bound(enum.Enum):
|
|
|
5
5
|
"""
|
|
6
6
|
Bound types, either CLOSED for inclusive, or OPEN for exclusive.
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
CLOSED = True
|
|
9
10
|
OPEN = False
|
|
10
11
|
|
|
11
12
|
def __bool__(self):
|
|
12
|
-
raise ValueError(
|
|
13
|
+
raise ValueError("The truth value of a bound is ambiguous.")
|
|
13
14
|
|
|
14
15
|
def __invert__(self):
|
|
15
16
|
return Bound.CLOSED if self is Bound.OPEN else Bound.OPEN
|
|
@@ -21,7 +22,7 @@ class Bound(enum.Enum):
|
|
|
21
22
|
return self.name
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
class _Singleton
|
|
25
|
+
class _Singleton:
|
|
25
26
|
__instance = None
|
|
26
27
|
|
|
27
28
|
def __new__(cls, *args, **kwargs):
|
|
@@ -35,21 +36,29 @@ class _PInf(_Singleton):
|
|
|
35
36
|
Represent positive infinity.
|
|
36
37
|
"""
|
|
37
38
|
|
|
38
|
-
def __neg__(self):
|
|
39
|
+
def __neg__(self):
|
|
40
|
+
return _NInf()
|
|
39
41
|
|
|
40
|
-
def __lt__(self, o):
|
|
42
|
+
def __lt__(self, o):
|
|
43
|
+
return False
|
|
41
44
|
|
|
42
|
-
def __le__(self, o):
|
|
45
|
+
def __le__(self, o):
|
|
46
|
+
return isinstance(o, _PInf)
|
|
43
47
|
|
|
44
|
-
def __gt__(self, o):
|
|
48
|
+
def __gt__(self, o):
|
|
49
|
+
return not isinstance(o, _PInf)
|
|
45
50
|
|
|
46
|
-
def __ge__(self, o):
|
|
51
|
+
def __ge__(self, o):
|
|
52
|
+
return True
|
|
47
53
|
|
|
48
|
-
def __eq__(self, o):
|
|
54
|
+
def __eq__(self, o):
|
|
55
|
+
return isinstance(o, _PInf)
|
|
49
56
|
|
|
50
|
-
def __repr__(self):
|
|
57
|
+
def __repr__(self):
|
|
58
|
+
return "+inf"
|
|
51
59
|
|
|
52
|
-
def __hash__(self):
|
|
60
|
+
def __hash__(self):
|
|
61
|
+
return hash(float("+inf"))
|
|
53
62
|
|
|
54
63
|
|
|
55
64
|
class _NInf(_Singleton):
|
|
@@ -57,21 +66,29 @@ class _NInf(_Singleton):
|
|
|
57
66
|
Represent negative infinity.
|
|
58
67
|
"""
|
|
59
68
|
|
|
60
|
-
def __neg__(self):
|
|
69
|
+
def __neg__(self):
|
|
70
|
+
return _PInf()
|
|
61
71
|
|
|
62
|
-
def __lt__(self, o):
|
|
72
|
+
def __lt__(self, o):
|
|
73
|
+
return not isinstance(o, _NInf)
|
|
63
74
|
|
|
64
|
-
def __le__(self, o):
|
|
75
|
+
def __le__(self, o):
|
|
76
|
+
return True
|
|
65
77
|
|
|
66
|
-
def __gt__(self, o):
|
|
78
|
+
def __gt__(self, o):
|
|
79
|
+
return False
|
|
67
80
|
|
|
68
|
-
def __ge__(self, o):
|
|
81
|
+
def __ge__(self, o):
|
|
82
|
+
return isinstance(o, _NInf)
|
|
69
83
|
|
|
70
|
-
def __eq__(self, o):
|
|
84
|
+
def __eq__(self, o):
|
|
85
|
+
return isinstance(o, _NInf)
|
|
71
86
|
|
|
72
|
-
def __repr__(self):
|
|
87
|
+
def __repr__(self):
|
|
88
|
+
return "-inf"
|
|
73
89
|
|
|
74
|
-
def __hash__(self):
|
|
90
|
+
def __hash__(self):
|
|
91
|
+
return hash(float("-inf"))
|
|
75
92
|
|
|
76
93
|
|
|
77
94
|
# Positive infinity
|
boris/portion/dict.py
CHANGED
|
@@ -28,7 +28,7 @@ class IntervalDict(MutableMapping):
|
|
|
28
28
|
number of distinct values (not keys) that are stored.
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
|
-
__slots__ = (
|
|
31
|
+
__slots__ = ("_storage",)
|
|
32
32
|
|
|
33
33
|
def __init__(self, mapping_or_iterable=None):
|
|
34
34
|
"""
|
|
@@ -352,10 +352,10 @@ class IntervalDict(MutableMapping):
|
|
|
352
352
|
return key in self.domain()
|
|
353
353
|
|
|
354
354
|
def __repr__(self):
|
|
355
|
-
return
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
355
|
+
return "{}{}{}".format(
|
|
356
|
+
"{",
|
|
357
|
+
", ".join("{!r}: {!r}".format(i, v) for i, v in self.items()),
|
|
358
|
+
"}",
|
|
359
359
|
)
|
|
360
360
|
|
|
361
361
|
def __eq__(self, other):
|
boris/portion/func.py
CHANGED
|
@@ -31,7 +31,7 @@ def iterate(interval, step, *, base=None, reverse=False):
|
|
|
31
31
|
:return: a lazy iterator.
|
|
32
32
|
"""
|
|
33
33
|
if base is None:
|
|
34
|
-
base =
|
|
34
|
+
base = lambda x: x
|
|
35
35
|
|
|
36
36
|
exclude = operator.lt if not reverse else operator.gt
|
|
37
37
|
include = operator.le if not reverse else operator.ge
|
|
@@ -39,7 +39,7 @@ def iterate(interval, step, *, base=None, reverse=False):
|
|
|
39
39
|
|
|
40
40
|
value = base(interval.lower if not reverse else interval.upper)
|
|
41
41
|
if (value == -inf and not reverse) or (value == inf and reverse):
|
|
42
|
-
raise ValueError(
|
|
42
|
+
raise ValueError("Cannot start iteration with infinity.")
|
|
43
43
|
|
|
44
44
|
for i in interval if not reverse else reversed(interval):
|
|
45
45
|
value = base(i.lower if not reverse else i.upper)
|
boris/portion/interval.py
CHANGED
|
@@ -2,7 +2,7 @@ from collections import namedtuple
|
|
|
2
2
|
from .const import Bound, inf
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
Atomic = namedtuple(
|
|
5
|
+
Atomic = namedtuple("Atomic", ["left", "lower", "upper", "right"])
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def mergeable(a, b):
|
|
@@ -96,7 +96,7 @@ class Interval:
|
|
|
96
96
|
one of the helpers provided in this module (open, closed, openclosed, etc.)
|
|
97
97
|
"""
|
|
98
98
|
|
|
99
|
-
__slots__ = (
|
|
99
|
+
__slots__ = ("_intervals",)
|
|
100
100
|
|
|
101
101
|
def __init__(self, *intervals):
|
|
102
102
|
"""
|
|
@@ -111,7 +111,7 @@ class Interval:
|
|
|
111
111
|
if not interval.empty:
|
|
112
112
|
self._intervals.extend(interval._intervals)
|
|
113
113
|
else:
|
|
114
|
-
raise TypeError(
|
|
114
|
+
raise TypeError("Parameters must be Interval instances")
|
|
115
115
|
|
|
116
116
|
if len(self._intervals) == 0:
|
|
117
117
|
# So we have at least one (empty) interval
|
|
@@ -181,10 +181,7 @@ class Interval:
|
|
|
181
181
|
"""
|
|
182
182
|
True if interval is empty, False otherwise.
|
|
183
183
|
"""
|
|
184
|
-
return (
|
|
185
|
-
self.lower > self.upper or
|
|
186
|
-
(self.lower == self.upper and (self.left == Bound.OPEN or self.right == Bound.OPEN))
|
|
187
|
-
)
|
|
184
|
+
return self.lower > self.upper or (self.lower == self.upper and (self.left == Bound.OPEN or self.right == Bound.OPEN))
|
|
188
185
|
|
|
189
186
|
@property
|
|
190
187
|
def atomic(self):
|
|
@@ -307,7 +304,7 @@ class Interval:
|
|
|
307
304
|
elif isinstance(value, tuple):
|
|
308
305
|
intervals.append(Interval.from_atomic(*value))
|
|
309
306
|
else:
|
|
310
|
-
raise TypeError(
|
|
307
|
+
raise TypeError("Unsupported return type {} for {}".format(type(value), value))
|
|
311
308
|
|
|
312
309
|
return Interval(*intervals)
|
|
313
310
|
|
|
@@ -350,7 +347,7 @@ class Interval:
|
|
|
350
347
|
return True
|
|
351
348
|
return False
|
|
352
349
|
else:
|
|
353
|
-
raise TypeError(
|
|
350
|
+
raise TypeError("Unsupported type {} for {}".format(type(other), other))
|
|
354
351
|
|
|
355
352
|
def intersection(self, other):
|
|
356
353
|
"""
|
|
@@ -468,14 +465,8 @@ class Interval:
|
|
|
468
465
|
if item.empty:
|
|
469
466
|
return True
|
|
470
467
|
elif self.atomic:
|
|
471
|
-
left = item.lower > self.lower or (
|
|
472
|
-
|
|
473
|
-
(item.left == self.left or self.left == Bound.CLOSED)
|
|
474
|
-
)
|
|
475
|
-
right = item.upper < self.upper or (
|
|
476
|
-
item.upper == self.upper and
|
|
477
|
-
(item.right == self.right or self.right == Bound.CLOSED)
|
|
478
|
-
)
|
|
468
|
+
left = item.lower > self.lower or (item.lower == self.lower and (item.left == self.left or self.left == Bound.CLOSED))
|
|
469
|
+
right = item.upper < self.upper or (item.upper == self.upper and (item.right == self.right or self.right == Bound.CLOSED))
|
|
479
470
|
return left and right
|
|
480
471
|
else:
|
|
481
472
|
selfiter = iter(self)
|
|
@@ -504,13 +495,11 @@ class Interval:
|
|
|
504
495
|
def __invert__(self):
|
|
505
496
|
complements = [
|
|
506
497
|
Interval.from_atomic(Bound.OPEN, -inf, self.lower, ~self.left),
|
|
507
|
-
Interval.from_atomic(~self.right, self.upper, inf, Bound.OPEN)
|
|
498
|
+
Interval.from_atomic(~self.right, self.upper, inf, Bound.OPEN),
|
|
508
499
|
]
|
|
509
500
|
|
|
510
501
|
for i, j in zip(self._intervals[:-1], self._intervals[1:]):
|
|
511
|
-
complements.append(
|
|
512
|
-
Interval.from_atomic(~i.right, i.upper, j.lower, ~j.left)
|
|
513
|
-
)
|
|
502
|
+
complements.append(Interval.from_atomic(~i.right, i.upper, j.lower, ~j.left))
|
|
514
503
|
|
|
515
504
|
return Interval(*complements)
|
|
516
505
|
|
|
@@ -526,12 +515,7 @@ class Interval:
|
|
|
526
515
|
return False
|
|
527
516
|
|
|
528
517
|
for a, b in zip(self._intervals, other._intervals):
|
|
529
|
-
eq =
|
|
530
|
-
a.left == b.left and
|
|
531
|
-
a.lower == b.lower and
|
|
532
|
-
a.upper == b.upper and
|
|
533
|
-
a.right == b.right
|
|
534
|
-
)
|
|
518
|
+
eq = a.left == b.left and a.lower == b.lower and a.upper == b.upper and a.right == b.right
|
|
535
519
|
if not eq:
|
|
536
520
|
return False
|
|
537
521
|
return True
|
|
@@ -543,8 +527,7 @@ class Interval:
|
|
|
543
527
|
if self.right == Bound.OPEN:
|
|
544
528
|
return self.upper <= other.lower
|
|
545
529
|
else:
|
|
546
|
-
return self.upper < other.lower or
|
|
547
|
-
(self.upper == other.lower and other.left == Bound.OPEN)
|
|
530
|
+
return self.upper < other.lower or (self.upper == other.lower and other.left == Bound.OPEN)
|
|
548
531
|
else:
|
|
549
532
|
return self.upper < other or (self.right == Bound.OPEN and self.upper == other)
|
|
550
533
|
|
|
@@ -553,8 +536,7 @@ class Interval:
|
|
|
553
536
|
if self.left == Bound.OPEN:
|
|
554
537
|
return self.lower >= other.upper
|
|
555
538
|
else:
|
|
556
|
-
return self.lower > other.upper or
|
|
557
|
-
(self.lower == other.upper and other.right == Bound.OPEN)
|
|
539
|
+
return self.lower > other.upper or (self.lower == other.upper and other.right == Bound.OPEN)
|
|
558
540
|
else:
|
|
559
541
|
return self.lower > other or (self.left == Bound.OPEN and self.lower == other)
|
|
560
542
|
|
|
@@ -563,8 +545,7 @@ class Interval:
|
|
|
563
545
|
if self.right == Bound.OPEN:
|
|
564
546
|
return self.upper <= other.upper
|
|
565
547
|
else:
|
|
566
|
-
return self.upper < other.upper or
|
|
567
|
-
(self.upper == other.upper and other.right == Bound.CLOSED)
|
|
548
|
+
return self.upper < other.upper or (self.upper == other.upper and other.right == Bound.CLOSED)
|
|
568
549
|
else:
|
|
569
550
|
return self.lower < other or (self.left == Bound.CLOSED and self.lower == other)
|
|
570
551
|
|
|
@@ -573,8 +554,7 @@ class Interval:
|
|
|
573
554
|
if self.left == Bound.OPEN:
|
|
574
555
|
return self.lower >= other.lower
|
|
575
556
|
else:
|
|
576
|
-
return self.lower > other.lower or
|
|
577
|
-
(self.lower == other.lower and other.left == Bound.CLOSED)
|
|
557
|
+
return self.lower > other.lower or (self.lower == other.lower and other.left == Bound.CLOSED)
|
|
578
558
|
else:
|
|
579
559
|
return self.upper > other or (self.right == Bound.CLOSED and self.upper == other)
|
|
580
560
|
|
|
@@ -586,16 +566,16 @@ class Interval:
|
|
|
586
566
|
|
|
587
567
|
for interval in self:
|
|
588
568
|
if interval.empty:
|
|
589
|
-
intervals.append(
|
|
569
|
+
intervals.append("()")
|
|
590
570
|
elif interval.lower == interval.upper:
|
|
591
|
-
intervals.append(
|
|
571
|
+
intervals.append("[{}]".format(repr(interval.lower)))
|
|
592
572
|
else:
|
|
593
573
|
intervals.append(
|
|
594
|
-
|
|
595
|
-
|
|
574
|
+
"{}{},{}{}".format(
|
|
575
|
+
"[" if interval.left == Bound.CLOSED else "(",
|
|
596
576
|
repr(interval.lower),
|
|
597
577
|
repr(interval.upper),
|
|
598
|
-
|
|
578
|
+
"]" if interval.right == Bound.CLOSED else ")",
|
|
599
579
|
)
|
|
600
580
|
)
|
|
601
|
-
return
|
|
581
|
+
return " | ".join(intervals)
|
boris/portion/io.py
CHANGED
|
@@ -4,9 +4,20 @@ from .const import Bound, inf
|
|
|
4
4
|
from .interval import Interval
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def from_string(
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
def from_string(
|
|
8
|
+
string,
|
|
9
|
+
conv,
|
|
10
|
+
*,
|
|
11
|
+
bound=r".+?",
|
|
12
|
+
disj=r" ?\| ?",
|
|
13
|
+
sep=r", ?",
|
|
14
|
+
left_open=r"\(",
|
|
15
|
+
left_closed=r"\[",
|
|
16
|
+
right_open=r"\)",
|
|
17
|
+
right_closed=r"\]",
|
|
18
|
+
pinf=r"\+inf",
|
|
19
|
+
ninf=r"-inf",
|
|
20
|
+
):
|
|
10
21
|
"""
|
|
11
22
|
Parse given string and create an Interval instance.
|
|
12
23
|
A converter function has to be provided to convert a bound (as string) to a value.
|
|
@@ -25,11 +36,11 @@ def from_string(string, conv, *, bound=r'.+?', disj=r' ?\| ?', sep=r', ?',
|
|
|
25
36
|
:return: an Interval instance.
|
|
26
37
|
"""
|
|
27
38
|
|
|
28
|
-
re_left_boundary = r
|
|
29
|
-
re_right_boundary = r
|
|
30
|
-
re_bounds = r
|
|
31
|
-
re_interval = r
|
|
32
|
-
re_intervals = r
|
|
39
|
+
re_left_boundary = r"(?P<left>{}|{})".format(left_open, left_closed)
|
|
40
|
+
re_right_boundary = r"(?P<right>{}|{})".format(right_open, right_closed)
|
|
41
|
+
re_bounds = r"(?P<lower>{bound})({sep}(?P<upper>{bound}))?".format(bound=bound, sep=sep)
|
|
42
|
+
re_interval = r"{}(|{}){}".format(re_left_boundary, re_bounds, re_right_boundary)
|
|
43
|
+
re_intervals = r"{}(?P<disj>{})?".format(re_interval, disj)
|
|
33
44
|
|
|
34
45
|
intervals = []
|
|
35
46
|
has_more = True
|
|
@@ -49,22 +60,23 @@ def from_string(string, conv, *, bound=r'.+?', disj=r' ?\| ?', sep=r', ?',
|
|
|
49
60
|
else:
|
|
50
61
|
group = match.groupdict()
|
|
51
62
|
|
|
52
|
-
left = Bound.CLOSED if re.match(left_closed +
|
|
53
|
-
right = Bound.CLOSED if re.match(right_closed +
|
|
63
|
+
left = Bound.CLOSED if re.match(left_closed + "$", group["left"]) else Bound.OPEN
|
|
64
|
+
right = Bound.CLOSED if re.match(right_closed + "$", group["right"]) else Bound.OPEN
|
|
54
65
|
|
|
55
|
-
lower = group.get(
|
|
56
|
-
upper = group.get(
|
|
66
|
+
lower = group.get("lower", None)
|
|
67
|
+
upper = group.get("upper", None)
|
|
57
68
|
lower = _convert(lower) if lower is not None else inf
|
|
58
69
|
upper = _convert(upper) if upper is not None else lower
|
|
59
70
|
|
|
60
71
|
intervals.append(Interval.from_atomic(left, lower, upper, right))
|
|
61
|
-
string = string[match.end():]
|
|
72
|
+
string = string[match.end() :]
|
|
62
73
|
|
|
63
74
|
return Interval(*intervals)
|
|
64
75
|
|
|
65
76
|
|
|
66
|
-
def to_string(
|
|
67
|
-
|
|
77
|
+
def to_string(
|
|
78
|
+
interval, conv=repr, *, disj=" | ", sep=",", left_open="(", left_closed="[", right_open=")", right_closed="]", pinf="+inf", ninf="-inf"
|
|
79
|
+
):
|
|
68
80
|
"""
|
|
69
81
|
Export given interval to string.
|
|
70
82
|
|
|
@@ -81,7 +93,7 @@ def to_string(interval, conv=repr, *, disj=' | ', sep=',', left_open='(',
|
|
|
81
93
|
:return: a string representation for given interval.
|
|
82
94
|
"""
|
|
83
95
|
if interval.empty:
|
|
84
|
-
return
|
|
96
|
+
return "{}{}".format(left_open, right_open)
|
|
85
97
|
|
|
86
98
|
def _convert(bound):
|
|
87
99
|
if bound == inf:
|
|
@@ -100,14 +112,14 @@ def to_string(interval, conv=repr, *, disj=' | ', sep=',', left_open='(',
|
|
|
100
112
|
upper = _convert(item.upper)
|
|
101
113
|
|
|
102
114
|
if item.lower == item.upper:
|
|
103
|
-
exported_intervals.append(
|
|
115
|
+
exported_intervals.append("{}{}{}".format(left, lower, right))
|
|
104
116
|
else:
|
|
105
|
-
exported_intervals.append(
|
|
117
|
+
exported_intervals.append("{}{}{}{}{}".format(left, lower, sep, upper, right))
|
|
106
118
|
|
|
107
119
|
return disj.join(exported_intervals)
|
|
108
120
|
|
|
109
121
|
|
|
110
|
-
def from_data(data, conv=None, *, pinf=float(
|
|
122
|
+
def from_data(data, conv=None, *, pinf=float("inf"), ninf=float("-inf")):
|
|
111
123
|
"""
|
|
112
124
|
Import an interval from a piece of data.
|
|
113
125
|
|
|
@@ -130,16 +142,18 @@ def from_data(data, conv=None, *, pinf=float('inf'), ninf=float('-inf')):
|
|
|
130
142
|
|
|
131
143
|
for item in data:
|
|
132
144
|
left, lower, upper, right = item
|
|
133
|
-
intervals.append(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
intervals.append(
|
|
146
|
+
Interval.from_atomic(
|
|
147
|
+
Bound(left),
|
|
148
|
+
_convert(lower),
|
|
149
|
+
_convert(upper),
|
|
150
|
+
Bound(right),
|
|
151
|
+
)
|
|
152
|
+
)
|
|
139
153
|
return Interval(*intervals)
|
|
140
154
|
|
|
141
155
|
|
|
142
|
-
def to_data(interval, conv=None, *, pinf=float(
|
|
156
|
+
def to_data(interval, conv=None, *, pinf=float("inf"), ninf=float("-inf")):
|
|
143
157
|
"""
|
|
144
158
|
Export given interval to a list of 4-uples (left, lower,
|
|
145
159
|
upper, right).
|
|
@@ -163,10 +177,5 @@ def to_data(interval, conv=None, *, pinf=float('inf'), ninf=float('-inf')):
|
|
|
163
177
|
return conv(bound)
|
|
164
178
|
|
|
165
179
|
for item in interval:
|
|
166
|
-
data.append((
|
|
167
|
-
item.left.value,
|
|
168
|
-
_convert(item.lower),
|
|
169
|
-
_convert(item.upper),
|
|
170
|
-
item.right.value
|
|
171
|
-
))
|
|
180
|
+
data.append((item.left.value, _convert(item.lower), _convert(item.upper), item.right.value))
|
|
172
181
|
return data
|
boris/project_functions.py
CHANGED
|
@@ -1539,7 +1539,8 @@ def open_project_json(project_file_name: str) -> tuple:
|
|
|
1539
1539
|
# sort events by time asc
|
|
1540
1540
|
for obs_id in pj[cfg.OBSERVATIONS]:
|
|
1541
1541
|
if pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.LIVE, cfg.MEDIA):
|
|
1542
|
-
|
|
1542
|
+
# sort events list using the first 3 items (time, subject, behavior)
|
|
1543
|
+
pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
1543
1544
|
|
|
1544
1545
|
return project_file_name, projectChanged, pj, msg
|
|
1545
1546
|
|
boris/state_events.py
CHANGED
|
@@ -136,7 +136,7 @@ def fix_unpaired_events(self, silent_mode: bool = False):
|
|
|
136
136
|
|
|
137
137
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].extend(events_to_add)
|
|
138
138
|
self.project_changed()
|
|
139
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
139
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
140
140
|
self.load_tw_events(self.observationId)
|
|
141
141
|
|
|
142
142
|
index = self.tv_events.model().index(
|
boris/transitions.py
CHANGED
|
@@ -354,7 +354,7 @@ def transitions_flow_diagram():
|
|
|
354
354
|
with open(tempfile.gettempdir() + os.sep + os.path.basename(file_name) + ".tmp.gv", "w") as f:
|
|
355
355
|
f.write(gv)
|
|
356
356
|
result = subprocess.getoutput(
|
|
357
|
-
(f'dot -Tpng -o "{file_name}.png"
|
|
357
|
+
(f'dot -Tpng -o "{file_name}.png" "{tempfile.gettempdir() + os.sep + os.path.basename(file_name)}.tmp.gv"')
|
|
358
358
|
)
|
|
359
359
|
if not result:
|
|
360
360
|
out += f"<b>{file_name}.png</b> created<br>"
|
boris/version.py
CHANGED
boris/write_event.py
CHANGED
|
@@ -472,8 +472,8 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
472
472
|
comment,
|
|
473
473
|
frame_idx,
|
|
474
474
|
]
|
|
475
|
-
# order
|
|
476
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
475
|
+
# order events list using time, subject, behavior
|
|
476
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
477
477
|
|
|
478
478
|
elif self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.LIVE:
|
|
479
479
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][event["row"]] = [
|
|
@@ -483,8 +483,8 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
483
483
|
modifier_str,
|
|
484
484
|
comment,
|
|
485
485
|
]
|
|
486
|
-
# order
|
|
487
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
486
|
+
# order events list using time, subject, behavior
|
|
487
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
488
488
|
|
|
489
489
|
elif self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
|
|
490
490
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][event["row"]] = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: boris-behav-obs
|
|
3
|
-
Version: 9.6.
|
|
3
|
+
Version: 9.6.2
|
|
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
|
|
@@ -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"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
boris/__init__.py,sha256=iAtmVMy22TJpMmxVTMSK_6-wXnCbx1ogvWgfYEcbHzU,773
|
|
2
2
|
boris/__main__.py,sha256=ANjTbXgXDoz2nB1tCtOIllfIVotCa602iebACX7rXaE,764
|
|
3
3
|
boris/about.py,sha256=VPa8zeu0bMb1LRXDq8uUSG_7mSbkb2HTk1AtWbzWQwE,5366
|
|
4
|
-
boris/add_modifier.py,sha256=
|
|
4
|
+
boris/add_modifier.py,sha256=l9LSa_9FAV9CnBgm26tJqhMAdnFoBQafZLyt9pTKmac,26240
|
|
5
5
|
boris/add_modifier_ui.py,sha256=Y7TLO5uS6zW7zpjXmjA4V_VIp_bFDNtjOTbJ9Q6m-mQ,11601
|
|
6
6
|
boris/advanced_event_filtering.py,sha256=VlvU12mL6xYacZOvJAi5uLpHMcmAw5Pvuvmka-PN29c,15469
|
|
7
7
|
boris/behav_coding_map_creator.py,sha256=_WmfWTYkKh_a7pZa49h2GtORCi6h8joZTWihud6YDBE,38826
|
|
8
8
|
boris/behavior_binary_table.py,sha256=bpmRDpEjq0rw3YOCoN_He3kfUe8A_R6E48kQR7KnkH8,12453
|
|
9
9
|
boris/behaviors_coding_map.py,sha256=xIGJxp2eghrpiGDmYH73eJPERuyc4A_54uT-Got3zTs,7302
|
|
10
|
-
boris/boris_cli.py,sha256=
|
|
10
|
+
boris/boris_cli.py,sha256=Bc51DEMcD79ZZfM9pCzpaWU6iT6b8gNwR3n8fr42_4E,13193
|
|
11
11
|
boris/cmd_arguments.py,sha256=oWb-FvhKLbKJhATlTHy9muWu8XnnUfOZ-3Fmz2M8Yzc,1848
|
|
12
12
|
boris/coding_pad.py,sha256=fBKdp7DDyupySJosIYtqNd8s2E-GruzCgVhDFsoVWKE,10986
|
|
13
13
|
boris/config.py,sha256=IbW8PkAFcZIL-8NoSscXSeI82dneLzpywaGXWDcnrWw,17845
|
|
@@ -16,8 +16,8 @@ boris/connections.py,sha256=kqc6jaYNzoJe8crPtfwE-fXTW4nTfB8-PojRzbbLEus,19629
|
|
|
16
16
|
boris/converters.py,sha256=n6gDM9x2hS-ZOoHLruiifuXxnC7ERsUukiFokhHZPoQ,11678
|
|
17
17
|
boris/converters_ui.py,sha256=uu7LOBV_fKv2DBdOqsqPwjGsjgONr5ODBoscAA-EP48,9900
|
|
18
18
|
boris/cooccurence.py,sha256=tVERC-V8MWjWHlGEfDuu08iS94qjt4do-38jwI62QaY,10367
|
|
19
|
-
boris/core.py,sha256=
|
|
20
|
-
boris/core_qrc.py,sha256=
|
|
19
|
+
boris/core.py,sha256=xDurFeAulOx1TjcaZbuoymvnP07fjIAUfhElLw4och8,231004
|
|
20
|
+
boris/core_qrc.py,sha256=Hz51Xw70ZIlDbYB281nfGtCm43_ItYhamMu2T5X8Tu8,639882
|
|
21
21
|
boris/core_ui.py,sha256=--VDOzUfjsA4TJRw3aFk2CeSL29193vPGLgYJRhQfUY,77143
|
|
22
22
|
boris/db_functions.py,sha256=Uw9wWH_Pe-qNzpV1k21YG_jKsoOmfY_iiK_7ARZHGDc,13352
|
|
23
23
|
boris/dev.py,sha256=9pUElbjl9g17rFUJXX5aVSu55_iIKIuDxNdrB0DI_d0,3671
|
|
@@ -28,7 +28,7 @@ boris/edit_event_ui.py,sha256=qFgt00cejGB6UGC1mFkyZcsIAdvMeYMK0WYjZtJl1T0,9207
|
|
|
28
28
|
boris/event_operations.py,sha256=bqUZjgJaJ1Z8oTiidG9wfCp2LLUH1Zf4kBDeg_yjC-o,38514
|
|
29
29
|
boris/events_cursor.py,sha256=VPY_ygD0fxE5lp25mcd2l00XQXurCR6hprffF4tKRbU,2078
|
|
30
30
|
boris/events_snapshots.py,sha256=PjWzQvUGQtIcEc_7FDsRphf7fAhhTccQgYc2eQSA65g,27621
|
|
31
|
-
boris/exclusion_matrix.py,sha256=
|
|
31
|
+
boris/exclusion_matrix.py,sha256=K_o8pEMYRQ3curgRQYkn5hPRksLDitICuwjB7mpVRPA,5269
|
|
32
32
|
boris/export_events.py,sha256=3B336WEA0g_8oW3VDo_kfq5D0ISu-e7z2f-_ROUvU9c,39756
|
|
33
33
|
boris/export_observation.py,sha256=B8ASj6H70xfcTSUHrbcJa6YOYjih2WD4DSiUtXj5eAk,50764
|
|
34
34
|
boris/external_processes.py,sha256=PogE2eEiQLVZ2useMamQMOAeDmMUX_TlIpqPKLMm6Ak,13607
|
|
@@ -36,11 +36,11 @@ boris/geometric_measurement.py,sha256=4pI-AYpBSFlJBqS-f8dnkgLtj_Z2E5kwwAdh6WwZ4k
|
|
|
36
36
|
boris/gui_utilities.py,sha256=2HdWFxo2y0oxC29VJAA3R-TOMxVbOy3FuVwspjrTD6A,5519
|
|
37
37
|
boris/image_overlay.py,sha256=zZAL8MTt2i2s58CuX81Nym3rJ5pKiTeP4AO8WbIUonM,2527
|
|
38
38
|
boris/import_observations.py,sha256=zKrkpk1ADxTj2BECactPPOhU6wtrh3TjtOWue2HCT5w,9074
|
|
39
|
-
boris/irr.py,sha256=
|
|
39
|
+
boris/irr.py,sha256=n6Y_Y9iEKOf9_7EE_lDRNei7tq2wkFKk_JVflm9UQdk,22335
|
|
40
40
|
boris/latency.py,sha256=48z9L_A582-wKCfD0M3h0uyYkeL2ezjlQAS_GzeoOe0,9739
|
|
41
41
|
boris/measurement_widget.py,sha256=lZV62KtK6TjdoNbKxj3uyNAuL5dfnQnn7mYwzMo-dOM,4480
|
|
42
42
|
boris/media_file.py,sha256=Wnw-PCyAz6CA00zhjrx0UTgXZ0wmHuNlnElV_TzJ_2M,4818
|
|
43
|
-
boris/menu_options.py,sha256=
|
|
43
|
+
boris/menu_options.py,sha256=uznHFMtpGRWL6Eig10gJ5tOiypgOr9XVyxRiuCbgN9U,7146
|
|
44
44
|
boris/modifier_coding_map_creator.py,sha256=NQHy_txgxKZnGByXiro_Oy_cq4DrFaFiAYwVp1CWrTs,33281
|
|
45
45
|
boris/modifiers_coding_map.py,sha256=oT56ZY_PXhEJsMoblEsyNMAPbDpv7ZMOCnvmt7Ibx_Y,4554
|
|
46
46
|
boris/mpv-1.0.3.py,sha256=EXRtzQqFjOn4wMC6482Ilq3fNQ9N1GRP1VxwLzdeaBY,88077
|
|
@@ -63,7 +63,7 @@ boris/plugins.py,sha256=lYR_sLqRMdyGEoSPwaLyVuF50B92k6vR8TlDAE5B4l0,14252
|
|
|
63
63
|
boris/preferences.py,sha256=gWkqKvKuAAzjNbL3_NdBeaHfNC5xKQVxVZW4J1OwYRg,20763
|
|
64
64
|
boris/preferences_ui.py,sha256=wbo51aBNdcQTJni1DmUM5ZQPOwAtKSkEQam7rRzRS5g,34166
|
|
65
65
|
boris/project.py,sha256=nyXfCDY_rLP3jC1QGv-280jUKgbABqESjOm7I19rJ1U,86432
|
|
66
|
-
boris/project_functions.py,sha256=
|
|
66
|
+
boris/project_functions.py,sha256=o0IOvhGs1cqEjpdeNUeY-qvFfWAQl_7tsUEKxogKRuU,80869
|
|
67
67
|
boris/project_import_export.py,sha256=oBG1CSXfKISsb3TLNT-8BH8kZPAzxIYSNemlLVH1Lh8,38560
|
|
68
68
|
boris/project_ui.py,sha256=yB-ewhHt8S8DTTRIk-dNK2tPMNU24lNji9fDW_Xazu8,38805
|
|
69
69
|
boris/qrc_boris.py,sha256=aH-qUirYY1CGxmTK1SFCPvuZfazIHX4DdUKF1gxZeYM,675008
|
|
@@ -71,35 +71,39 @@ boris/qrc_boris5.py,sha256=prnOw7VGXWXRuVCYp_yIrmWhrlG1F9rx-3BQvkPenjY,161608
|
|
|
71
71
|
boris/select_modifiers.py,sha256=42uG9F75pfPoPJ-blp-vFgmpBpVJtL42FlIxpNpq9z4,13319
|
|
72
72
|
boris/select_observations.py,sha256=k7c3FNVQW74YGH9oFmtHXRVCRnpKGhjCVk3cQtyLML8,8027
|
|
73
73
|
boris/select_subj_behav.py,sha256=ulXbsRY-AIyQRSwXhVlvkNRS_eqWaCvkDKTTyOLqvoE,11742
|
|
74
|
-
boris/state_events.py,sha256=
|
|
74
|
+
boris/state_events.py,sha256=iUrC5ypwIKOnmoq0moDQwtH9-DrgiJ81zL2pMxESucU,7790
|
|
75
75
|
boris/subjects_pad.py,sha256=lSRRGfLfD10_YpGua8RGVdKhoXlsXawGhNibPkRhuzM,3541
|
|
76
76
|
boris/synthetic_time_budget.py,sha256=3Eb9onMLmgqCLd50CuxV9L8RV2ESzfaMWvPK_bXUMMk,10489
|
|
77
77
|
boris/time_budget_functions.py,sha256=y5He8crz0xsTxVfz0jATwFFQVnPAIrNHja_0sF6NtRE,52551
|
|
78
78
|
boris/time_budget_widget.py,sha256=z-tyITBtIz-KH1H2OdMB5a8x9QQLK7Wu96-zkC6NVDA,43213
|
|
79
|
-
boris/transitions.py,sha256=
|
|
79
|
+
boris/transitions.py,sha256=okyDCO-Vn4p_Fixd8cGiSIaUhUxG5ePIOqGSuP52g_c,12246
|
|
80
80
|
boris/utilities.py,sha256=dD5HpojqlAGLVkr3YnOsaqfbCMHFYroe040ZchB5WnM,56662
|
|
81
|
-
boris/version.py,sha256=
|
|
81
|
+
boris/version.py,sha256=fv-Q0KkA5fOHNIN-jnwFQaHABpI1tEKUf19G8ytKUzo,787
|
|
82
82
|
boris/video_equalizer.py,sha256=FartoGghFK-T53zklP70rPKYqTuzL8qdvfGlsOF2wwc,5854
|
|
83
83
|
boris/video_equalizer_ui.py,sha256=1CG3s79eM4JAbaCx3i1ILZXLceb41_gGXlOLNfpBgnw,10142
|
|
84
84
|
boris/video_operations.py,sha256=rXKWndaALaF-yLEPIY_-Z99XRAncZRzRd1sLzwSpbjs,10768
|
|
85
85
|
boris/view_df.py,sha256=AKScLASX2Uatw7rqPbsnio83eVT4GZYCFhL091eMvlY,3370
|
|
86
86
|
boris/view_df_ui.py,sha256=CaMeRH_vQ00CTDDFQn73ZZaS-r8BSTWpL-dMCFqzJ_Q,2775
|
|
87
|
-
boris/write_event.py,sha256=
|
|
87
|
+
boris/write_event.py,sha256=iczoNOHNBZVAy6ZM2bw6dUC1EqoT9kW9umkdU-qo31U,24125
|
|
88
88
|
boris/analysis_plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
89
|
boris/analysis_plugins/_latency.py,sha256=9kCdFDtb5Zdao1xFpioi_exm_IxyGm6RlY3Opn6GUFo,2030
|
|
90
|
+
boris/analysis_plugins/irr_cohen_kappa.py,sha256=UtIZ_y5Wm9UbZpGI67a-AszYnH_5DV1LNcff76ChQYg,2253
|
|
91
|
+
boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py,sha256=olYp-L5DZ71vs9B-k9PyErLY5-O2d-R69TUYx6mAzj4,2563
|
|
92
|
+
boris/analysis_plugins/irr_weighted_cohen_kappa.py,sha256=ZEV9PHq0IfPmGBP3gxgnDlnUyM_NXmEIhqqltUHMGwU,4561
|
|
93
|
+
boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py,sha256=of4EVdUPCpre-HH5l9d1yD-19vj0Judn60OM0Km40EQ,4880
|
|
90
94
|
boris/analysis_plugins/list_of_dataframe_columns.py,sha256=VEiVhxADtyaIKN4JrfFV02TuTAfWhQ60bf1mHVQp27I,437
|
|
91
95
|
boris/analysis_plugins/number_of_occurences.py,sha256=IDyDrdezqvSKT3BlD8QWpSYk8X9nnBBLI80OUnFJ3bY,509
|
|
92
96
|
boris/analysis_plugins/number_of_occurences_by_independent_variable.py,sha256=_7HTKXsyxNfyO69tP8zkQEHzT0C7qHdL1sqBjnUfRQY,1459
|
|
93
97
|
boris/analysis_plugins/time_budget.py,sha256=C1wNYwd5Jugr8h5z2aXRUBY8dF8pD4n953dPwNHY5VY,2244
|
|
94
|
-
boris/portion/__init__.py,sha256=
|
|
95
|
-
boris/portion/const.py,sha256=
|
|
96
|
-
boris/portion/dict.py,sha256=
|
|
97
|
-
boris/portion/func.py,sha256=
|
|
98
|
-
boris/portion/interval.py,sha256=
|
|
99
|
-
boris/portion/io.py,sha256=
|
|
100
|
-
boris_behav_obs-9.6.
|
|
101
|
-
boris_behav_obs-9.6.
|
|
102
|
-
boris_behav_obs-9.6.
|
|
103
|
-
boris_behav_obs-9.6.
|
|
104
|
-
boris_behav_obs-9.6.
|
|
105
|
-
boris_behav_obs-9.6.
|
|
98
|
+
boris/portion/__init__.py,sha256=KmMu4q-iIFO5nrVRIP340mEzxzOlZQqaRupiDmS70es,642
|
|
99
|
+
boris/portion/const.py,sha256=JSYZUktIPCekB6qSom1FPfASn-X7w0G5-aNNHKhIZnw,1715
|
|
100
|
+
boris/portion/dict.py,sha256=uNM-LEY52CZ2VNMMW_C9QukoyTvPlQf8vcbGa1lQBHI,11281
|
|
101
|
+
boris/portion/func.py,sha256=mSQr20YS1ug7R1fRqBg8LifjtXDRvJ6Kjc3WOeL9P34,2172
|
|
102
|
+
boris/portion/interval.py,sha256=sOlj3MAGGaB-JxCkigS-n3qw0fY7TANAsXv1pavr8J4,19931
|
|
103
|
+
boris/portion/io.py,sha256=kpq44pw3xnIyAlPwaR5qRHKRdZ72f8HS9YVIWs5k2pk,6367
|
|
104
|
+
boris_behav_obs-9.6.2.dist-info/licenses/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
|
|
105
|
+
boris_behav_obs-9.6.2.dist-info/METADATA,sha256=Y4dUMgXciZdoXdacbzA0NULwV7c0Cn9kEgveFCXVss4,4637
|
|
106
|
+
boris_behav_obs-9.6.2.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
|
|
107
|
+
boris_behav_obs-9.6.2.dist-info/entry_points.txt,sha256=k__8XvFi4vaA4QFvQehCZjYkKmZH34HSAJI2iYCWrMs,52
|
|
108
|
+
boris_behav_obs-9.6.2.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
|
|
109
|
+
boris_behav_obs-9.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|