Skip to content

5. Чтение, индексация и срезы одномерных векторов ​

Срезы 1D массивов или векторов ​

Анатомия одномерного индексирования ​

Индексация в NumPy полностью наследует синтаксис стандартных списков Python, но работает на уровне скомпилированного C-кода, обеспечивая мгновенный доступ к ячейкам памяти. Нумерация элементов начинается с 0 для доступа с начала массива и с -1 для доступа с конца (обратная индексация).

python
import numpy

# Creating a simple 1D vector of training loss values
loss_history = numpy.array([0.85, 0.62, 0.41, 0.35, 0.22])

# Direct and reverse indexing
print(loss_history[0])   # Outputs first element: 0.85
print(loss_history[-1])  # Outputs last element (current loss): 0.22

Синтаксис расширенных срезов (Slicing) ​

Срез позволяет извлечь подмножество элементов из вектора. Полная формула среза выглядит следующим образом: array[start:stop:step]

  • start — индекс элемента, с которого начинается срез (включается). По умолчанию 0.
  • stop — индекс элемента, на котором срез заканчивается (не включается). По умолчанию равен длине массива.
  • step — шаг обхода элементов. Может быть отрицательным для движения в обратную сторону. По умолчанию 1.
python
import numpy

metrics = numpy.array([10, 20, 30, 40, 50, 60])

# Basic slicing scenarios
print(metrics[1:4])    # Elements from index 1 to 3: [20, 30, 40]
print(metrics[:3])     # First 3 elements: [10, 20, 30]
print(metrics[3:])     # From index 3 to the end: [40, 50, 60]
print(metrics[::2])    # Every second element: [10, 30, 50]
print(metrics[::-1])   # Fully reversed array: [60, 50, 40, 30, 20, 10]

Изменение элементов через срезы (In-place modification) ​

Срезы в NumPy поддерживают операцию массового присвоения. Вы можете обновить целую группу элементов одной векторизованной командой без использования циклов.

python
import numpy

signals = numpy.zeros(6)
# Modifying every second element at once
signals[::2] = 99.0
print(signals)  # Outputs: [99.  0. 99.  0. 99.  0.]

Обратная замена через вывернутые срезы (Reverse In-place slicing) ​

Отрицательный шаг позволяет не только читать массив задом наперед, но и точечно перезаписывать выбранные фрагменты вектора в обратном порядке на лету.

python
import numpy

arr = numpy.array([1, 2, 3, 4, 5])
# Replace the first 3 elements with new values in reverse order
arr[2::-1] = [10, 20, 30]
print(arr)  # Outputs: [30 20 10  4  5]

Произвольное изменение по списку индексов (Fancy Indexing Assignment) ​

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

python
import numpy

weights = numpy.zeros(5)
indices_to_update = [0, 1, 4]

# Modify only the specific slots without affecting indices 2 and 3
weights[indices_to_update] = [5.5, 6.5, 7.5]
print(weights)  # Outputs: [5.5 6.5 0.  0.  7.5]

Срезы в контексте нейросетей ​

Извлечение временных окон и шаговый срез в AI ​

В реальных задачах искусственного интеллекта одномерные срезы применяются повсеместно:

  1. Генерация батчей и валидация: Быстрое отделение тестовой выборки от обучающей (например, забрать первые 80% данных на обучение dataset[:80], а остаток dataset[80:] — на валидацию).
  2. Аудио и Спектрограммы: Обрезание аудио-сигналов, выделение окон фиксированной длительности для подачи в сверточные сети (CNN).
  3. Обработка текста (NLP): Выделение контекстного окна токенов (например, срез tokens[0:max_sequence_len]) перед подачей вектора в Трансформер.
  4. Даунсэмплинг (Downsampling): Понижение частоты дискретизации данных с помощью шага среза (например, срез signal[::2] уменьшает частоту сигнала ровно в 2 раза).
  5. Очистка аномалий в данных (Data Cleaning): Применение масок для быстрой нейтрализации битых значений или выбросов (например, tensor[tensor > threshold] = threshold для обрезки слишком высоких пиков).

Упражнения базовые и продвинутые (7 шт) (только с опорой на объясненную в данном уроке теорию) ​

1. Извлечение последних метрик обучения ​

Задача: Дан вектор значений точности модели (accuracy) по эпохам: [0.51, 0.64, 0.78, 0.85, 0.92, 0.94]. Выделите последние 3 эпохи с помощью обратного среза и выведите их на экран.

Посмотреть решение
python
import numpy

accuracy_history = numpy.array([0.51, 0.64, 0.78, 0.85, 0.92, 0.94])

# Extracting the last 3 elements
last_epochs = accuracy_history[-3:]
print(last_epochs)  # Outputs: [0.85 0.92 0.94]

2. Понижение частоты сигнала (Downsampling) ​

Задача: Сгенерируйте вектор отсчетов аудиосигнала, содержащий числа от 0 до 9 включительно (с помощью numpy.arange). Проведите даунсэмплинг, выделив каждый третий элемент сигнала, начиная с самого первого (индекс 0). Выведите результат.

Посмотреть решение
python
import numpy

audio_signal = numpy.arange(10)

# Slice with a step of 3
downsampled_signal = audio_signal[::3]
print(downsampled_signal)  # Outputs: [0 3 6 9]

3. Инвертирование временного ряда ​

Задача: Дан временной ряд цен акций: [100.5, 101.2, 99.8, 102.4]. Разверните этот массив в обратном хронологическом порядке с помощью синтаксиса срезов. Выведите результат в консоль.

Посмотреть решение
python
import numpy

time_series = numpy.array([100.5, 101.2, 99.8, 102.4])

# Reversing the array using negative step
reversed_series = time_series[::-1]
print(reversed_series)  # Outputs: [102.4  99.8 101.2 100.5]

4. Массовое зануление аномалий ​

Задача: Создайте вектор показаний датчиков: [12.0, 14.5, 999.0, 999.0, 999.0, 11.2]. Известно, что элементы с индексами от 2 до 4 включительно являются ошибочными аномалиями. Замените эти элементы на 0.0 одной операцией среза (без циклов). Выведите итоговый вектор.

Посмотреть решение
python
import numpy

sensor_readings = numpy.array([12.0, 14.5, 999.0, 999.0, 999.0, 11.2])

# Slice index 2 to 4 inclusive means stop index must be 5
sensor_readings[2:5] = 0.0
print(sensor_readings)  # Outputs: [12.  14.5  0.   0.   0.  11.2]

5. Выделение центрального контекстного окна ​

Задача: Дан вектор токенов текста длины 8: [101, 2003, 1037, 2467, 2004, 3012, 1205, 102]. Извлеките центральное окно токенов, исключив первый элемент (индекс 0) и последний элемент (индекс 7), используя один срез. Выведите результат.

Посмотреть решение
python
import numpy

token_ids = numpy.array([101, 2003, 1037, 2467, 2004, 3012, 1205, 102])

# Excluding the first and the last element
context_window = token_ids[1:-1]
print(context_window)  # Outputs: [2003 1037 2467 2004 3012 1205]

6. Зеркальное обновление весов (Reverse In-place Slicing) ​

Задача: Дан вектор весов слоя нейросети: [0.1, 0.2, 0.3, 1.0, 2.0]. Замените первые 3 элемента этого вектора на новые значения [0.9, 0.8, 0.7], но запишите их в массив в обратном порядке (чтобы 0.7 встал на индекс 0, а 0.9 на индекс 2). Сделайте это с помощью вывернутого среза «на месте» без циклов.

Посмотреть решение
python
import numpy

weights = numpy.array([0.1, 0.2, 0.3, 1.0, 2.0])

# Slice the first 3 elements in reverse order and assign new values
weights[2::-1] = [0.9, 0.8, 0.7]
print(weights)  # Outputs: [0.7 0.8 0.9 1.0 2.0]

7. Точечная замена эмбеддингов (Fancy Indexing Assignment) ​

Задача: Инициализируйте нулевой вектор смещений (biases) из 6 элементов. Известно, что в процессе обратного распространения ошибки обновились только крайние и центральные ячейки с индексами 0, 3 и 5. Запишите в эти ячейки значения [-1.2, 0.5, 3.1] одновременно с помощью передачи списка индексов. Выведите финальный массив.

Посмотреть решение
python
import numpy

biases = numpy.zeros(6)
indices_to_update = [0, 3, 5]

# Modifying specific slots at once using a list of coordinates
biases[indices_to_update] = [-1.2, 0.5, 3.1]
print(biases)  # Outputs: [-1.2  0.   0.   0.5  0.   3.1]

Упражнения смешанные (5 шт) (включают материал прошлых уроков) ​

8. Генерация сетки Learning Rate и выделение её начала ​

Задача: Сгенерируйте сетку из 10 шагов темпа обучения (learning rate) от 0.1 до 0.01 включительно (используя numpy.linspace из Урока 3). Извлеките из этой сетки первые 4 элемента — фазу агрессивного старта модели. Выведите срез.

Посмотреть решение
python
import numpy

# Generating steps (Lesson 3 theory)
lr_grid = numpy.linspace(0.1, 0.01, 10)

# Slicing the initial aggressive phase (Lesson 5 theory)
initial_phase = lr_grid[:4]
print(initial_phase)

9. Анализ занимаемой памяти среза квантованного вектора ​

Задача: Создайте вектор индексов токенов из 100 элементов (от 0 до 99 через numpy.arange), принудительно задав ему тип данных numpy.int8 (Из Урока 4). Сделайте срез, выделив каждый второй элемент массива. Выведите объем памяти в байтах (.nbytes), который занимает этот полученный срез.

Посмотреть решение
python
import numpy

# Creating a quantized integer array (Lesson 4 theory)
token_vector = numpy.arange(100, dtype=numpy.int8)

# Slicing every second element (Lesson 5 theory)
sliced_vector = token_vector[::2]

# Checking byte size. Slicing returns a view, but contains 50 elements of 1 byte
print("Number of elements:", sliced_vector.size) # 50
print("Memory size in bytes:", sliced_vector.nbytes)  # Outputs: 50 bytes

10. Исправление пропусков (NaN) внутри среза данных ​

Задача: Создайте вектор весов list_values = [0.25, 0.45, None, None, 0.85]. Как вы знаете из Урока 4, None использовать нельзя. Инициализируйте массив корректно, заменив None на numpy.nan (тип numpy.float32). Затем с помощью одной операции среза замените оба поврежденных центральных элемента со значениями NaN на константу 0.5. Выведите итоговый массив и типы элементов массива

Посмотреть решение
python
import numpy

# Valid initialization using float32 and NaN (Lesson 4 theory)
weights_vector = numpy.array([0.25, 0.45, numpy.nan, numpy.nan, 0.85], dtype=numpy.float32)

# Replacing NaN slice with fixed default value (Lesson 5 theory)
weights_vector[2:4] = 0.5
print(weights_vector)       # Outputs: [0.25 0.45 0.5  0.5  0.85]
print(weights_vector.dtype) # Outputs: dtype('float32')

11. Реверс сгенерированной последовательности шагов оптимизации ​

Задача: Сгенерируйте массив из 6 элементов от 0 до 25 с помощью функции numpy.linspace. Сделайте срез, который вернет этот массив полностью развернутым задом наперед. Убедитесь, что все элементы итогового массива сохранили тип данных float64.

Посмотреть решение
python
import numpy

# Generating linear grid (Lesson 3 theory)
steps_array = numpy.linspace(0, 25, 6)

# Reversing via slice step (Lesson 5 theory)
reversed_array = steps_array[::-1]

print("Reversed:", reversed_array)
print("Dtype confirmation:", reversed_array.dtype)  # float64

12. Массовая инициализация среза пустого тензора ​

Задача: Создайте пустой вектор будущих градиентов из 10 элементов, полностью заполненный нулями (через numpy.zeros). Замените его вторую половину элементов (последние 5 штук) на единицы, используя срез с отрицательным индексом start. Выведите результат.

Посмотреть решение
python
import numpy

# Initializing zero tensor placeholder (Lesson 3 theory)
gradients_tensor = numpy.zeros(10)

#Overwriting the second half utilizing negative start index (Lesson 5 theory)
gradients_tensor[-5:] = 1.0
print(gradients_tensor)  # Outputs: [0. 0. 0. 0. 0. 1. 1. 1. 1. 1.]