Скидка на подшипники из наличия!
Новое поступление товара в 2026 году!
Pandas для обработки данных измерений — рабочий инструмент инженера, когда протоколы испытаний, логи стенда или выгрузки с датчиков нужно загрузить, отфильтровать, посчитать по каналам и вернуть результат в понятном виде. Ниже — практический разбор на актуальной версии библиотеки: чтение CSV и Excel, отсев брака и грубых промахов, группировка по каналам через groupby, статистика (среднее, СКО) и выгрузка обратно в CSV и Excel. Примеры кода написаны под pandas 3.0 — с учётом изменившегося поведения копирования данных и строк частот.
Актуальная линейка — pandas 3.0, вышедшая в начале 2026 года. Это крупный релиз с несколькими изменениями поведения, важными для обработки измерений (о них ниже). Для чтения и записи Excel нужен пакет openpyxl; для ускорения работы со строковыми столбцами рекомендуется поставить pyarrow.
pip install pandas openpyxl pyarrow import pandas as pd import numpy as np print(pd.__version__) # 3.0.x
Три изменения pandas 3.0, которые стоит держать в голове:
.loc
df[маска][столбец] = ...
str
object
's'
'min'
'h'
'S'
'T'
'H'
Данные измерений чаще всего приходят в CSV (выгрузка с регистратора) или Excel (сводный протокол). Обе задачи закрываются функциями pd.read_csv и pd.read_excel. Ключевой нюанс отечественных выгрузок — разделитель столбцов «точка с запятой» и десятичная запятая: их задают явно.
pd.read_csv
pd.read_excel
df = pd.read_csv( "stand_log.csv", sep=";", # разделитель столбцов decimal=",", # десятичная запятая parse_dates=["time"], # столбец времени -> datetime dayfirst=True, # день.месяц.год na_values=["", "---", "OVER"], # маркеры пропусков encoding="utf-8", )
Из Excel читают конкретный лист и, при необходимости, диапазон столбцов и строк (когда сверху шапка предприятия, а не заголовки таблицы):
df = pd.read_excel( "protocol.xlsx", sheet_name="Измерения", # имя или индекс листа usecols="A:E", # нужные столбцы skiprows=3, # пропустить верхнюю шапку )
sep
sep=";"
decimal
decimal=","
parse_dates
parse_dates=["time"]
usecols
usecols="A:E"
skiprows
skiprows=3
na_values
na_values=["OVER"]
sheet_name
sheet_name="Измерения"
Перед расчётами данные осматривают: сколько строк, какие типы столбцов, есть ли пропуски. Три команды дают полную картину.
df.head() # первые строки df.info() # столбцы, типы, число непустых значений df.describe() # быстрая сводка по числовым столбцам
Если столбец времени прочитался как текст, его приводят к datetime; числовой столбец с «мусором» — к числу, а нераспознанное станет пропуском (NaN).
NaN
df["time"] = pd.to_datetime(df["time"], dayfirst=True) df["value"] = pd.to_numeric(df["value"], errors="coerce") df = df.dropna(subset=["value"]) # убрать строки без значения df["value"] = df["value"].interpolate() # или заполнить интерполяцией
В pandas 3.0 действует Copy-on-Write: правку делают одним действием через .loc. Запись вида df[df["value"] < 0]["value"] = 0 исходный кадр не изменит. Правильно: df.loc[df["value"] < 0, "value"] = 0.
df[df["value"] < 0]["value"] = 0
df.loc[df["value"] < 0, "value"] = 0
Фильтрация — отбор строк по условию. Базовый приём — булева маска; несколько условий объединяют операторами & (и) и | (или), каждое условие в скобках.
&
|
# значения одного канала в рабочем диапазоне температур sel = df[(df["channel"] == "T1") & (df["value"].between(20, 80))] # то же через query — читается как обычное условие sel = df.query("channel == 'T1' and 20 <= value <= 80")
Частая инженерная задача — отсев грубых промахов (выбросов). Простой критерий «трёх сигм» отбрасывает точки, отстоящие от среднего более чем на три стандартных отклонения:
m = df["value"].mean() s = df["value"].std() # выборочное СКО (ddof=1) mask = (df["value"] - m).abs() <= 3 * s clean = df[mask]
df["outlier"] = False
df.loc[(df["value"] - m).abs() > 3 * s, "outlier"] = True
Когда в одной таблице собраны несколько каналов или режимов, их разбивают методом groupby и считают показатели по каждой группе. Это ядро обработки: «посчитать статистику отдельно по каждому датчику».
groupby
# несколько показателей сразу по каждому каналу by_ch = df.groupby("channel")["value"].agg(["count", "mean", "std", "min", "max"])
Чтобы задать понятные имена итоговым столбцам, применяют именованное агрегирование: имя результата — слева, пара «столбец, функция» — справа.
stats = df.groupby("channel").agg( n=("value", "count"), mean_v=("value", "mean"), std_v=("value", "std"), min_v=("value", "min"), max_v=("value", "max"), )
Группировать можно и по нескольким ключам сразу — например, по каналу и режиму испытания: df.groupby(["channel", "mode"]).
df.groupby(["channel", "mode"])
Базовые статистики считаются одноимёнными методами. Важный для метрологии момент: метод std по умолчанию вычисляет выборочное среднее квадратическое отклонение (с делителем n−1, параметр ddof=1) — именно ту оценку СКО, что применяется при обработке результатов многократных измерений по ГОСТ Р 8.736-2011.
std
ddof=1
mean()
std()
var()
median()
min()
max()
quantile(q)
quantile(0.95)
count()
describe()
mean_v = df["value"].mean() std_v = df["value"].std() # выборочное СКО, ddof=1 q95 = df["value"].quantile(0.95) # полная сводка по каждому каналу summary = df.groupby("channel")["value"].describe()
Один вызов describe() по группам даёт готовую таблицу «объём выборки — среднее — СКО — минимум — квартили — максимум» для каждого канала.
Данные с датчиков — это временной ряд. Чтобы работали операции по времени, столбец времени делают индексом. Тогда resample усредняет по интервалам (прореживает частый сигнал), а rolling строит скользящее среднее для сглаживания шума.
resample
rolling
ts = df.set_index("time") # посекундные средние по каналу (строка частоты в нижнем регистре!) per_sec = ts["value"].resample("1s").mean() # минутные средние per_min = ts["value"].resample("1min").mean() # скользящее среднее по окну из 10 отсчётов ts["smooth"] = ts["value"].rolling(window=10, center=True).mean()
В pandas 3.0 строки частот пишут в нижнем регистре: "s" — секунды, "min" — минуты, "h" — часы. Прежние обозначения "S", "T", "H" устарели и в актуальной версии не используются.
"s"
"min"
"h"
"S"
"T"
"H"
Итог возвращают в привычном формате. CSV с русской локалью — точка с запятой и десятичная запятая; кодировка utf-8-sig нужна, чтобы Excel корректно открыл кириллицу.
utf-8-sig
clean.to_csv( "result.csv", index=False, sep=";", decimal=",", encoding="utf-8-sig", ) # один лист Excel stats.to_excel("stats.xlsx")
Часто удобнее собрать многостраничный отчёт: на одном листе — очищенные данные, на другом — статистика по каналам. Это делается через ExcelWriter.
ExcelWriter
with pd.ExcelWriter("report.xlsx") as writer: clean.to_excel(writer, sheet_name="Данные", index=False) stats.to_excel(writer, sheet_name="Статистика")
read_csv
groupby("channel")
to_excel
Через параметры sep=";" и decimal="," в pd.read_csv. Тогда «точка с запятой» распознаётся как разделитель столбцов, а запятая — как десятичный знак, и числа читаются как числа, а не как текст.
Фильтрация оставляет строки по условию (один срез данных). groupby разбивает таблицу на группы по значению ключа (например, по каналу) и считает показатели отдельно для каждой группы, возвращая сводную таблицу «канал — статистика».
По умолчанию выборочное, с делителем n−1 (ddof=1) — как принято при обработке результатов многократных измерений. Для оценки по всей генеральной совокупности указывают std(ddof=0).
std(ddof=0)
Простейший приём — правило трёх сигм: оставляют точки, отстоящие от среднего не более чем на 3 стандартных отклонения. Для отбраковки грубых промахов существуют и формальные критерии; методология обработки прямых многократных измерений описана в ГОСТ Р 8.736-2011.
В pandas 3.0 обозначения частот записывают в нижнем регистре: "s", "min", "h". Старые "S", "T", "H" больше не применяются. Кроме того, для resample нужен индекс типа даты/времени — столбец времени предварительно делают индексом через set_index.
set_index
Для файлов .xlsx устанавливают пакет openpyxl (pip install openpyxl). После этого работают pd.read_excel и DataFrame.to_excel, включая сборку многостраничных отчётов через ExcelWriter.
.xlsx
pip install openpyxl
DataFrame.to_excel
С pandas 3.0 действует Copy-on-Write: срез ведёт себя как копия. Изменять данные нужно одним действием через .loc, например df.loc[df["value"] < 0, "value"] = 0, а не через цепочку индексаций.
Вы можете задать любой вопрос на тему нашей продукции или работы нашего сайта.