Все материалы
Продуктовая аналитикагайдстарт

Как загружать Excel, JSON и Parquet в pandas

Практический разбор источников в pandas: прочитать Excel и JSON, выбрать лист, сохранить типы и перейти с CSV на Parquet без сюрпризов.

КПКейсПрактика6 августа 2026 г.18 мин

Аналитические данные приходят не только в CSV. Финансовый отчёт лежит в Excel с несколькими листами, API возвращает вложенный JSON, а подготовленный слой хранится в Parquet. У каждого формата есть свои ловушки: заголовки, типы, merged cells, timezone и разная структура вложенности.

Коротко

Первые минуты после загрузки нужно потратить на проверку формы: названия колонок, типы, количество строк, лист или путь внутри JSON. Сохраняй типы и схему отдельно от кода чтения, чтобы изменение файла не прошло незаметно.

  • Excel — это книга с листами, а не одна таблица.
  • JSON может быть списком записей, словарём или вложенным ответом API.
  • Parquet хранит типы и колонки эффективнее CSV, но требует совместимого engine.
  • Не смешивай файлы разных периодов без проверки схемы.

Excel: выбрать лист и пропустить служебные строки

read_excel умеет читать конкретный лист, диапазон колонок и строку заголовка. Если файл оформлен для человека, первые строки могут содержать название отчёта, а настоящие колонки начинаются ниже. После чтения проверь, что order_id действительно стал колонкой, а не попал в первую строку данных.

pythonЗагрузить нужный лист Excel
orders = pd.read_excel(
    'input/monthly_report.xlsx',
    sheet_name='Orders',
    header=2,
    usecols=['order_id', 'created_at', 'channel', 'revenue'],
)

print(orders.columns.tolist())
print(orders.shape)

JSON: сначала понять структуру ответа

В JSON записи могут лежать внутри data, results или нескольких вложенных объектов. read_json удобен для плоского списка, а json_normalize — для ответа API, где поля находятся внутри словарей. Не разворачивай весь JSON без необходимости: оставь только поля, которые отвечают на вопрос.

pythonРазвернуть вложенные записи API
import json
from pandas import json_normalize

with open('input/response.json', encoding='utf-8') as file:
    payload = json.load(file)

events = json_normalize(payload['data'])
events = events.rename(columns={'user.id': 'user_id'})
events = events[['user_id', 'event_name', 'occurred_at']]

Parquet: типы и выбор колонок

Parquet удобен для очищенных аналитических слоёв. В отличие от CSV, он сохраняет типы, поэтому дата и категория не превращаются в строки при каждом чтении. Если нужен только один срез, читай нужные колонки и фильтруй период как можно раньше.

pythonПрочитать колонночный слой Parquet
orders = pd.read_parquet(
    'data/clean/orders.parquet',
    columns=['order_id', 'created_at', 'revenue'],
)

orders['created_at'] = pd.to_datetime(orders['created_at'], utc=True)
orders = orders.loc[orders['created_at'].dt.month.eq(8)]

Единый контракт для разных источников

Если один отчёт собирается из Excel сегодня и Parquet завтра, приведи оба источника к одинаковой схеме до объединения. Одинаковые названия колонок, типы и зерно важнее формата файла. Держи проверку колонок рядом с загрузкой, чтобы ошибка появилась до merge и groupby.

pythonПроверить схему после загрузки
EXPECTED = {'order_id', 'created_at', 'channel', 'revenue'}
missing = EXPECTED.difference(orders.columns)
if missing:
    raise ValueError(f'Missing columns: {sorted(missing)}')

orders['created_at'] = pd.to_datetime(orders['created_at'], errors='coerce', utc=True)
orders['revenue'] = pd.to_numeric(orders['revenue'], errors='coerce')

Формат файла не описывает смысл данных

Excel, JSON, CSV и Parquet отличаются способом хранения, но ни один формат не говорит автоматически, что означает одна строка. В Excel строка может быть итогом за день, в JSON — объектом пользователя с вложенным массивом заказов, а в Parquet — уже очищенным слоем событий. Перед чтением запиши зерно, ожидаемые ключи, период и правила обновления. Иначе технически успешная загрузка приведёт к неверной метрике.

Проверь дату выгрузки и диапазон наблюдений до объединения файлов. Один источник может быть в московском времени, другой — в UTC. Один файл может содержать полный месяц, а второй — только первые семь дней. После чтения сравни минимальную и максимальную дату, количество уникальных ключей и долю пропусков. Это дешёвая проверка, которая ловит больше проблем, чем попытка угадать правильный параметр read_*.

Хороший ingestion-слой возвращает не только DataFrame, но и метаданные: имя источника, время загрузки, число строк, хэш файла и список предупреждений. Для маленького ноутбука достаточно словаря рядом с результатом. Для регулярного pipeline эти поля стоит записывать в журнал запуска.

Что зафиксировать до чтения источника
ВопросПример ответа
Зерноодна строка — один заказ
Ключorder_id уникален в итоговом слое
Периодcreated_at в UTC, только полный месяц
Обязательные поляorder_id, user_id, revenue, created_at
Обработка ошибокнепарсящиеся даты уходят в quarantine

Excel: служебные строки и несколько листов

Excel часто выглядит как таблица, но фактически является презентацией отчёта. Вверху могут быть название отдела, дата формирования и объединённые ячейки, а заголовок начинается с пятой строки. На одном листе лежит итог, на другом — детализация. Поэтому сначала прочитай несколько строк без предположения о заголовке и посмотри названия листов.

Параметр usecols ограничивает вход, но не исправляет смещённый header. После чтения сразу нормализуй имена колонок: убери пробелы, приведи регистр и проверь обязательный набор. Если источник меняется вручную каждую неделю, сохрани пример плохого файла в тестовой папке. Такой regression-тест защитит от того, что менеджер добавил одну строку перед заголовком.

Формулы Excel могут быть сохранены как вычисленные значения или прочитаны иначе в зависимости от настроек и библиотеки. Для финансового отчёта сравни несколько контрольных ячеек с тем, что видит владелец файла. Не считай Excel источником истины только потому, что его удобно открыть.

pythonОсмотреть книгу до нормализации
book = pd.ExcelFile('data/report.xlsx')
print(book.sheet_names)

raw = pd.read_excel(
    book, sheet_name='Orders', header=None, nrows=8
)
print(raw)

orders = pd.read_excel(
    book, sheet_name='Orders', skiprows=4, usecols='A:F'
)
orders.columns = orders.columns.astype('string').str.strip().str.lower()

JSON: сначала распрями структуру, потом считай

JSON бывает простым списком объектов, ответом API с полем data или деревом с вложенными items. pd.read_json не всегда даёт таблицу нужного зерна. Вначале посмотри верхний уровень объекта и реши, какая сущность должна стать строкой. Если один пользователь содержит массив событий, после json_normalize можно получить строку на событие, но нужно сохранить связь с user_id.

Следи за пагинацией и дубликатами. API может отдавать одну страницу по 100 записей, а последняя страница — повторять часть предыдущей при нестабильной сортировке. Собирай страницы в список сырых объектов, проверяй курсор и только потом объединяй. Успешный HTTP-ответ ещё не означает полный набор данных.

Типы в JSON тоже плавают: сегодня revenue — число, завтра API прислал "1 200", а в третьем ответе — null. Приведи число явно, сохрани количество ошибок и не превращай непонятную строку в ноль. Ноль — это наблюдение, не синоним ошибки загрузки.

pythonРасплющить вложенные события и проверить ключи
payload = response.json()
rows = pd.json_normalize(
    payload['data'],
    record_path='events',
    meta=['user_id', 'created_at'],
)
rows['event_id'] = rows['event_id'].astype('string')
assert rows['event_id'].notna().all()
assert rows['user_id'].notna().all()

Parquet и изменение схемы

Parquet сохраняет типы и позволяет читать только нужные колонки, но схема всё равно может меняться между партициями. В одном файле user_id оказался integer, в другом — string; новый источник добавил device_type, а старый его не знает. Перед concat приведи ключи к общему типу и явно определи, какие колонки обязательны, а какие могут отсутствовать.

Выбор колонок при чтении снижает I/O, но не должен скрывать audit-поля. Если отчёту нужен только revenue, всё равно сохрани отдельно период, имя файла и version источника. При объединении партиций проверяй, что новые даты действительно появились, а не были отфильтрованы из-за несовместимой схемы.

Разделяй raw и clean Parquet. Raw слой должен позволять восстановить исходный ответ, clean — иметь нормализованные типы, названия и контрольные проверки. Перезаписывать единственный файл после очистки удобно в начале, но быстро лишает возможности разобраться, почему число изменилось.

Parquet — это формат хранения, не гарантия качества

Колонночный файл может быть быстрым, хорошо сжатым и при этом содержать неправильное зерно, дубли или неполный период. Проверки схемы и контрольные числа нужны после каждой загрузки.

Собери загрузку в одну повторяемую функцию

Когда источник используется больше одного раза, вынеси чтение и нормализацию из основной аналитической ячейки. Функция должна принимать путь и возвращать DataFrame с известным контрактом. Внутри можно выбрать reader по расширению, но результат обязан быть одинаковым: названия колонок, типы, часовой пояс и правила ошибок.

Такой слой делает анализ переносимым. Сегодня ты читаешь Excel от операционного менеджера, завтра — Parquet из DWH, а расчёт выручки не меняется. Если контракт нарушен, pipeline останавливается до графика. Это лучше, чем получить пустую линию и искать причину в визуализации.

Практическая проверка проста: положи рядом один нормальный и один намеренно испорченный файл. Первый должен пройти до отчёта, второй — остановиться с сообщением о конкретной колонке, типе или периоде. Это маленькая инвестиция, которая окупается при первой же смене шаблона.

  • Осмотреть структуру до выбора параметров чтения.
  • Нормализовать колонки и типы в одном месте.
  • Проверить даты, ключи, дубли и объём источника.
  • Разделить raw и clean слои.
  • Считать метрики только после прохождения контракта.
Продолжить чтение
Вся библиотека