Appearance
4. Векторизованные операции и сортировки в pandas.Series
Векторизованная математика и проблема несовпадения индексов
Поэлементная математика «Series со скаляром» и «Series с Series»
Как и низкоуровневые массивы NumPy, структуры pandas.Series поддерживают полноценную векторизованную математику. Когда вы применяете стандартные арифметические операторы (+, -, *, /, **) к объекту Series и одиночному числу (скаляру), библиотека на C-скорости выполняет операцию над каждой ячейкой скрытого буфера данных, полностью избавляя код от тяжелых циклов Python.
При выполнении операций между двумя структурами Series математика по умолчанию происходит поэлементно (element-wise). Однако здесь вступает в силу важнейший архитектурный контракт Pandas — привязка к метаданным индексов.
Механизм автоматического выравнивания по индексам (Data Alignment)
Когда вы складываете, вычитаете или перемножаете два объекта pandas.Series, библиотека выполняет операцию не по физическому порядку элементов в массиве, а сопоставляя одинаковые индексные метки (index labels).
Если в обоих объектах находится идентичный набор индексов (пусть даже расположенных в разной последовательности), Pandas автоматически выровняет их по ключам, выполнит математическую операцию и вернет упорядоченный результат.
Почему несовпадение меток порождает маркеры NaN
Если индексная метка присутствует в первом объекте Series, но полностью отсутствует во втором, Pandas не сможет выполнить математическое действие из-за неопределенности. В соответствии с международным стандартом вещественной арифметики IEEE 754, вместо падения программы или выбрасывания исключений Python, библиотека запишет на позицию этого индекса специальный маркер пропуска — numpy.nan (Not a Number).
Любая последующая математическая операция с ячейкой, содержащей NaN, будет возвращать NaN, сигнализируя AI-инженеру о нарушении целостности данных при сборке фичей.
python
import pandas
# Simulating initial biases weights for specific feature indexes
first_weights = pandas.Series(data=[0.5, 1.2], index=["feature_A", "feature_B"])
second_weights = pandas.Series(data=[0.1, 0.3], index=["feature_B", "feature_C"])
# Adding Series with partially mismatched index tracks
combined_weights = first_weights + second_weights
print(combined_weights)
# Outputs:
# feature_A NaN (Mismatched - present only in first)
# feature_B 1.3 (Aligned and summed successfully)
# feature_C NaN (Mismatched - present only in second)
# dtype: float64🔬 Стандарт IEEE 754 и маркер pandas.NA: почему они разделены?
Нет, стандарт IEEE 754 не имеет никакого отношения к pandas.NA.
Международный стандарт IEEE 754 строго регулирует арифметику чисел с плавающей точкой (floating-point). В этом стандарте NaN (Not a Number) — это конкретное битовое состояние для типа данных float, зашитое на уровне процессора.
Именно поэтому NumPy и Pandas исторически используют numpy.nan (который технически является float64) при математических операциях: процессору так проще и быстрее всего считать.
🛠 Зачем тогда нужен pandas.NA?
Маркер pandas.NA был создан для решения проблем, которые стандарт IEEE 754 вообще не покрывает (работа со строками, целыми числами и логическими объектами).
- Проблема целых чисел (Integer): По стандарту IEEE 754,
NaNможет быть только уfloat. Если в Pandas у вас был столбец целых чисел[1, 2, 3]и в нем появлялся пропуск, Pandas был вынужден приводить весь столбец к типуfloat64([1.0, 2.0, NaN]). Маркерpd.NAпозволяет оставлять числа целыми. - Трехзначная логика (как в SQL):
pandas.NAработает по правилам логики Клейна:- По стандарту IEEE 754:
numpy.nan == numpy.nanвсегда возвращаетFalse. - В базах данных (SQL) и в
pd.NA:pd.NA == pd.NAвозвращает<NA>(потому что мы не можем точно сказать, равны ли две неизвестности).
- По стандарту IEEE 754:
📊 Сравнение поведения при несовпадении индексов
То, какой маркер вы получите при сложении двух Series с непересекающимися индексами, зависит исключительно от исходного типа данных (dtype):
| Исходный dtype | Что запишет Pandas? | Основание |
|---|---|---|
Standard NumPy (float64, int64) | numpy.nan | Стандарт IEEE 754 (быстрые вычисления на уровне процессора) |
Nullable Pandas (Float64, Int64, string) | pandas.NA | Внутренняя логика Pandas для работы с пропусками без потери типов |
Пример в коде с использованием Nullable-типов:
python
import pandas
# Используем специальный тип 'Float64' (с большой буквы)
s1 = pandas.Series([1.5], index=["a"], dtype="Float64")
s2 = pandas.Series([2.5], index=["b"], dtype="Float64")
# Из-за несовпадения индексов Pandas запишет именно pd.NA, игнорируя IEEE 754
print(s1 + s2)
# Результат:
# a <NA>
# b <NA>
# dtype: Float64Алгоритмы сортировки одномерных контейнеров
В процессах разведочного анализа данных (EDA) и подготовки признаков критически важно уметь быстро ранжировать и упорядочивать информацию. В pandas.Series встроены два мощных алгоритма сортировки, работающих на C-скорости:
Сортировка по значениям элементов через .sort_values()
Метод series.sort_values() упорядочивает массив на основе численных или алфавитных значений, хранящихся внутри скрытого C-буфера. По умолчанию сортировка всегда происходит по возрастанию (ascending=True). Если в массиве присутствуют маркеры аномалий NaN, Pandas по умолчанию смещает их в самый конец отсортированного списка.
Сортировка по индексным меткам через .sort_index()
Метод series.sort_index() восстанавливает или перестраивает порядок элементов на основе структуры самих индексов. Это незаменимо для упорядочивания временных рядов (где индексами выступают даты) или алфавитного выравнивания фичей перед экспортом в тензоры.
Параметры ascending и контроль мутации памяти inplace
Оба метода сортировки принимают два важнейших управляющих параметра:
ascending=False— переключает алгоритм в режим сортировки по убыванию (от большего к меньшему).inplace=True— принудительно выполняет сортировку на месте, модифицируя память прямо внутри исходного объекта без выделения независимого буфера. Это экономит RAM-память компьютера. По умолчаниюinplace=False, и метод возвращает отсортированную копию.
python
import pandas
raw_metrics = pandas.Series(data=[10, -5, 42], index=["gamma", "alpha", "beta"])
# Sorting values in descending order (returns a new copy)
sorted_by_values = raw_metrics.sort_values(ascending=False)
print(sorted_by_values)
# beta 42
# gamma 10
# alpha -5
# Sorting indices in ascending alphabetical order in-place
raw_metrics.sort_index(inplace=True)
print(raw_metrics)
# alpha -5
# beta 42
# gamma 10Упражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию)
1. Векторизованное масштабирование Series на скаляр
Задача: Создайте одномерный контейнер biases = pandas.Series(data=[0.1, -0.5, 0.4], index=["b1", "b2", "b3"]). Умножьте этот объект на скаляр 2.0 одной векторизованной операцией, а к результату прибавьте 0.05. Выведите итоговый Series.
Посмотреть решение
python
import pandas
biases = pandas.Series(data=[0.1, -0.5, 0.4], index=["b1", "b2", "b3"])
# Vectorized array-scalar math_oge operations combined
updated_biases = (biases * 2.0) + 0.05
print(updated_biases)2. Сложение Series с несовпадающими индексами
Задача: Даны два объекта: series_X = pandas.Series(data=[10, 20], index=["id_1", "id_2"]) и series_Y = pandas.Series(data=[5, 15], index=["id_2", "id_3"]). Сложите их между собой через оператор +. Проанализируйте вывод и посмотрите, в каких позициях сгенерировались маркеры NaN.
Посмотреть решение
python
import pandas
series_X = pandas.Series(data=[10, 20], index=["id_1", "id_2"])
series_Y = pandas.Series(data=[5, 15], index=["id_2", "id_3"])
# Automatic index data alignment execution
summed_output = series_X + series_Y
print(summed_output)
# id_1 NaN
# id_2 25.0
# id_3 NaN3. Ранжирование метрик потерь по убыванию
Задача: Дан массив значений функции потерь по эпохам: loss_series = pandas.Series(data=[0.45, 1.22, 0.08, 0.61], index=["e1", "e2", "e3", "e4"]). Отсортируйте этот массив по значениям элементов в порядке убывания (от худшего к лучшему), возрастания значения, убывания индекса и возрастания индекса с созданием новых копий. Выведите результаты.
Посмотреть решение
python
import pandas
loss_series = pandas.Series(data=[0.45, 1.22, 0.08, 0.61], index=["e1", "e2", "e3", "e4"])
# Sorting values via object method in descending order
print(loss_series.sort_values(ascending=False))
print(loss_series.sort_values(ascending=True))
print(loss_series.sort_index(ascending=False))
print(loss_series.sort_index(ascending=True))4. Алфавитное выравнивание индексов на месте
Задача: Создайте структуру признаков features_series = pandas.Series(data=[1.5, 9.2, -0.4], index=["z_axis", "a_axis", "m_axis"]). Выполните сортировку по индексным меткам на месте, полностью изменив исходный контейнер без лишнего выделения оперативной памяти (allocation). Выведите результат.
Посмотреть решение
python
import pandas
features_series = pandas.Series(data=[1.5, 9.2, -0.4], index=["z_axis", "a_axis", "m_axis"])
# Mutating memory structure in-place based on index criteria
features_series.sort_index(inplace=True)
print(features_series)5. Сортировка Series, содержащей аномалии NaN
Задача: Создайте объект pandas.Series([0.5, numpy.nan, -1.2, 3.4]). Отсортируйте его по значениям с помощью метода .sort_values() по возрастанию и и по убыванию, и убедитесь по выводу на экран, что маркер NaN автоматически сместился в самый конец структуры.
Посмотреть решение
python
import numpy
import pandas
anomaly_series = pandas.Series([0.5, numpy.nan, -1.2, 3.4])
# Sorting processes push missing values tokens to the end by default
sorted_anomalies = anomaly_series.sort_values()
print(sorted_anomalies)
sorted_anomalies = anomaly_series.sort_values(ascending=False)
print(sorted_anomalies)Упражнения смешанные (5 шт) (включают материал прошлых уроков)
6. Векторизованная математика над срезом Series по текстовым меткам
Задача: Создайте структуру весов weights_data = pandas.Series([1.0, 2.0, 3.0, 4.0], index=["w1", "w2", "w3", "w4"]) (Урок 2 модуля Pandas). Сделайте срез по явным строковым меткам от "w1" до "w3" включительно (Урок 3 модуля Pandas). Умножьте полученный срез на скалярный коэффициент 10.0 веторизованной операцией (Урок 4 модуля Pandas) и выведите результат.
Посмотреть решение
python
import pandas
weights_data = pandas.Series([1.0, 2.0, 3.0, 4.0], index=["w1", "w2", "w3", "w4"])
# Step 1: Slice text boundaries inclusive (Lesson 3)
target_slice = weights_data["w1":"w3"]
# Step 2: Vectorized scalar multiplication (Lesson 4)
scaled_output = target_slice * 10.0
print(scaled_output) # Contains items: w1 (10.0), w2 (20.0), w3 (30.0)7. Сортировка значений изолированной копии квантованного массива
Задача: Создайте Series из списка случайных чисел [42, -12, 0, 85, 3], принудительно выставив тип данных numpy.int8 (Урок 4 модуля NumPy / Урок 2 модуля Pandas). Чтобы защитить данные от неявных изменений, извлеките позиционный срез последних 3 элементов в виде независимой изолированной копии с помощью метода .copy() (Урок 3 модуля Pandas). Отсортируйте полученную копию по значениям элементов по возрастанию (Урок 4 модуля Pandas). Выведите результат.
Посмотреть решение
python
import numpy
import pandas
# Building quantized pandas container (NumPy Lesson 4 & Pandas Lesson 2)
quantized_series = pandas.Series(data=[42, -12, 0, 85, 3], dtype=numpy.int8)
# Extracting isolated memory slice allocation (Pandas Lesson 3)
isolated_segment = quantized_series[-3:].copy()
# Sorting elements values inside the safe copy (Pandas Lesson 4)
sorted_segment = isolated_segment.sort_values()
print(sorted_segment)
print("Dtype verification:", sorted_segment.dtype) # int88. Выравнивание данных при сложении Series и сгенерированной сетки NumPy
Задача: Сгенерируйте одномерную числовую последовательность из 3 элементов от 1.0 до 5.0 с помощью numpy.linspace(1.0, 5.0, 3) (Урок 3 модуля NumPy). Оберните её в объект pandas.Series, назначив явные строковые индексы ["a", "b", "c"] (Урок 2 модуля Pandas). Создайте второй объект pandas.Series([10.0, 20.0], index=["b", "c"]). Сложите эти два объекта и посмотрите, как Pandas автоматически выровнял массивы по буквенным ключам (Урок 4 модуля Pandas). Выведите результат.
Посмотреть решение
python
import numpy
import pandas
# Step 1: Linear grid generation (NumPy Lesson 3)
numpy_grid = numpy.linspace(1.0, 5.0, 3) # [1.0, 3.0, 5.0]
# Step 2: Packing array data into index-aware containers (Pandas Lesson 2)
series_first = pandas.Series(data=numpy_grid, index=["a", "b", "c"])
series_second = pandas.Series(data=[10.0, 20.0], index=["b", "c"])
# Step 3: Explicit keys data alignment math_oge (Pandas Lesson 4)
resulting_alignment = series_first + series_second
print(resulting_alignment)
# a NaN (No match for key 'a' in second series)
# b 13.0 (3.0 + 10.0 aligned successfully)
# c 25.0 (5.0 + 20.0 aligned successfully)9. Анализ характеристик и шагов памяти отсортированного буфера Series
Задача: Создайте неупорядоченный массив признаков raw_data = pandas.Series([45.5, -12.1, 0.8], index=["x", "y", "z"]). Отсортируйте его по значениям на месте (Урок 4 модуля Pandas). Выведите в консоль характеристики его внутреннего C-буфера: общее количество элементов через .size (Урок 2 модуля Pandas) и шаги в памяти .strides низкоуровневого массива .values (Урок 2 модуля NumPy).
Посмотреть решение
python
import pandas
raw_data = pandas.Series([45.5, -12.1, 0.8], index=["x", "y", "z"])
# In-place values sorting (Pandas Lesson 4)
raw_data.sort_values(inplace=True)
# Metadata infrastructure screening (NumPy Lesson 2 & Pandas Lesson 2)
print("Total size:", raw_data.size)
print("Underlying bytes strides:", raw_data.values.strides) # Outputs: (8,)10. Каст типов и расчет логарифмического штрафа над отсортированным Series
Задача: Инициализируйте массив вероятностей правильных ответов модели: probs = pandas.Series([0.99, 0.50, 0.12], index=["idx_3", "idx_1", "idx_2"]). Отсортируйте массив по индексным меткам на месте, чтобы восстановить исходную хронологию логов (Урок 4 модуля Pandas). Обратитесь к внутреннему числовому буферу .values и примените к нему векторизованную ufunc-функцию натурального логарифма -numpy.log() для расчета штрафа (Урок 8 модуля NumPy). Выведите итоговый массив штрафов.
Посмотреть решение
python
import numpy
import pandas
probs = pandas.Series([0.99, 0.50, 0.12], index=["idx_3", "idx_1", "idx_2"])
# Step 1: Align chronologically via index tokens sorting (Pandas Lesson 4)
probs.sort_index(inplace=True) # Order becomes: idx_1, idx_2, idx_3
# Step 2: Extracting C-buffer array and running vectorized math_oge ufunc (NumPy Lesson 8)
log_penalties = -numpy.log(probs.values)
print("Log penalties calculated vector:\n", log_penalties)