boris-behav-obs 9.6.1__py2.py3-none-any.whl → 9.6.3__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/analysis_plugins/time_budget.py +0 -4
- boris/behav_coding_map_creator.py +0 -1
- boris/boris_cli.py +1 -1
- boris/coding_pad.py +0 -2
- boris/core.py +11 -28
- boris/core_qrc.py +3 -0
- boris/db_functions.py +4 -4
- boris/edit_event.py +0 -9
- boris/exclusion_matrix.py +1 -1
- boris/export_events.py +63 -71
- boris/gui_utilities.py +0 -1
- boris/irr.py +10 -22
- boris/menu_options.py +2 -0
- boris/modifier_coding_map_creator.py +0 -2
- boris/observation_operations.py +2 -4
- boris/param_panel.py +0 -4
- boris/plot_spectrogram_rt.py +2 -1
- boris/plot_waveform_rt.py +2 -1
- 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/time_budget_functions.py +0 -9
- boris/transitions.py +1 -1
- boris/version.py +2 -2
- boris/video_equalizer.py +0 -2
- boris/view_df.py +0 -2
- boris/write_event.py +4 -13
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/METADATA +12 -8
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/RECORD +43 -39
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/WHEEL +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/top_level.txt +0 -0
boris/export_events.py
CHANGED
|
@@ -702,7 +702,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
702
702
|
if not export_dir:
|
|
703
703
|
return
|
|
704
704
|
|
|
705
|
-
mem_command = ""
|
|
705
|
+
mem_command: str = ""
|
|
706
706
|
|
|
707
707
|
# see https://www.fon.hum.uva.nl/praat/manual/TextGrid_file_formats.html
|
|
708
708
|
|
|
@@ -795,6 +795,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
795
795
|
max_time = float(obs_interval[1]) + offset if obs_interval[1] != 0 else float(max_media_duration)
|
|
796
796
|
|
|
797
797
|
# delete events outside time interval
|
|
798
|
+
|
|
798
799
|
cursor.execute(
|
|
799
800
|
"DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)",
|
|
800
801
|
(
|
|
@@ -838,38 +839,30 @@ def export_events_as_textgrid(self) -> None:
|
|
|
838
839
|
|
|
839
840
|
next_obs: bool = False
|
|
840
841
|
|
|
841
|
-
|
|
842
|
-
total_media_duration = round(
|
|
843
|
-
observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id]), 3
|
|
844
|
-
)
|
|
845
|
-
"""
|
|
846
|
-
|
|
842
|
+
# number of items for size parameter
|
|
847
843
|
cursor.execute(
|
|
848
844
|
(
|
|
849
|
-
"SELECT COUNT(
|
|
850
|
-
"WHERE observation = ? AND subject IN ({})
|
|
851
|
-
",".join(["?"] * len(parameters[cfg.SELECTED_SUBJECTS]))
|
|
852
|
-
)
|
|
845
|
+
"SELECT COUNT(*) FROM (SELECT * FROM aggregated_events "
|
|
846
|
+
f"WHERE observation = ? AND subject IN ({','.join(['?'] * len(parameters[cfg.SELECTED_SUBJECTS]))}) GROUP BY subject, behavior) "
|
|
853
847
|
),
|
|
854
848
|
[obs_id] + parameters[cfg.SELECTED_SUBJECTS],
|
|
855
849
|
)
|
|
856
850
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
subjectsMax = max_time
|
|
851
|
+
subjects_num = int(cursor.fetchone()[0])
|
|
852
|
+
subjects_max = max_time
|
|
860
853
|
|
|
861
854
|
out = (
|
|
862
855
|
'File type = "ooTextFile"\n'
|
|
863
856
|
'Object class = "TextGrid"\n'
|
|
864
857
|
"\n"
|
|
865
858
|
f"xmin = 0.0\n"
|
|
866
|
-
f"xmax = {
|
|
859
|
+
f"xmax = {subjects_max}\n"
|
|
867
860
|
"tiers? <exists>\n"
|
|
868
|
-
f"size = {
|
|
861
|
+
f"size = {subjects_num}\n"
|
|
869
862
|
"item []:\n"
|
|
870
863
|
)
|
|
871
864
|
|
|
872
|
-
subject_index = 0
|
|
865
|
+
subject_index: int = 0
|
|
873
866
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
874
867
|
if subject not in [
|
|
875
868
|
x[cfg.EVENT_SUBJECT_FIELD_IDX] if x[cfg.EVENT_SUBJECT_FIELD_IDX] else cfg.NO_FOCAL_SUBJECT
|
|
@@ -877,7 +870,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
877
870
|
]:
|
|
878
871
|
continue
|
|
879
872
|
|
|
880
|
-
intervalsMin
|
|
873
|
+
intervalsMin = min_time
|
|
874
|
+
intervalsMax = max_time
|
|
881
875
|
|
|
882
876
|
# STATE events
|
|
883
877
|
cursor.execute(
|
|
@@ -892,68 +886,66 @@ def export_events_as_textgrid(self) -> None:
|
|
|
892
886
|
{"start": util.float2decimal(r["start"]), "stop": util.float2decimal(r["stop"]), "code": r["behavior"]}
|
|
893
887
|
for r in cursor.fetchall()
|
|
894
888
|
]
|
|
895
|
-
if
|
|
896
|
-
|
|
889
|
+
if rows:
|
|
890
|
+
out += interval_subject_header
|
|
897
891
|
|
|
898
|
-
|
|
892
|
+
count = 0
|
|
899
893
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
"It is not possible to create the Praat TextGrid file."
|
|
894
|
+
# check if 1st behavior starts at the beginning
|
|
895
|
+
if rows[0]["start"] > 0:
|
|
896
|
+
count += 1
|
|
897
|
+
out += interval_template.format(count=count, name="null", xmin=0.0, xmax=rows[0]["start"])
|
|
898
|
+
|
|
899
|
+
for idx, row in enumerate(rows):
|
|
900
|
+
# check if events are overlapping
|
|
901
|
+
if (idx + 1 < len(rows)) and (row["stop"] > rows[idx + 1]["start"]):
|
|
902
|
+
self.results.ptText.appendHtml(
|
|
903
|
+
(
|
|
904
|
+
f"The events overlap for subject <b>{subject}</b> in the observation <b>{obs_id}</b>. "
|
|
905
|
+
"It is not possible to create the Praat TextGrid file."
|
|
906
|
+
)
|
|
914
907
|
)
|
|
915
|
-
|
|
916
|
-
QApplication.processEvents()
|
|
908
|
+
QApplication.processEvents()
|
|
917
909
|
|
|
918
|
-
|
|
919
|
-
|
|
910
|
+
next_obs = True
|
|
911
|
+
break
|
|
920
912
|
|
|
921
|
-
|
|
913
|
+
count += 1
|
|
922
914
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
915
|
+
if (idx + 1 < len(rows)) and (rows[idx + 1]["start"] - dec("0.001") <= row["stop"] < rows[idx + 1]["start"]):
|
|
916
|
+
xmax = rows[idx + 1]["start"]
|
|
917
|
+
else:
|
|
918
|
+
xmax = row["stop"]
|
|
919
|
+
|
|
920
|
+
out += interval_template.format(count=count, name=row["code"], xmin=row["start"], xmax=xmax)
|
|
921
|
+
|
|
922
|
+
# check if no behavior
|
|
923
|
+
if (idx + 1 < len(rows)) and (row["stop"] < rows[idx + 1]["start"] - dec("0.001")):
|
|
924
|
+
count += 1
|
|
925
|
+
out += interval_template.format(
|
|
926
|
+
count=count,
|
|
927
|
+
name="null",
|
|
928
|
+
xmin=row["stop"],
|
|
929
|
+
xmax=rows[idx + 1]["start"],
|
|
930
|
+
)
|
|
927
931
|
|
|
928
|
-
|
|
932
|
+
if next_obs:
|
|
933
|
+
break
|
|
929
934
|
|
|
930
|
-
# check if
|
|
931
|
-
if
|
|
935
|
+
# check if last event ends at the end of media file
|
|
936
|
+
if rows[-1]["stop"] < max_time:
|
|
932
937
|
count += 1
|
|
933
|
-
out += interval_template.format(
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if rows[-1]["stop"] < max_time:
|
|
945
|
-
count += 1
|
|
946
|
-
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
947
|
-
|
|
948
|
-
# add info
|
|
949
|
-
subject_index += 1
|
|
950
|
-
out = out.format(
|
|
951
|
-
subject_index=subject_index,
|
|
952
|
-
subject=subject,
|
|
953
|
-
intervalsSize=count,
|
|
954
|
-
intervalsMin=intervalsMin,
|
|
955
|
-
intervalsMax=intervalsMax,
|
|
956
|
-
)
|
|
938
|
+
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
939
|
+
|
|
940
|
+
# add info
|
|
941
|
+
subject_index += 1
|
|
942
|
+
out = out.format(
|
|
943
|
+
subject_index=subject_index,
|
|
944
|
+
subject=subject,
|
|
945
|
+
intervalsSize=count,
|
|
946
|
+
intervalsMin=intervalsMin,
|
|
947
|
+
intervalsMax=intervalsMax,
|
|
948
|
+
)
|
|
957
949
|
|
|
958
950
|
# POINT events
|
|
959
951
|
cursor.execute(
|
boris/gui_utilities.py
CHANGED
|
@@ -66,7 +66,6 @@ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
|
|
|
66
66
|
ini_file_path = pl.Path.home() / pl.Path(".boris")
|
|
67
67
|
if ini_file_path.is_file():
|
|
68
68
|
settings = QSettings(str(ini_file_path), QSettings.IniFormat)
|
|
69
|
-
print(settings.value(f"{widget_name} geometry"))
|
|
70
69
|
widget.restoreGeometry(settings.value(f"{widget_name} geometry"))
|
|
71
70
|
logging.debug(f"geometry restored for {widget_name} {settings.value(f'{widget_name} geometry')}")
|
|
72
71
|
else:
|
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/observation_operations.py
CHANGED
|
@@ -1301,10 +1301,8 @@ def init_mpv(self):
|
|
|
1301
1301
|
|
|
1302
1302
|
logging.debug("function: init_mpv")
|
|
1303
1303
|
|
|
1304
|
-
""
|
|
1305
|
-
print(f"{self.winId()=}")
|
|
1306
|
-
print(f"{str(int(self.winId()))=}")
|
|
1307
|
-
"""
|
|
1304
|
+
# print(f"{self.winId()=}")
|
|
1305
|
+
# print(f"{str(int(self.winId()))=}")
|
|
1308
1306
|
|
|
1309
1307
|
subprocess.Popen(
|
|
1310
1308
|
[
|
boris/param_panel.py
CHANGED
|
@@ -96,12 +96,8 @@ class Param_panel(QDialog, Ui_Dialog):
|
|
|
96
96
|
# Set start_time and end_time widgets values even if it is not shown with
|
|
97
97
|
# more than 1 observation as some analyses might use it (eg: advanced event filtering)
|
|
98
98
|
|
|
99
|
-
print(f"{self.end_interval=}")
|
|
100
|
-
|
|
101
99
|
end_interval = self.end_interval if self.end_interval != 0 else self.media_duration
|
|
102
100
|
|
|
103
|
-
print(f"{end_interval=}")
|
|
104
|
-
|
|
105
101
|
self.start_time.set_time(self.start_interval)
|
|
106
102
|
self.end_time.set_time(end_interval)
|
|
107
103
|
self.frm_time_interval.setEnabled(False)
|
boris/plot_spectrogram_rt.py
CHANGED
|
@@ -129,7 +129,8 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
129
129
|
try:
|
|
130
130
|
wav = wave.open(wav_file, "r")
|
|
131
131
|
frames = wav.readframes(-1)
|
|
132
|
-
sound_info = np.fromstring(frames, dtype=np.int16)
|
|
132
|
+
# sound_info = np.fromstring(frames, dtype=np.int16)
|
|
133
|
+
sound_info = np.frombuffer(frames, dtype=np.int16)
|
|
133
134
|
frame_rate = wav.getframerate()
|
|
134
135
|
wav.close()
|
|
135
136
|
return sound_info, frame_rate
|
boris/plot_waveform_rt.py
CHANGED
|
@@ -109,7 +109,8 @@ class Plot_waveform_RT(QWidget):
|
|
|
109
109
|
try:
|
|
110
110
|
wav = wave.open(wav_file, "r")
|
|
111
111
|
frames = wav.readframes(-1)
|
|
112
|
-
signal = np.fromstring(frames, dtype=np.int16)
|
|
112
|
+
# signal = np.fromstring(frames, dtype=np.int16)
|
|
113
|
+
signal = np.frombuffer(frames, dtype=np.int16)
|
|
113
114
|
frame_rate = wav.getframerate()
|
|
114
115
|
wav.close()
|
|
115
116
|
return signal, frame_rate
|
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)
|