A mutable, self-balancing interval tree data structure that enables efficient querying of intervals by point, range overlap, or range envelopment
—
Advanced operations for batch processing, interval manipulation, and tree restructuring. These operations enable efficient modification of multiple intervals simultaneously and provide powerful tools for interval-based data processing.
Remove multiple intervals based on overlap criteria.
def remove_overlap(self, begin, end=None):
"""
Remove all intervals overlapping with point or range.
Parameters:
- begin: Point, range start, or Interval object
- end: Range end (optional if begin is Interval or point query)
Time Complexity: O((r+m)*log n) where r is removed count, m is result size
"""
def remove_envelop(self, begin, end):
"""
Remove all intervals completely contained within range.
Parameters:
- begin: Range start
- end: Range end
Time Complexity: O((r+m)*log n) where r is removed count, m is result size
"""Modify intervals by chopping, slicing, and splitting.
def chop(self, begin, end, datafunc=None):
"""
Remove portion [begin, end) from all intervals, splitting as needed.
Parameters:
- begin: Start of region to remove
- end: End of region to remove
- datafunc: Optional function to modify data of split intervals
Signature: datafunc(interval, is_left_part) -> new_data
Intervals overlapping [begin, end) are modified:
- Completely contained intervals are removed
- Partially overlapping intervals are trimmed or split
"""
def slice(self, point, datafunc=None):
"""
Split all intervals crossing point into two intervals.
Parameters:
- point: Point at which to split intervals
- datafunc: Optional function to modify data of split intervals
Signature: datafunc(interval, is_left_part) -> new_data
Each interval containing point becomes two intervals:
- Left part: [original_begin, point)
- Right part: [point, original_end)
"""Detect and resolve overlapping intervals.
def split_overlaps(self):
"""
Split all overlapping intervals at their boundary points.
After this operation, no two intervals will overlap - they will
either be disjoint or share exact boundaries.
Time Complexity: O(n²*log n) worst case, O(n*log n) best case
"""
def merge_overlaps(self, data_reducer=None, data_initializer=None, strict=True):
"""
Merge all overlapping intervals into single intervals.
Parameters:
- data_reducer: Function to combine data from merged intervals
Signature: data_reducer(current_data, new_data) -> combined_data
- data_initializer: Function to initialize data for merged intervals
Signature: data_initializer(intervals_list) -> initial_data
- strict: If True, merge only overlapping intervals
If False, also merge adjacent intervals
Time Complexity: O(n*log n)
"""
def merge_equals(self, data_reducer=None, data_initializer=None):
"""
Merge intervals with identical ranges (same begin and end).
Parameters:
- data_reducer: Function to combine data from merged intervals
- data_initializer: Function to initialize data for merged intervals
Time Complexity: O(n*log n)
"""Find complex interval relationships.
def find_nested(self):
"""
Find nested interval relationships.
Returns:
dict: Mapping of parent intervals to sets of nested child intervals
{parent_interval: {child1, child2, ...}, ...}
Time Complexity: O(n²)
"""from intervaltree import Interval, IntervalTree
# Create tree with overlapping intervals
tree = IntervalTree([
Interval(0, 10, "a"),
Interval(5, 15, "b"),
Interval(12, 18, "c"),
Interval(20, 30, "d"),
Interval(25, 35, "e")
])
print(f"Initial tree: {len(tree)} intervals")
# Remove all intervals that overlap with range [8, 22)
tree.remove_overlap(8, 22)
print(f"After remove_overlap(8, 22): {len(tree)} intervals")
# Removes intervals a, b, c, d (all overlap with [8, 22))
# Remaining: interval e (25, 35)
# Reset tree
tree = IntervalTree([
Interval(0, 10, "a"),
Interval(5, 15, "b"),
Interval(12, 18, "c"),
Interval(20, 30, "d")
])
# Remove only intervals completely contained within [6, 25)
tree.remove_envelop(6, 25)
print(f"After remove_envelop(6, 25): {len(tree)} intervals")
# Removes intervals c, d (completely within [6, 25))
# Remaining: intervals a, b (partially overlap but not completely contained)from intervaltree import Interval, IntervalTree
# Create tree for chopping example
tree = IntervalTree([
Interval(0, 20, "long_interval"),
Interval(5, 15, "middle_interval"),
Interval(25, 35, "separate_interval")
])
# Chop out the region [8, 12) from all intervals
tree.chop(8, 12)
print(f"After chop(8, 12): {len(tree)} intervals")
# Original interval (0, 20) becomes two: (0, 8) and (12, 20)
# Interval (5, 15) becomes two: (5, 8) and (12, 15)
# Interval (25, 35) unchanged (no overlap with [8, 12))
# Reset for slicing example
tree = IntervalTree([
Interval(0, 20, "span_point"),
Interval(15, 25, "also_spans"),
Interval(30, 40, "no_span")
])
# Split all intervals that cross point 18
tree.slice(18)
print(f"After slice(18): {len(tree)} intervals")
# Interval (0, 20) becomes: (0, 18) and (18, 20)
# Interval (15, 25) becomes: (15, 18) and (18, 25)
# Interval (30, 40) unchanged (doesn't contain point 18)from intervaltree import Interval, IntervalTree
# Example with data modification functions
def modify_chopped_data(interval, is_left_part):
"""Modify data when interval is split by chopping"""
original_data = interval.data
if is_left_part:
return f"{original_data}_left"
else:
return f"{original_data}_right"
def modify_sliced_data(interval, is_left_part):
"""Modify data when interval is split by slicing"""
original_data = interval.data
part = "before" if is_left_part else "after"
return f"{original_data}_{part}_split"
# Create tree
tree = IntervalTree([
Interval(0, 20, "original"),
Interval(10, 30, "another")
])
# Chop with data modification
tree.chop(8, 12, datafunc=modify_chopped_data)
# Results in intervals with modified data like "original_left", "original_right"
# Reset and slice with data modification
tree = IntervalTree([Interval(0, 20, "data")])
tree.slice(10, datafunc=modify_sliced_data)
# Results in "data_before_split" and "data_after_split"from intervaltree import Interval, IntervalTree
# Create tree with overlapping intervals
tree = IntervalTree([
Interval(0, 10, "a"),
Interval(5, 15, "b"),
Interval(12, 20, "c"),
Interval(25, 30, "d"),
Interval(28, 35, "e")
])
def combine_data(current, new):
"""Combine data from merged intervals"""
return f"{current}+{new}"
def initialize_data(intervals):
"""Initialize data for merged intervals"""
data_parts = [i.data for i in intervals]
return "+".join(sorted(data_parts))
print(f"Before merge: {len(tree)} intervals")
# Merge overlapping intervals
tree.merge_overlaps(data_reducer=combine_data, data_initializer=initialize_data)
print(f"After merge_overlaps: {len(tree)} intervals")
# Overlapping intervals (0,10), (5,15), (12,20) become one interval (0,20)
# Overlapping intervals (25,30), (28,35) become one interval (25,35)
# Example with equal intervals
tree = IntervalTree([
Interval(0, 10, "first"),
Interval(0, 10, "second"), # Same range
Interval(0, 10, "third"), # Same range
Interval(15, 25, "different")
])
print(f"Before merge_equals: {len(tree)} intervals")
tree.merge_equals(data_initializer=initialize_data)
print(f"After merge_equals: {len(tree)} intervals")
# Three intervals with range (0,10) become one with combined datafrom intervaltree import Interval, IntervalTree
# Create tree with complex overlaps
tree = IntervalTree([
Interval(0, 15, "a"),
Interval(5, 20, "b"),
Interval(10, 25, "c"),
Interval(30, 40, "d")
])
print(f"Before split_overlaps: {len(tree)} intervals")
for interval in sorted(tree):
print(f" {interval}")
# Split all overlaps at boundary points
tree.split_overlaps()
print(f"After split_overlaps: {len(tree)} intervals")
for interval in sorted(tree):
print(f" {interval}")
# Result: intervals are split at points 5, 10, 15, 20 where boundaries cross
# No two intervals overlap anymore - they're either disjoint or adjacentfrom intervaltree import Interval, IntervalTree
# Create tree with nested structure
tree = IntervalTree([
Interval(0, 100, "outer"),
Interval(10, 30, "inner1"),
Interval(40, 60, "inner2"),
Interval(15, 25, "nested_in_inner1"),
Interval(70, 90, "another_inner"),
Interval(200, 300, "separate")
])
# Find nesting relationships
nested_map = tree.find_nested()
print("Nesting relationships:")
for parent, children in nested_map.items():
print(f"Parent {parent}:")
for child in children:
print(f" -> {child}")
# Output shows:
# - "outer" contains "inner1", "inner2", "nested_in_inner1", "another_inner"
# - "inner1" contains "nested_in_inner1"
# - "separate" has no nested intervalsfrom intervaltree import Interval, IntervalTree
# Example: Processing time series data with gaps
data_tree = IntervalTree([
Interval(0, 100, "sensor_data_1"),
Interval(50, 150, "sensor_data_2"),
Interval(120, 200, "sensor_data_3"),
Interval(300, 400, "sensor_data_4")
])
# Step 1: Merge overlapping data segments
def combine_sensor_data(current, new):
return f"merged({current},{new})"
print("Original data segments:", len(data_tree))
data_tree.merge_overlaps(data_reducer=combine_sensor_data)
print("After merging overlaps:", len(data_tree))
# Step 2: Remove data during maintenance window [175, 225)
maintenance_start, maintenance_end = 175, 225
print(f"Removing data during maintenance [{maintenance_start}, {maintenance_end})")
data_tree.remove_overlap(maintenance_start, maintenance_end)
print("After maintenance removal:", len(data_tree))
# Step 3: Split remaining data at analysis boundary
analysis_point = 350
print(f"Splitting data at analysis point {analysis_point}")
data_tree.slice(analysis_point)
print("Final data segments:", len(data_tree))
for interval in sorted(data_tree):
print(f" Data segment: {interval}")Install with Tessl CLI
npx tessl i tessl/pypi-intervaltree