boris-behav-obs 9.7.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. boris/__init__.py +26 -0
  2. boris/__main__.py +25 -0
  3. boris/about.py +143 -0
  4. boris/add_modifier.py +635 -0
  5. boris/add_modifier_ui.py +303 -0
  6. boris/advanced_event_filtering.py +455 -0
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +1110 -0
  18. boris/behavior_binary_table.py +305 -0
  19. boris/behaviors_coding_map.py +239 -0
  20. boris/boris_cli.py +340 -0
  21. boris/cmd_arguments.py +49 -0
  22. boris/coding_pad.py +280 -0
  23. boris/config.py +785 -0
  24. boris/config_file.py +356 -0
  25. boris/connections.py +409 -0
  26. boris/converters.py +333 -0
  27. boris/converters_ui.py +225 -0
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +5901 -0
  30. boris/core_qrc.py +15958 -0
  31. boris/core_ui.py +1107 -0
  32. boris/db_functions.py +324 -0
  33. boris/dev.py +134 -0
  34. boris/dialog.py +1108 -0
  35. boris/duration_widget.py +238 -0
  36. boris/edit_event.py +245 -0
  37. boris/edit_event_ui.py +233 -0
  38. boris/event_operations.py +1040 -0
  39. boris/events_cursor.py +61 -0
  40. boris/events_snapshots.py +596 -0
  41. boris/exclusion_matrix.py +141 -0
  42. boris/export_events.py +1006 -0
  43. boris/export_observation.py +1203 -0
  44. boris/external_processes.py +332 -0
  45. boris/geometric_measurement.py +941 -0
  46. boris/gui_utilities.py +135 -0
  47. boris/image_overlay.py +72 -0
  48. boris/import_observations.py +242 -0
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +634 -0
  51. boris/latency.py +244 -0
  52. boris/measurement_widget.py +161 -0
  53. boris/media_file.py +115 -0
  54. boris/menu_options.py +213 -0
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +157 -0
  57. boris/mpv.py +2016 -0
  58. boris/mpv2.py +2193 -0
  59. boris/observation.py +1453 -0
  60. boris/observation_operations.py +2538 -0
  61. boris/observation_ui.py +679 -0
  62. boris/observations_list.py +337 -0
  63. boris/otx_parser.py +442 -0
  64. boris/param_panel.py +201 -0
  65. boris/param_panel_ui.py +305 -0
  66. boris/player_dock_widget.py +198 -0
  67. boris/plot_data_module.py +536 -0
  68. boris/plot_events.py +634 -0
  69. boris/plot_events_rt.py +237 -0
  70. boris/plot_spectrogram_rt.py +316 -0
  71. boris/plot_waveform_rt.py +230 -0
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +31 -0
  74. boris/portion/const.py +95 -0
  75. boris/portion/dict.py +365 -0
  76. boris/portion/func.py +52 -0
  77. boris/portion/interval.py +581 -0
  78. boris/portion/io.py +181 -0
  79. boris/preferences.py +510 -0
  80. boris/preferences_ui.py +770 -0
  81. boris/project.py +2007 -0
  82. boris/project_functions.py +2041 -0
  83. boris/project_import_export.py +1096 -0
  84. boris/project_ui.py +794 -0
  85. boris/qrc_boris.py +10389 -0
  86. boris/qrc_boris5.py +2579 -0
  87. boris/select_modifiers.py +312 -0
  88. boris/select_observations.py +210 -0
  89. boris/select_subj_behav.py +286 -0
  90. boris/state_events.py +197 -0
  91. boris/subjects_pad.py +106 -0
  92. boris/synthetic_time_budget.py +290 -0
  93. boris/time_budget_functions.py +1136 -0
  94. boris/time_budget_widget.py +1039 -0
  95. boris/transitions.py +365 -0
  96. boris/utilities.py +1810 -0
  97. boris/version.py +24 -0
  98. boris/video_equalizer.py +159 -0
  99. boris/video_equalizer_ui.py +248 -0
  100. boris/video_operations.py +310 -0
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.7.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
  106. boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
  107. boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
  108. boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
  109. boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
boris/db_functions.py ADDED
@@ -0,0 +1,324 @@
1
+ """
2
+ BORIS
3
+ Behavioral Observation Research Interactive Software
4
+ Copyright 2012-2025 Olivier Friard
5
+
6
+
7
+ This program is free software; you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation; either version 2 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program; if not, write to the Free Software
19
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20
+ MA 02110-1301, USA.
21
+
22
+ """
23
+
24
+ import sqlite3
25
+ import logging
26
+ from typing import Optional, Tuple
27
+ from . import config as cfg
28
+ from . import project_functions
29
+ from . import event_operations
30
+
31
+
32
+ def load_events_in_db(
33
+ pj: dict,
34
+ selected_subjects: list,
35
+ selected_observations: list,
36
+ selected_behaviors: list,
37
+ time_interval: str = cfg.TIME_FULL_OBS,
38
+ ):
39
+ """
40
+ populate a memory sqlite database with events from selected_observations,
41
+ selected_subjects and selected_behaviors
42
+
43
+ Args:
44
+ pj (dict): project dictionary
45
+ selected_observations (list):
46
+ selected_subjects (list):
47
+ selected_behaviors (list):
48
+ time_interval (str): time interval for loading events (cfg.TIME_FULL_OBS / TIME_cfg.EVENTS / TIME_ARBITRARY_INTERVAL)
49
+
50
+ Returns:
51
+ database cursor:
52
+
53
+ """
54
+
55
+ # selected behaviors defined as state event
56
+ state_behaviors_codes = [
57
+ pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
58
+ for x in pj[cfg.ETHOGRAM]
59
+ if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
60
+ ]
61
+
62
+ # selected behaviors defined as point event
63
+ """
64
+ point_behaviors_codes = [
65
+ pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
66
+ for x in pj[cfg.ETHOGRAM]
67
+ if cfg.POINT in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
68
+ and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
69
+ ]
70
+ """
71
+
72
+ db = sqlite3.connect(":memory:", isolation_level=None)
73
+
74
+ """
75
+ import os
76
+ os.system("rm /tmp/ramdisk/events.sqlite")
77
+ db = sqlite3.connect("/tmp/ramdisk/events.sqlite", isolation_level=None)
78
+ """
79
+
80
+ db.row_factory = sqlite3.Row
81
+ cursor = db.cursor()
82
+ cursor.execute(
83
+ (
84
+ "CREATE TABLE events (observation TEXT, "
85
+ "subject TEXT, "
86
+ "code TEXT, "
87
+ "type TEXT, "
88
+ "modifiers TEXT, "
89
+ "occurence FLOAT, "
90
+ "comment TEXT,"
91
+ "image_index INTEGER,"
92
+ "image_path TEXT)"
93
+ )
94
+ )
95
+
96
+ cursor.execute("CREATE INDEX observation_idx ON events(observation)")
97
+ cursor.execute("CREATE INDEX subject_idx ON events(subject)")
98
+ cursor.execute("CREATE INDEX code_idx ON events(code)")
99
+ cursor.execute("CREATE INDEX modifiers_idx ON events(modifiers)")
100
+
101
+ for subject_to_analyze in selected_subjects:
102
+ for obs_id in selected_observations:
103
+ for event in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]:
104
+ if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in selected_behaviors:
105
+ # extract time, code, modifier and comment (time:0, subject:1, code:2, modifier:3, comment:4)
106
+ if (subject_to_analyze == cfg.NO_FOCAL_SUBJECT and event[cfg.EVENT_SUBJECT_FIELD_IDX] == "") or (
107
+ event[cfg.EVENT_SUBJECT_FIELD_IDX] == subject_to_analyze
108
+ ):
109
+ if pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
110
+ cursor.execute(
111
+ (
112
+ "INSERT INTO events "
113
+ "(observation, subject, code, type, modifiers, occurence, comment, image_index) "
114
+ "VALUES (?,?,?,?,?,?,?,?)"
115
+ ),
116
+ (
117
+ obs_id,
118
+ cfg.NO_FOCAL_SUBJECT
119
+ if event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
120
+ else event[cfg.EVENT_SUBJECT_FIELD_IDX],
121
+ event[cfg.EVENT_BEHAVIOR_FIELD_IDX],
122
+ cfg.STATE if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes else cfg.POINT,
123
+ event[cfg.EVENT_MODIFIER_FIELD_IDX],
124
+ float(event[cfg.EVENT_TIME_FIELD_IDX]) if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan() else None,
125
+ event[cfg.EVENT_COMMENT_FIELD_IDX],
126
+ # frame index or NA
127
+ event_operations.read_event_field(event, pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE], cfg.FRAME_INDEX),
128
+ ),
129
+ )
130
+
131
+ if pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] == cfg.IMAGES:
132
+ cursor.execute(
133
+ (
134
+ "INSERT INTO events "
135
+ "(observation, subject, code, type, modifiers, occurence, comment, image_index, image_path) "
136
+ "VALUES (?,?,?,?,?,?,?,?,?)"
137
+ ),
138
+ (
139
+ obs_id,
140
+ cfg.NO_FOCAL_SUBJECT
141
+ if event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
142
+ else event[cfg.EVENT_SUBJECT_FIELD_IDX],
143
+ event[cfg.EVENT_BEHAVIOR_FIELD_IDX],
144
+ cfg.STATE if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes else cfg.POINT,
145
+ event[cfg.EVENT_MODIFIER_FIELD_IDX],
146
+ float(event[cfg.EVENT_TIME_FIELD_IDX]) if not event[cfg.EVENT_TIME_FIELD_IDX].is_nan() else None,
147
+ event[cfg.EVENT_COMMENT_FIELD_IDX],
148
+ event[cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.IMAGE_INDEX]],
149
+ event[cfg.PJ_OBS_FIELDS[cfg.IMAGES][cfg.IMAGE_PATH]],
150
+ ),
151
+ )
152
+
153
+ db.commit()
154
+ return cursor
155
+
156
+
157
+ def load_aggregated_events_in_db(
158
+ pj: dict, selected_subjects: list, selected_observations: list, selected_behaviors: list
159
+ ) -> Tuple[bool, str, Optional[sqlite3.Connection]]:
160
+ """
161
+ populate a memory sqlite database with aggregated events from selected_observations, selected_subjects and selected_behaviors
162
+
163
+ Args:
164
+ pj (dict): project dictionary
165
+ selected_observations (list):
166
+ selected_subjects (list):
167
+ selected_behaviors (list):
168
+
169
+ Returns:
170
+ bool: True if OK else False
171
+ str: error message
172
+ database connector: db connector if bool True else None
173
+
174
+ """
175
+
176
+ logging.debug("function: load_aggregated_events_in_db")
177
+
178
+ # if no observation selected select all
179
+ if not selected_observations:
180
+ selected_observations = sorted([x for x in pj[cfg.OBSERVATIONS]])
181
+
182
+ # if no subject selected select all
183
+ if not selected_subjects:
184
+ selected_subjects = sorted([pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in pj[cfg.SUBJECTS]] + [cfg.NO_FOCAL_SUBJECT])
185
+
186
+ # if no behavior selected select all
187
+ if not selected_behaviors:
188
+ selected_behaviors = sorted([pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in pj[cfg.ETHOGRAM]])
189
+
190
+ # check if state events are paired
191
+ out: str = ""
192
+ for obs_id in selected_observations:
193
+ r, msg = project_functions.check_state_events_obs(obs_id, pj[cfg.ETHOGRAM], pj[cfg.OBSERVATIONS][obs_id], cfg.HHMMSS)
194
+ if not r:
195
+ out += f"Observation: <strong>{obs_id}</strong><br>{msg}<br>"
196
+ if out:
197
+ return False, out, None
198
+
199
+ # selected behaviors defined as state event
200
+ state_behaviors_codes = [
201
+ pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
202
+ for x in pj[cfg.ETHOGRAM]
203
+ if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
204
+ ]
205
+
206
+ # selected behaviors defined as point event
207
+ point_behaviors_codes = [
208
+ pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
209
+ for x in pj[cfg.ETHOGRAM]
210
+ if cfg.POINT in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper() and pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in selected_behaviors
211
+ ]
212
+
213
+ db = sqlite3.connect(":memory:")
214
+
215
+ # only for debugging
216
+ """
217
+ import os
218
+ os.system("rm /tmp/ramdisk/aggregated_events.sqlite")
219
+ db = sqlite3.connect("/tmp/ramdisk/aggregated_events.sqlite", isolation_level=None)
220
+ """
221
+
222
+ db.row_factory = sqlite3.Row
223
+ cursor2 = db.cursor()
224
+ cursor2.execute(
225
+ (
226
+ "CREATE TABLE aggregated_events "
227
+ "(id INTEGER PRIMARY KEY ASC, "
228
+ "observation TEXT, "
229
+ "subject TEXT, "
230
+ "behavior TEXT, "
231
+ "type TEXT, "
232
+ "modifiers TEXT, "
233
+ "start FLOAT, "
234
+ "stop FLOAT, "
235
+ "comment TEXT, "
236
+ "comment_stop TEXT,"
237
+ "image_index_start INTEGER,"
238
+ "image_index_stop INTEGER,"
239
+ "image_path_start TEXT,"
240
+ "image_path_stop TEXT)"
241
+ )
242
+ )
243
+
244
+ cursor2.execute("CREATE INDEX observation_idx ON aggregated_events(observation)")
245
+ cursor2.execute("CREATE INDEX subject_idx ON aggregated_events(subject)")
246
+ cursor2.execute("CREATE INDEX behavior_idx ON aggregated_events(behavior)")
247
+ cursor2.execute("CREATE INDEX modifiers_idx ON aggregated_events(modifiers)")
248
+
249
+ # too slow! cursor1 = load_events_in_db(pj, selected_subjects, selected_observations, selected_behaviors)
250
+
251
+ insert_sql = (
252
+ "INSERT INTO aggregated_events (observation, subject, behavior, type, modifiers, "
253
+ " start, stop, comment, comment_stop, "
254
+ "image_index_start, image_index_stop, image_path_start, image_path_stop) "
255
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"
256
+ )
257
+
258
+ for obs_id in selected_observations:
259
+ cursor1 = load_events_in_db(pj, selected_subjects, [obs_id], selected_behaviors)
260
+
261
+ for subject in selected_subjects:
262
+ for behavior in selected_behaviors:
263
+ cursor1.execute(
264
+ "SELECT DISTINCT modifiers FROM events WHERE subject=? AND code=? ORDER BY modifiers",
265
+ (
266
+ subject,
267
+ behavior,
268
+ ),
269
+ )
270
+
271
+ rows_distinct_modifiers = list(x[0] for x in cursor1.fetchall())
272
+
273
+ for distinct_modifiers in rows_distinct_modifiers:
274
+ cursor1.execute(
275
+ (
276
+ "SELECT occurence, comment, image_index, image_path FROM events "
277
+ "WHERE subject = ? AND code = ? AND modifiers = ? ORDER by occurence"
278
+ ),
279
+ (subject, behavior, distinct_modifiers),
280
+ )
281
+ rows = list(cursor1.fetchall())
282
+
283
+ for idx, row in enumerate(rows):
284
+ if behavior in point_behaviors_codes:
285
+ data = (
286
+ obs_id,
287
+ subject,
288
+ behavior,
289
+ cfg.POINT,
290
+ distinct_modifiers,
291
+ row["occurence"],
292
+ row["occurence"],
293
+ row["comment"],
294
+ "", # no stop comment for point event
295
+ row["image_index"],
296
+ row["image_index"],
297
+ row["image_path"],
298
+ row["image_path"],
299
+ )
300
+ cursor2.execute(insert_sql, data)
301
+
302
+ if behavior in state_behaviors_codes:
303
+ if idx % 2 == 0:
304
+ data = (
305
+ obs_id,
306
+ subject,
307
+ behavior,
308
+ cfg.STATE,
309
+ distinct_modifiers,
310
+ row["occurence"],
311
+ rows[idx + 1]["occurence"],
312
+ row["comment"],
313
+ rows[idx + 1]["comment"],
314
+ row["image_index"],
315
+ rows[idx + 1]["image_index"],
316
+ row["image_path"],
317
+ rows[idx + 1]["image_path"],
318
+ )
319
+
320
+ cursor2.execute(insert_sql, data)
321
+
322
+ db.commit()
323
+
324
+ return True, "", db
boris/dev.py ADDED
@@ -0,0 +1,134 @@
1
+ import sys
2
+ import time
3
+ import pprint
4
+ import pandas as pd
5
+
6
+ from . import project_functions
7
+ from . import config as cfg
8
+ from . import utilities as util
9
+
10
+
11
+ def event_val(event, field, obs_type: str = cfg.MEDIA):
12
+ try:
13
+ return event[cfg.PJ_OBS_FIELDS[obs_type][field]]
14
+ except:
15
+ return None
16
+
17
+
18
+ _, _, pj, _ = project_functions.open_project_json(sys.argv[1])
19
+
20
+ # pprint.pprint(list(pj[cfg.OBSERVATIONS].keys()))
21
+
22
+ # print()
23
+
24
+ """
25
+ obs_id = "images NO TIME"
26
+ pprint.pprint(pj[cfg.OBSERVATIONS][obs_id])
27
+ """
28
+
29
+ state_events_list = util.state_behavior_codes(pj[cfg.ETHOGRAM])
30
+
31
+ df_def = {
32
+ "observation id": pd.Series(dtype="str"),
33
+ "observation type": pd.Series(dtype="str"),
34
+ "observation description": pd.Series(dtype="str"),
35
+ "subject": pd.Series(dtype="str"),
36
+ "behavior": pd.Series(dtype="str"),
37
+ "modifier": pd.Series(dtype="str"),
38
+ "start": pd.Series(dtype="float"),
39
+ "stop": pd.Series(dtype="float"),
40
+ "duration": pd.Series(dtype="float"),
41
+ }
42
+ df = pd.DataFrame(df_def)
43
+ l = []
44
+ # print(df.info)
45
+
46
+ for obs_id in pj[cfg.OBSERVATIONS]:
47
+ # print(obs_id)
48
+ obs_type = pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE]
49
+ obs_descr = pj[cfg.OBSERVATIONS][obs_id][cfg.DESCRIPTION]
50
+ mem_idx = []
51
+ for idx, event in enumerate(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]):
52
+ if idx in mem_idx:
53
+ continue
54
+ start = event_val(event, cfg.TIME)
55
+ subject = event_val(event, cfg.SUBJECT)
56
+ behavior = event_val(event, cfg.BEHAVIOR_CODE)
57
+ modifier = event_val(event, cfg.MODIFIER)
58
+ if behavior in state_events_list:
59
+ stop = None
60
+ for idx2, event2 in enumerate(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][idx + 1 :], start=idx + 1):
61
+ subject2 = event_val(event2, cfg.SUBJECT)
62
+ behavior2 = event_val(event2, cfg.BEHAVIOR_CODE)
63
+ modifier2 = event_val(event2, cfg.MODIFIER)
64
+ if subject == subject2 and behavior == behavior2 and modifier == modifier2:
65
+ stop = event_val(event2, cfg.TIME)
66
+ mem_idx.append(idx2)
67
+ l.append([obs_id, obs_type, obs_descr, subject, behavior, modifier, start, stop, stop - start])
68
+ break
69
+ if stop is None:
70
+ print(obs_id, " not paired")
71
+ # print(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS])
72
+ # print(f"{l=}")
73
+ # print(f"{mem_idx=}")
74
+
75
+ sys.exit()
76
+ else:
77
+ l.append([obs_id, obs_type, obs_descr, subject, behavior, modifier, start, start, 0])
78
+
79
+ # print(pd.DataFrame(l))
80
+ df = pd.concat(
81
+ [
82
+ df,
83
+ pd.DataFrame(
84
+ l,
85
+ columns=[
86
+ "observation id",
87
+ "observation type",
88
+ "observation description",
89
+ "subject",
90
+ "behavior",
91
+ "modifier",
92
+ "start",
93
+ "stop",
94
+ "duration",
95
+ ],
96
+ ),
97
+ ]
98
+ )
99
+ del l
100
+ print("=" * 30)
101
+ print("describe")
102
+ print(df.describe())
103
+ print("=" * 30)
104
+
105
+ # print(f'{df["subject"].value_counts()=}')
106
+ # print(f'{df["subject"].nunique()=}')
107
+
108
+ pd.set_option("display.max_rows", None, "display.max_columns", None)
109
+
110
+ print("=" * 30)
111
+ print("mean")
112
+ r = df.groupby(["subject", "behavior"])["duration"].mean()
113
+ print(r)
114
+ print("=" * 30)
115
+
116
+
117
+ r = df.groupby(["observation id", "subject", "behavior"])
118
+ print(r["start"])
119
+
120
+
121
+ """
122
+ # replace value (for selecting a time interval)
123
+ t1 = time.time()
124
+ df.loc[df["stop"] > 10, "stop"] = 10
125
+ print(time.time() - t1)
126
+ print(df)
127
+ """
128
+
129
+
130
+ """
131
+ t1 = 1
132
+ t2 = 2
133
+ print(df.query(f"`start` <= {t1} & `stop` >= {t2} & `duration` != 0"))
134
+ """