Одни и те же данные можно сложить в таблицу двумя способами. Первый удобен человеку: всё сгруппировано, есть итоги, глазу не на чем споткнуться. Второй выглядит скучно и избыточно — зато его без подготовки понимают скрипт, база данных и языковая модель. Разбираемся, почему для ИИ выигрывает «скучный» вариант.
Представим справочник норм времени на операции: работы разбиты на группы и подгруппы, у каждой операции есть единица измерения и норма в минутах. Такой файл есть почти в любой производственной или сервисной компании. Его сделали люди для людей — и обычно он вложенный.
Два файла с одинаковыми данными
Вот как выглядит типичный «человеческий» вариант. Название группы — отдельная строка поверх операций, подгруппа записана в объединённой ячейке, внизу каждой группы стоит строка «Итого».
| Подгруппа | Операция | Ед. | Норма, мин |
|---|---|---|---|
| Группа: Монтаж оборудования | |||
| Подготовка | Разметка места установки | шт. | 15 |
| Сверление отверстий | шт. | 4 | |
| Установка | Крепление кронштейна | шт. | 12 |
| Подключение питания | шт. | 20 | |
| Итого по группе | 51 | ||
| Группа: Пусконаладка | |||
| Проверка | Тестовый прогон | цикл | 30 |
| Итого по группе | 30 | ||
А вот те же сведения в плоском виде. Каждая строка — одна операция, и всё, что к ней относится, записано прямо в этой строке.
| Группа | Подгруппа | Операция | Ед. | Норма, мин |
|---|---|---|---|---|
| Монтаж оборудования | Подготовка | Разметка места установки | шт. | 15 |
| Монтаж оборудования | Подготовка | Сверление отверстий | шт. | 4 |
| Монтаж оборудования | Установка | Крепление кронштейна | шт. | 12 |
| Монтаж оборудования | Установка | Подключение питания | шт. | 20 |
| Пусконаладка | Проверка | Тестовый прогон | цикл | 30 |
Человеку первая таблица кажется аккуратнее, вторая — многословной: «Монтаж оборудования» повторяется четыре раза. Но посмотрим на них глазами программы.
Что мешает машине во вложенном файле
- Объединённые ячейки. Значение «Подготовка» хранится только в первой ячейке диапазона, остальные — пустые. Скрипт видит у строки «Сверление отверстий» пустую подгруппу.
- Строки-заголовки внутри данных. «Группа: Монтаж оборудования» — это не операция, но читается как строка таблицы. Программе нужно догадаться, что это заголовок, и протянуть его вниз.
- Итоги среди фактов. Строка «Итого по группе» выглядит как ещё одна запись. Сложите столбец — и получите двойной счёт.
- Многоуровневые шапки. Когда название столбца собрано из двух строк («Норма» над «мин» и «ч»), у столбца нет одного имени.
- Смысл в оформлении. Жирный шрифт, отступы и цвет говорят человеку «это группа», а для машины их просто нет.
Вложенная таблица — это отчёт. Плоская таблица — это данные.
Почему плоский формат выигрывает
Плоский файл избыточен, но у этой избыточности есть смысл: каждая строка самодостаточна. Её можно вырезать, отфильтровать, отсортировать — и она не потеряет контекст.
- Явная иерархия. Группа и подгруппа — обычные столбцы, а не позиция строки на листе.
- Табличная структура. Файл один к одному ложится в реляционную таблицу,
DataFrameв pandas, модель данных в BI. - Понятные типы. Названия и группы — текст, нормы — числа. В одном столбце нет «15» и «Итого» одновременно.
- Агрегация без подготовки. Сумма по группе, средняя норма по подгруппе, сводная таблица — в одну операцию.
- Переносимость. Плоская таблица без потерь сохраняется в CSV, отдаётся через API, загружается в любую систему.
| Критерий | Вложенный | Плоский |
|---|---|---|
| Читать глазами | Удобно | Терпимо |
| Объём файла | Меньше | Больше за счёт повторов |
| Фильтры и сортировка | Ломаются | Работают сразу |
| Загрузка в SQL, pandas, BI | Нужна ручная подготовка | Без подготовки |
| Суммы и сводные | Риск двойного счёта | Корректно |
| Разбор языковой моделью | С ошибками и догадками | Однозначно |
Как это выглядит в коде
Плоский файл читается и считается в две строки:
import pandas as pd
df = pd.read_excel("normy_flat.xlsx")
df.groupby("Группа")["Норма, мин"].sum()
Для вложенного сначала придётся «расплющить» его вручную — и надеяться, что структура не поменяется в следующей версии файла:
raw = pd.read_excel("normy_nested.xlsx", header=0)
# 1. вытащить группу из строк-заголовков и протянуть вниз
raw["Группа"] = raw["Подгруппа"].where(raw["Подгруппа"].str.startswith("Группа:", na=False)).ffill()
# 2. заполнить пустоты объединённых ячеек
raw["Подгруппа"] = raw["Подгруппа"].ffill()
# 3. выбросить заголовки и итоги, которые притворяются данными
df = raw[raw["Операция"].notna() & ~raw["Подгруппа"].str.startswith(("Группа:", "Итого"))]
С языковой моделью то же самое, только без кода: плоскую таблицу она пересказывает и считает точно, а во вложенной вынуждена угадывать, к какой группе относится строка и что делать с «Итого». Угадывает она обычно уверенно — и не всегда правильно.
А как же JSON и «вложенность»?
Важно не путать два разных явления. Вложенность в Excel — визуальная: структура держится на положении строк и объединении ячеек. Вложенность в JSON — явная: у каждого уровня есть имя и границы.
{
"группа": "Монтаж оборудования",
"подгруппы": [
{ "название": "Подготовка",
"операции": [
{ "название": "Разметка места установки", "ед": "шт.", "норма_мин": 15 }
] }
]
}
Такой JSON машина разбирает без догадок, и для документов, API и настроек он хорош. Но для аналитики — фильтров, сумм, сводных — его всё равно разворачивают в плоскую таблицу. Поэтому правило простое: табличные факты храним плоско, объекты со сложной внутренней структурой — в JSON.
GR-01 вместо «Монтаж оборудования» в каждой строке.Правило: храним плоско, показываем красиво
Отказываться от удобных для людей отчётов не нужно. Нужно перестать хранить данные в форме отчёта.
- Источник — плоская таблица. Одна строка — один факт, один столбец — один признак, никаких итогов и объединений.
- Отчёт — производная. Группировки, промежуточные итоги и оформление строятся сводной таблицей, BI-панелью или скриптом поверх источника.
- Правки — только в источнике. Тогда отчёт пересобирается автоматически, а ИИ, BI и люди видят одни и те же цифры.