Представьте журнал, в котором три записи, каждая по часу. В сумме — 180 минут. Но на временной шкале они умещаются всего в два часа. Ошибка возникает, когда сумму длительностей строк выдают за время, в течение которого велось хотя бы одно наблюдение. Это разные показатели, и прежде всего нужно определить, какой из них требуется в задаче.
Рассмотрим вымышленный объект O01 и один критерий наблюдения на общей временной оси. В наборе намеренно есть перекрытия: C перекрывает части A и B, а A и B только касаются. Числа не взяты из реального хронометража; даты, часовые пояса и переходы времени не моделируются. Не стоит объединять по этому принципу интервалы разных людей или признаков только ради того, чтобы уменьшить сумму.
Все исходные интервалы
| Запись | Время и длина |
|---|---|
A | 09:00–10:00; [0, 60) минут от 09:00; 60 минут. |
B | 10:00–11:00; [60, 120); 60 минут. |
C | 09:30–10:30; [30, 90); 60 минут. |

Авторское соглашение - [начало, конец): начало включено, конец не включён. В документации pandas.Interval.overlaps поясняется, что пересечение зависит в том числе от включения границ. Ниже это соглашение задано явно; оно не объявлено настройкой по умолчанию для всех программ. Код примера использует только обычный Python, а не интерфейс pandas.
Касание не равно перекрытию
| Пара | Общая часть по нашему правилу |
|---|---|
A и B | Общих точек нет: A заканчивается перед включённым моментом 10:00, с которого начинается B. |
A и C | 09:30–10:00; 30 минут. |
B и C | 10:00–10:30; 30 минут. |
Для наших полуоткрытых интервалов пересечение есть, если максимум начал меньше минимума концов. Равенство границ эту проверку не проходит. Не заменяйте «<» на «≤», когда задача — найти именно пересечение в формате [начало, конец). При иной договорённости о включении концов условие нужно пересмотреть.
Сумма строк и покрытая длительность
Сумма длин исходных записей: 60 + 60 + 60 = 180 минут. Объединение интервалов покрывает [09:00, 11:00), то есть 120 минут. Разница 180 − 120 = 60 минут — это время, которое при построчном суммировании было учтено повторно. Она не означает, что запись C ошибочна или её нужно удалять.
Если нужен объём зафиксированной работы с учётом одновременно шедших процессов, сумма 180 минут может быть содержательно уместна. Если же требуется показатель «хотя бы одна запись по объекту O01 и одному критерию активна», считают покрытие — 120 минут. В реальном проекте название показателя и правило группировки фиксируют до расчёта. Исходные строки при получении покрытия сохраняются.
Полный код объединения интервалов
intervals = [(0, 60), (60, 120), (30, 90)]
def overlap(a, b):
return max(a[0], b[0]) < min(a[1], b[1])
def merge_intervals(rows):
merged = []
for start, end in sorted(rows):
if start >= end:
raise ValueError("Expected start < end")
if not merged or start > merged[-1][1]:
merged.append([start, end])
else:
merged[-1][1] = max(merged[-1][1], end)
return merged
merged = merge_intervals(intervals)
print("A/B, A/C, B/C:", overlap(intervals[0], intervals[1]),
overlap(intervals[0], intervals[2]), overlap(intervals[1], intervals[2]))
print("sum_rows =", sum(end - start for start, end in intervals))
print("merged =", merged)
print("covered =", sum(end - start for start, end in merged))
assert merged == [[0, 120]]
assert not overlap((0, 60), (60, 120))
assert merge_intervals([(0, 120), (30, 60), (45, 90)]) == [[0, 120]]
Вывод для показанных минут от 09:00:
A/B, A/C, B/C: False True True
sum_rows = 180
merged = [[0, 120]]
covered = 120
После сортировки интервалы A и C объединяются, следом добавляется B — в итоге получается один участок [0, 120). Алгоритм объединения разрешает присоединить соседний участок, начинающийся ровно в конце предыдущего. Это не утверждение о том, что A и B пересекаются: их объединение непрерывно, хотя общих точек у двух полуоткрытых интервалов нет. Функция overlap и операция объединения решают разные вопросы.
Не пытайтесь в общем случае получить покрытие вычитанием всех попарных пересечений: при тройном перекрытии придётся учитывать общую часть сразу нескольких интервалов. Показанный проход по отсортированным интервалам не складывает такие поправки. Он проверен также на вложенном учебном наборе, но это не замена проверки структуры реального журнала.
Проверка перед расчётом
- Один объект, один критерий и общая временная шкала заданы явно.
- Для каждой строки начало строго меньше конца; неоднозначные записи не исправляют автоматической перестановкой.
- Записано соглашение о включении концов и проверен случай касания.
- Отдельно названы 180 минут суммы строк и 120 минут покрытия.
- Интервалы разных объектов не объединяли между собой.
- Пересечение не объявляли доказательством технического дубликата или ошибки наблюдения.
Если задача состоит в распределении отдельных числовых значений по группам, это другая операция: пример границ интервалов гистограммы считает частоты точек, а не длительность объединения временных участков. Совпадение обозначений скобок не делает эти расчёты взаимозаменяемыми.
Если для собственного исследовательского набора нужна помощь с правилами подготовки и проверкой расчёта, можно обсудить подготовку и проверку статистического анализа. Заранее согласуйте вопрос, состав исходных файлов и объём помощи. Автоматическое преобразование строк само по себе не подтверждает достоверность данных.