Appearance
3. Индексирование и слайсинг одномерных pandas.Series
Позиционный и явный доступ к элементам
Двойственная природа адресации в Series
Поскольку объект pandas.Series обладает как скрытым порядковым номером, так и явными индексными метками, вы можете извлекать данные из него двумя принципиально разными путями:
- Позиционный (числовой) доступ: Обращение по порядковому номеру элемента в памяти, начиная с
0(как в стандартных списках Python или массивах NumPy). - Явный (метковый) доступ: Обращение по назначенному текстовому или числовому имени (метке) индекса.
python
import pandas
# Creating a Series tracking weights metrics with explicit custom labels
weights_series = pandas.Series(data=[0.15, -0.42, 0.89], index=["w_first", "w_second", "w_third"])
# Reading elements via two alternative paradigms
value_by_position = weights_series.iloc[0] # Positional index lookup
value_by_explicit_label = weights_series["w_first"] # Explicit label index lookup
print(value_by_position) # Outputs: 0.15
print(value_by_explicit_label) # Outputs: 0.15Скрытые уязвимости при совпадении числовых меток
ВАЖНО ДЛЯ AI-ИНЖЕНЕРА
Если в качестве явных индексов вашего массива pandas.Series используются целые числа, которые не начинаются с нуля (например, реальные ID пользователей [101, 102, 103]), прямой вызов series[0] может привести к критической ошибке KeyError. В таких ситуациях Pandas путается: искать элемент с меткой 0 или брать элемент на нулевой позиции. Чтобы исключить двусмысленность в промышленном коде, для строгого меткового доступа всегда используются операторы .loc[] и .iloc[], которые вы подробно разберете далее в курсе.
Срезы в Pandas и управление памятью
Главное отличие Pandas от NumPy: включение правой границы
При создании расширенных срезов вида series[start:stop:step] поведение Pandas кардинально зависит от типа используемых индексов:
- При срезе по позиционным числам: Нарезка работает по стандартным правилам Python — правая граница
stopникогда не включается в результат. Вызовseries[0:2]вернет элементы с индексами0и1. - При срезе по явным текстовым меткам: Нарезка работает по правилам математических отрезков — правая граница
stopвсегда включается в итоговый результат. Вызовseries["w_first":"w_second"]вернет оба элемента, включая элемент"w_second". Это сделано потому, что при алфавитно-текстовом упорядочивании невозможно интуитивно определить «элемент, предшествующий данной строке».
python
import pandas
scores_series = pandas.Series(data=[90, 85, 77, 95], index=["alpha", "beta", "gamma", "delta"])
# Slicing via positional integers (standard Python behavior: excludes stop)
positional_slice = scores_series[0:2]
print("Positional slice elements count:", positional_slice.size) # Outputs: 2 (indices 0, 1)
# Slicing via explicit index labels (Pandas behavior: includes stop)
label_slice = scores_series["alpha":"gamma"]
print("Label slice elements count:", label_slice.size) # Outputs: 3 (alpha, beta, gamma)
# Reversing the whole vector via a negative step slice
reversed_scores = scores_series[::-1]
print(reversed_scores) # Outputs elements from index "delta" down to "alpha"Анатомия управления памятью: Views против Copies в Pandas
Как и в NumPy, стандартный срез в pandas унаследовал механизм представлений (Views). Когда вы делаете срез sub_series = series[0:2], Pandas не выделяет физическую оперативную память под новые элементы, а возвращает объект, ссылающийся на исходный буфер данных на C-уровне.
Любое изменение элементов внутри полученного среза неявно мутирует исходный базовый массив. Если вам требуется полная изоляция данных (например, отделение валидационной выборки от тренировочной), необходимо принудительно использовать метод глубокого копирования .copy().
python
import pandas
signals = pandas.Series(data=[1.0, 2.0, 3.0], index=["s1", "s2", "s3"])
# WRONG: Slicing without isolation creates a view side-effect risk
signals_view = signals["s1":"s2"]
signals_view["s1"] = 99.0
print(signals["s1"]) # Outputs modified original buffer: 99.0
# CORRECT: Creating an isolated independent block allocation
signals_copy = signals["s2":"s3"].copy()Булево маскирование одномерного контейнера
Отбор элементов Series по логическим условиям
Кроме срезов, элементы pandas.Series можно отбирать по произвольным логическим условиям. Любая операция сравнения (>, <, ==...) применительно к Series возвращает булеву маску — одномерный контейнер с флагами True/False той же длины. Передавая эту маску обратно в квадратные скобки исходного объекта, вы извлекаете только те элементы, для которых условие истинно. В условиях с несколькими признаками стандартные and/or заменяются побитовыми операторами & (И) и | (ИЛИ) в обязательных круглых скобках.
python
import pandas
# Building a time-series container with explicit daily date index labels
date_labels = pandas.date_range(start="20190101", periods=5)
sensor_readings = pandas.Series(data=[0.12, 0.85, 0.44, 0.91, 0.03], index=date_labels)
# Step 1: Any comparison operator produces a boolean mask Series
high_signal_mask = sensor_readings > 0.5
print(high_signal_mask)
# Outputs:
# 2019-01-01 False
# 2019-01-02 True
# 2019-01-03 False
# 2019-01-04 True
# 2019-01-05 False
# Freq: D, dtype: bool
# Step 2: Feeding the mask back into the container filters rows on C-velocity
high_signal_batch = sensor_readings[sensor_readings > 0.5]
print(high_signal_batch)
# Outputs:
# 2019-01-02 0.85
# 2019-01-04 0.91
# Freq: D, dtype: float64
# Step 3: Bitwise union (OR) of two independent threshold conditions
extreme_batch = sensor_readings[(sensor_readings > 0.8) | (sensor_readings < 0.1)]
print(extreme_batch)
# Outputs:
# 2019-01-02 0.85
# 2019-01-04 0.91
# 2019-01-05 0.03
# Freq: D, dtype: float64
# Step 4: Bitwise intersection (AND) may yield an empty Series
# when no value can satisfy both contradictory bounds
impossible_batch = sensor_readings[(sensor_readings > 0.8) & (sensor_readings < 0.1)]
print(impossible_batch) # Outputs an empty one-dimensional containerУпражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию)
1. Извлечение метрики по текстовому ключу
Задача: Дан одномерный контейнер loss_history = pandas.Series(data=[0.65, 0.42, 0.18], index=["epoch_1", "epoch_2", "epoch_3"]). Напишите строку кода, извлекающую значение потерь на второй эпохе с помощью явного обращения по строковому индексу. Выведите значение.
Посмотреть решение
python
import pandas
loss_history = pandas.Series(data=[0.65, 0.42, 0.18], index=["epoch_1", "epoch_2", "epoch_3"])
# Accessing scalar records via explicit string index label
epoch_two_loss = loss_history["epoch_2"]
print(epoch_two_loss) # Outputs: 0.422. Срез по текстовым меткам с фиксацией границ
Задача: На основе массива loss_history из предыдущего упражнения сделайте срез от "epoch_1" до "epoch_2" включительно, используя явные строковые метки. Выведите полученную структуру и проверьте её размер через .size.
Посмотреть решение
python
import pandas
loss_history = pandas.Series(data=[0.65, 0.42, 0.18], index=["epoch_1", "epoch_2", "epoch_3"])
# Slicing using explicit string keys includes the stop element boundary
sub_history = loss_history["epoch_1":"epoch_2"]
print(sub_history)
print("Total slice elements count:", sub_history.size) # Outputs: 23. Позиционный срез по правилам Python
Задача: Дан одномерный контейнер metrics = pandas.Series(data=[10, 20, 30, 40], index=["a", "b", "c", "d"]). Сделайте срез первых трех элементов по их числовым позициям [0:3]. Выведите полученный результат и убедитесь, что элемент с индексом 3 ("d") был исключен.
Посмотреть решение
python
import pandas
metrics = pandas.Series(data=[10, 20, 30, 40], index=["a", "b", "c", "d"])
# Slicing using integers follows standard exclusive python bounds
top_metrics = metrics[0:3]
print(top_metrics) # Outputs elements for indices: a, b, c4. Выявление скрытых побочных эффектов мутации View
Задача: Создайте Series признаков features = pandas.Series([1.1, 2.2, 3.3], index=["f1", "w2", "f3"]). Возьмите срез features_view = features[:2]. Измените первый элемент в features_view на значение 0.0. Распечатайте исходный массив features и убедитесь, что он подвергся неявной мутации.
Посмотреть решение
python
import numpy
import pandas
features = pandas.Series([1.1, 2.2, 3.3], index=["f1", "w2", "f3"])
features_view = features[:2]
# Mutating the view affects parent object memory buffer arrays directly
features_view[0] = 0.0
print(features) # Outputs altered original: [0.0, 2.2, 3.3]5. Безопасное изолированное копирование среза
Задача: На основе массива features из прошлого упражнения извлеките срез последних двух элементов в переменную safe_features_copy, принудительно вызвав метод .copy(). Замените все элементы внутри safe_features_copy на 99.9 одной операцией. Выведите features, доказав отсутствие побочных эффектов.
Посмотреть решение
python
import pandas
features = pandas.Series([0.0, 2.2, 3.3], index=["f1", "w2", "f3"])
# Allocating independent isolated memory buffer via deep copying
safe_features_copy = features[-2:].copy()
safe_features_copy[:] = 99.9
print("Original data remains safe:\n", features) # Remains [0.0, 2.2, 3.3]Упражнения смешанные (5 шт) (включают материал прошлых уроков)
6. Слайсинг Series, созданного из логарифмической сетки NumPy
Задача: Сгенерируйте массив из 4 точек, равномерно распределенных по степеням десятки в логарифмическом масштабе от 0 до 3 с помощью numpy.logspace(0, 3, 4) (Урок 3 модуля NumPy). Передайте этот буфер в качестве данных в структуру pandas.Series, назначив индексы ["scale_1", "scale_2", "scale_3", "scale_4"] (Урок 2 модуля Pandas). Выделите срез по явным текстовым меткам от "scale_1" до "scale_3" включительно (Урок 3 модуля Pandas). Выведите итоговый срез.
Посмотреть решение
python
import numpy
import pandas
# Step 1: Generate logarithmic spacing values (NumPy Lesson 3)
# Yields: [1. 10. 100. 1000.]
log_values = numpy.logspace(0, 3, 4)
# Step 2: Pack array weights to a pandas series container (Pandas Lesson 2)
scales_series = pandas.Series(data=log_values, index=["scale_1", "scale_2", "scale_3", "scale_4"])
# Step 3: Explicit text labels interval slicing (Pandas Lesson 3)
target_scales_slice = scales_series["scale_1":"scale_3"]
print(target_scales_slice) # Contains items: scale_1, scale_2, scale_37. Анализ метаданных и принудительный каст изолированного среза
Задача: Создайте Series из вещественных чисел [0.99, 2.45, 3.88], явно назначив тип данных dtype=numpy.float32 (Урок 4 модуля NumPy). Сделайте изолированную копию среза первых двух элементов через комбинацию позиционных индексов и метода .copy() (Урок 3 модуля Pandas). Преобразуйте тип данных полученной изолированной копии в целочисленный numpy.int32 с помощью метода .astype() (Урок 7 модуля NumPy). Выведите итоговые элементы и проверьте их характеристики формы .shape (Урок 2 модуля Pandas).
Посмотреть решение
python
import numpy
import pandas
# Structuring a low-precision floats tracking container (NumPy Lesson 4 & Pandas Lesson 2)
float_series = pandas.Series(data=[0.99, 2.45, 3.88], dtype=numpy.float32)
# Safe slice extraction (Pandas Lesson 3)
isolated_slice = float_series[0:2].copy()
# Explicit datatype casting with fractional truncation (NumPy Lesson 7)
truncated_integers = isolated_slice.astype(numpy.int32)
print("Truncated values:\n", truncated_integers) # Outputs truncated elements: [0 2]
print("Geometry layout metadata shape:", truncated_integers.shape) # Outputs: (2,)8. In-place зануление элементов через позиционный срез представления
Задача: Сгенерируйте арифметическую прогрессию из 5 нулей с помощью numpy.zeros(5) и оберните её в контейнер pandas.Series (Урок 3 модуля NumPy / Урок 2 модуля Pandas). Выделите позиционный срез последних 3 элементов в переменную end_view (Урок 3 модуля Pandas). Чтобы сэкономить оперативную память, прибавьте к элементам end_view значение 10.0 на месте с помощью in-place оператора += (Урок 8 модуля NumPy). Распечатайте исходный родительский контейнер Series и посмотрите, как мутация View отразилась на базовой памяти (Урок 3 модуля Pandas).
Посмотреть решение
python
import numpy
import pandas
# Allocation flow patterns assembly (NumPy Lesson 3 & Pandas Lesson 2)
root_series = pandas.Series(data=numpy.zeros(5))
# Creating a positional slicing view pipeline reference (Pandas Lesson 3)
end_view = root_series[-3:]
# Running fast in-place accumulation math_oge (NumPy Lesson 8)
end_view += 10.0
print("Mutated original container structure state:\n", root_series)
# Outputs correctly updated values due to View memory link propagation:
# 0 0.0
# 1 0.0
# 2 10.0
# 3 10.0
# 4 10.09. Интроспекция свойств проверки связи буфера .base для срезов Series
Задача: Создайте простую структуру series_obj = pandas.Series([1, 2, 3]) (Урок 2 модуля Pandas). Извлеките из нее срез slice_obj = series_obj[:] (Урок 3 модуля Pandas). Поскольку внутренний буфер данных объекта Series — это массив NumPy, напишите логическое выражение, проверяющее свойство .base у массива значений среза: slice_obj.values.base is series_obj.values. Объясните результат в комментарии к коду (Вспоминаем Урок 6 модуля NumPy).
Посмотреть решение
python
import pandas
series_obj = pandas.Series([1, 2, 3])
slice_obj = series_obj[:]
# Reaching down to low-level array metadata tracking roots (NumPy Lesson 6 & Pandas Lesson 3)
is_linked_to_parent_buffer = slice_obj.values.base is series_obj.values
print("Is the underlying NumPy memory buffer linked directly?", is_linked_to_parent_buffer)
# Outputs: True.
# Explanation: Pandas slicing reuses the same memory layout of the NumPy array underneath.10. Поиск и фильтрация методов математических преобразований в Pandas
Задача: Напишите поисковый запрос по шаблону для выполнения в среде Jupyter Notebook, который отфильтрует и выведет список всех доступных в pandas функций, начинающихся на префикс to (например, методы конвертации форматов) (Урок 1 модуля NumPy / Урок 1 модуля Pandas).
Посмотреть решение
python
import pandas
# Reflection via dir() lists all conversion/export functions with the to_ prefix
print([name for name in dir(pandas) if name.startswith("to")])