# NumPy для Data Science на Python

В этом учебнике **вы узнаете, как использовать библиотеку NumPy для Python в науке о данных**. Вы узнаете, почему эта библиотека имеет значение в области науки о данных и почему она является основой для многих других библиотек. Вы познакомитесь со структурой данных `ndarray` от NumPy и узнаете, как она работает. К концу учебника вы узнаете:

* Чем массивы NumPy отличаются от списков Python
* Почему NumPy такой быстрый и эффективный
* Как создавать одномерные и многомерные массивы NumPy
* Как применять методы и функции к массивам NumPy
* Как программно создавать массивы NumPy

Оглавление

* [Зачем использовать NumPy для науки о данных в Python](#zachem-ispolzovat-numpy-dlya-nauki-o-dannykh-v-python)
* [Установка и импорт NumPy в Python](#ustanovka-i-import-numpy-v-python)
* [Создание массивов Python NumPy](#sozdanie-massivov-python-numpy)
* [Массивы NumPy против списков Python](#massivy-numpy-protiv-spiskov-python)
  * [Умножение массива на скаляр](#umnozhenie-massiva-na-skalyar)
* [Многомерные массивы NumPy](#mnogomernye-massivy-numpy)
* [Индексирование, нарезка и логическое индексирование массивов NumPy](#indeksirovanie-narezka-i-logicheskoe-indeksirovanie-massivov-numpy)
  * [Индексирование, нарезка и логическое индексирование многомерных массивов NumPy](#indeksirovanie-narezka-i-logicheskoe-indeksirovanie-mnogomernykh-massivov-numpy)
* [Применение функций к массивам Numpy](#primenenie-funkcii-k-massivam-numpy)
* [Объединение массивов NumPy](#obedinenie-massivov-numpy)
* [Генерация массивов NumPy программно](#generaciya-massivov-numpy-programmno)
  * [Создание массивов нулей в NumPy](#sozdanie-massivov-nulei-v-numpy)
  * [Создание массивов единиц в NumPy](#sozdanie-massivov-edinic-v-numpy)
  * [Создание матриц идентичности в NumPy](#sozdanie-matric-identichnosti-v-numpy)
  * [Создание массива диапазона в NumPy](#sozdanie-massiva-diapazona-v-numpy)
* [Генерация случайных чисел в NumPy](#generaciya-sluchainykh-chisel-v-numpy)
  * [Создание равномерно случайных значений в NumPy](#sozdanie-ravnomerno-sluchainykh-znachenii-v-numpy)
* [Создание нормального (гауссовского) распределения в NumPy](#sozdanie-normalnogo-gaussovskogo-raspredeleniya-v-numpy)
* [Создание массива случайных целых чисел в NumPy](#sozdanie-massiva-sluchainykh-celykh-chisel-v-numpy)
* [Упражнения](#uprazhneniya)
* [Заключение и резюме](#zaklyuchenie-i-rezyume)
* [Дополнительные ресурсы](#dopolnitelnye-resursy)

### Зачем использовать NumPy для науки о данных в Python

NumPy является одним из основных пакетов для научных вычислений в Python. Эта библиотека настолько важна для сообщества специалистов по данным в Python, что лежит в основе многих других библиотек для работы с данными, таких как Pandas и Matplotlib.

NumPy предоставляет ключевой объект - `ndarray`. `ndarray` представляет собой многомерный массив однородных данных. Он позволяет создавать массивы одного измерения, двух измерений (вроде таблицы или матрицы) и других многих измерений.

Одним из важных преимуществ NumPy является его _скорость_. Почему NumPy так быстр? NumPy позволяет векторизовать ваш код, предоставляя вам методы для изменения, преобразования и агрегирования ваших массивов с невероятно высокой скоростью. Возможность векторизации обусловлена тем, что NumPy использует оптимизированный, предварительно скомпилированный код на C.

NumPy предоставляет вам инструменты, которые позволяют вам распространять ваши операции (концепцию, о которой вы узнаете позже). Это приводит к гораздо более читаемому коду. Это потому, что NumPy обрабатывает эти операции за вас, вместо того чтобы полагаться на операции, такие как циклы for.

### Установка и импорт NumPy в Python

Давайте начнем с изучения того, как установить NumPy. Поскольку NumPy не входит в стандартную библиотеку Python, вам нужно установить его, прежде чем вы сможете его использовать. Установить библиотеку очень просто с помощью установщика пакетов `pip`. Чтобы установить библиотеку, просто выполните следующую команду в вашем терминале:

```python
# Устанавливаем библиотеку NumPy с помощью pip
pip install numpy
```

`pip` управляет установкой NumPy и всех его зависимостей. После завершения установки вы можете импортировать библиотеку. Согласно конвенции, NumPy импортируется с псевдонимом `np`. Хотя вы не обязаны следовать этой конвенции, вы встретите её практически везде. Это значительно облегчит поиск и устранение проблем в вашем коде, если таковые возникнут.

Сохраните приведенный ниже код в файл Python и запустите его. Если код работает без ошибок, значит, вы готовы начать работать с NumPy в Python!

```python
# Импортируем NumPy
import numpy as np
```

Давайте начнем изучение замечательного мира массивов NumPy.

### Создание массивов Python NumPy

Объекты `ndarray` из библиотеки NumPy представляют собой n-мерные массивы. На первый взгляд они могут показаться весьма похожими на списки в Python, но на деле они работают совсем по-другому. Давайте попробуем создать наш первый массив:

```python
# Создание вашего первого массива
import numpy as np
array = np.array([1, 2, 3, 4, 5])
```

Давайте проверим, какой тип у этого массива, используя функцию `type()`

```python
# Проверка типа массива
print(type(array))

# Возвращает: <class 'numpy.ndarray'>
```

Поскольку массивы NumPy однородны, вы можете указать тип данных массива при его создании. Давайте проверим, какой тип данных у созданного вами выше массива. Это можно сделать, используя атрибут `.dtype`

```python
# Проверка типа данных массива
print(array.dtype)

# Возвращает: int64
```

Аналогичным образом, вы можете определить тип данных при создании массива, передав параметр `dtype=`. Давайте создадим наш массив заново, используя тип данных `float64`:

```python
# Создание массива с определенным типом данных
array = np.array([1, 2, 3, 4, 5], dtype='float64')
print(array.dtype)

# Возвращает: float64
```

Теперь, когда у вас есть предварительное понимание того, как создавать массивы NumPy, давайте рассмотрим, чем они отличаются от списков.

### Массивы NumPy против списков Python

На первый взгляд, массивы NumPy могут выглядеть довольно похожими на объект списка Python. Фактически, вы даже можете использовать списки для создания массивов (и наоборот). Однако массивы NumPy _довольно_ отличаются от списков Python. Давайте рассмотрим некоторые ключевые отличия между ними.

1. **Фиксированный размер**: Массивы NumPy имеют фиксированный размер при создании. В отличие от них, списки Python могут динамически увеличиваться в размере. При изменении размера массива NumPy исходный массив уничтожается, и создается новый.
2. **Однородные элементы**: элементы в массиве NumPy должны быть одного типа данных. В отличие от этого, списки в Python не требуют однородности типов данных. (Существует одно исключение: когда массивы NumPy содержат объекты, эти объекты могут содержать разные типы данных)
3. **Массивы NumPy созданы для математических операций**: функции и методы, которые могут быть применены к массиву NumPy, сосредоточены вокруг математики и эффективности

Массивы NumPy замечательны тем, что их можно писать с простотой Python, но достигать скорости скомпилированного кода C. Это потому, что, под капотом, NumPy использует скомпилированный код C для многих своих операций. Это позволяет достигать невероятно высокой эффективности программирования, с легкостью и простотой, которые предоставляет кодирование на Python.

Давайте рассмотрим пример того, _как_ это работает на практике. Мы также более подробно рассмотрим это позже.

#### Умножение массива на скаляр

Для нашего примера давайте рассмотрим, как мы можем умножить все элементы списка на скаляр или умножить весь массив на скаляр. Допустим, у нас есть список с элементами `[1, 2, 3, 4, 5]`. С массивом мы можем просто умножить массив на это значение:

```python
# Умножение массива на скаляр
array = np.array([1, 2, 3, 4, 5])
array2 = 2 * array
print(array2)

# Возвращает: [ 2  4  6  8 10]
```

Давайте попробуем ту же операцию со списком:

```python
# Умножение списка на скаляр
list1 = [1, 2, 3, 4, 5]
list2 = 2 * list1
print(list2)

# Возвращает: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
```

Чтобы достичь того же с листом, \*\*нам нужно итерироваться по каждому элементу и умножать его на этот скаляр

```python
# Умножение элементов списка на скаляр
list1 = [1, 2, 3, 4, 5]
list2 = []
for item in list1:
    list2.append(2 * item)
print(list2)

# Возвращает: [2, 4, 6, 8, 10]
```

Преимущества использования NumPy двоякие:

1. Мы достигаем большей читаемости того, что мы надеемся выполнить
2. Обработка данных у нас **векторизована** благодаря использованию C в основе. Это позволяет выполнять операции значительно быстрее!

Теперь, когда у вас есть хорошее понимание того, как списки и массивы отличаются, давайте рассмотрим многомерные массивы NumPy в Python!

### Многомерные массивы NumPy

В этом разделе вы узнаете, как работают многомерные массивы. В основном, вы узнаете, как создавать двумерные массивы, так как их легче передать на экране компьютера.

В предыдущем примере вы создали одномерный массив, передав список. Аналогичным образом, вы можете создать двумерный массив, передав список списков. Давайте посмотрим на простой пример:

```python
# Создание двумерного массива
array = np.array([[1, 2, 3], [4, 5, 6]])
print(array)

# Возвращает:
# [[1 2 3]
#  [4 5 6]]
```

Мы можем проверить размерность массива, используя атрибут `.ndim`, который возвращает одно значение размерности:

```python
# Проверка размерности массива
array = np.array([[1, 2, 3], [4, 5, 6]])
print(array.ndim)

# Возвращает: 2
```

Аналогично, мы можем использовать атрибут `.shape` для возвращения количества элементов, хранящихся в каждом измерении массива.

```python
# Проверка размерности массива
array = np.array([[1, 2, 3], [4, 5, 6]])
print(array.shape)

# Возвращает: (2, 3)
```

Наконец, вы можете использовать атрибут `.size`, чтобы понять общее количество элементов, существующих в массиве. Этот атрибут отражает произведение элементов формы массивов.

```python
# Проверка размера массива
array = np.array([[1, 2, 3], [4, 5, 6]])
print(array.size)

# Возвращает: 6
```

В следующем разделе вы узнаете, как получить доступ к элементам массива NumPy, используя индексацию, срезы и булеву индексацию.

### Индексирование, нарезка и логическое индексирование массивов NumPy

На данный момент вы научились создавать одномерные и многомерные массивы. В этом разделе вы узнаете, как получать доступ к данным в этих массивах с помощью индексации, срезов и булевой индексации.

Давайте начнем с доступа к элементам одномерного массива. Это работает очень похоже на доступ к элементам списка. Индексация и срезка массивов NumPy работает очень похоже на индексацию и срезку списков Python:

* Индексы начинаются с 0 и продолжаются до конца списка
* Отрицательные индексы начинаются с
* Массивы можно разделять с помощью двоеточия, используя как положительные, так и отрицательные индексы (или оба).
* Конец среза подразумевает либо полную левую, либо правую сторону массива

Давайте рассмотрим несколько индексов и срезов:

```python
# Индексирование и срезы 1-D массива NumPy
import numpy as np
array = np.array([1, 2, 3, 4, 5])

print(array[0])     # Возвращает: 1
print(array[-1])    # Возвращает: 5
print(array[1:3])   # Возвращает [2 3]
```

Вы также можете использовать логическую индексацию массивов. Это означает, что вы можете фильтровать массив на основе логического условия. Вы можете освежить в памяти логические таблицы истинности здесь

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

```python
# Создание булевого индекса
import numpy as np
array = np.array([1, 2, 3, 4, 5])
bool_array = array > 2

print(bool_array)

# Возвращает: [False False  True  True  True]
```

Этот код возвращает массив, содержащий исключительно логические значения, где `True` указывает на то, что условие выполнено. На первый взгляд это может показаться не особо полезным. Однако, вы можете использовать данный массив как индексатор для фильтрации значений в вашем исходном массиве.

```python
# Фильтрация массива
import numpy as np
array = np.array([1, 2, 3, 4, 5])
bool_array = array > 2

filtered = array[bool_array]
# То же самое: filtered = array[bool_array > 2]
print(filtered)

# Возвращает: [3 4 5]
```

#### Индексирование, нарезка и логическое индексирование многомерных массивов NumPy

В предыдущем разделе сравнивались индексация, срезание и булева индексация одномерного массива с работой со списками Python. Точно так же индексация, срезание и булева индексация многомерного массива NumPy могут быть сравнены с работой со списками списков Python.

Давайте создадим многомерный массив NumPy для работы:

```python
# Создание двумерного массива NumPy
import numpy as np

array = np.array([[1, 2, 3], [4, 5, 6]])
```

Теперь давайте попробуем применить индексирование, которое вы узнали ранее:

```python
print(array[0])

# Возвращает: [1 2 3]
```

Вместо того чтобы вернуть первое значение (`1`), метод индексации вернул массив. Это на самом деле очень удобно, поскольку мы можем просто проиндексировать этот массив снова!

```python
print(array[0][0])

# Возвращает: 1
```

Кроме замечания о индексации внутренних массивов, индексация и срезание работают абсолютно одинаково.

С другой стороны, индексирование с использованием булевых значений работает немного иначе. Давайте попробуем применить тот же фильтр, что и ранее (что элемент больше 2):

```python
# Булевое индексирование двумерного массива
import numpy as np

array = np.array([[1, 2, 3], [4, 5, 6]])
filtered = array[array > 2]
print(filtered)

# Возвращает: [3 4 5 6]
```

Применение булева индекса к многомерному массиву возвращает сглаженный одномерный массив. Для сохранения исходной размерности массивов можно использовать функцию `np.where()`. Давайте применим тот же фильтр:

```python
# Использование np.where() для фильтрации массива
import numpy as np

array = np.array([[1, 2, 3], [4, 5, 6]])
filtered = np.where(array > 2, array, np.NaN)

print(filtered)

# Возвращает:
# [[nan nan  3.]
#  [ 4.  5.  6.]]
```

### Применение функций к массивам Numpy

В этом разделе вы научитесь применять функции и методы к массиву NumPy. Ранее вы уже узнали, что умножение массива NumPy на скаляр отличается от работы со списком Python. Это верно и для многих других операций. Давайте рассмотрим несколько примеров:

```python
# Применение операций к массиву NumPy
array = np.array([1, 2, 3, 4, 5])
print(array * 2)        # [ 2  4  6  8 10]
print(array + 1)        # [2 3 4 5 6]
print(array % 2)        # [1 0 1 0 1]
```

Точно так же вы можете складывать, вычитать и умножать (и не только) различные массивы друг с другом:

```python
# Добавление, вычитание и умножение массивов
array1 = np.array([1, 2, 3])
array2 = np.array([4, 5, 6])

print(array1 + array2)          # [5 7 9]
print(array1 - array2)          # [-3 -3 -3]
print(array1 * array2)          # [ 4 10 18]
```

Массивы NumPy также обладают рядом очень полезных методов. Например, вы можете легко вычислить сумму всех значений или среднее всех значений, применив соответствующий метод. Давайте рассмотрим несколько примеров:

```python
# Методы массива NumPy
array = np.array([1, 2, 3, 4, 5])

print(array.mean())         # 3.0
print(array.sum())          # 15
```

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

При работе с методами массивов на многомерных массивах концепция оси становится важной. Если вы не указываете ось, NumPy будет предполагать ось `None`. При передаче `axis=None`, любой многомерный массив будет преобразован в одномерный.

Ось с индексом 0 можно рассматривать как «столбцы» матрицы. Тем временем ось с индексом 1 можно рассматривать как «строки» матрицы. Исходя из этого, можно применять эти оси к методам для расчета различных агрегаций. Давайте загрузим многомерный массив и применим различные методы к нему:

```python
# Применение методов к двумерным массивам
array = np.array([[1, 2], [3, 4], [5, 6]])

print(array.sum(axis=None))     # 21
print(array.sum(axis=0))        # [ 9 12]
print(array.sum(axis=1))        # [ 3  7 11]
```

Вы можете видеть, как это работает. Помните, массив выглядит как код ниже, где каждый переданный вложенный список является «строкой» в матрице:

```python
print(array)

# Returns:
# [[1 2]
#  [3 4]
#  [5 6]]
```

Передавая различные оси, можно сделать следующие интерпретации:

* `axis=None` плоское представление массивов и возвращает сумму всех элементов
* `axis=0` возвращает сумму по размерности столбца
* `axis=1` возвращает сумму по направлению строки

### Объединение массивов NumPy

В этом разделе вы узнаете, как работает конкатенация массивов NumPy. Массивы NumPy имеют понятие `axis` (ось), которое может помочь указать NumPy, _как_ конкатенировать различные массивы.

Давайте рассмотрим два массива:

```python
# Two Samples Arrays
a = np.array([[1,2], [3,4]])
b = np.array([[5,6]])
```

Вы можете использовать функцию `np.concatenate()` для объединения этих массивов. Метод требует указания оси. Когда мы передаем ось со значением `None`, то получаемый объединенный массив имеет одномерную структуру.

```python
# Concatenating with an axis of None
a = np.array([[1,2], [3,4]])
b = np.array([[5,6]])

c = np.concatenate((a, b), axis=None)
print(c)

# Returns: [1 2 3 4 5 6]
```

Если вы хотите добавить второй массив как еще одну "строку" в матрицу, вы можете использовать параметр `axis=0`. Важно, чтобы длина каждого массива была одинаковой.

```python
# Concatenating with axis=0
a = np.array([[1,2], [3,4]])
b = np.array([[5,6]])

c = np.concatenate((a, b), axis=0)
print(c)

# Returns:
# [[1 2]
#  [3 4]
#  [5 6]]
```

Если вы хотите добавить массив в качестве "столбца" к первому массиву, вы можете использовать параметр `axis=1`. Однако, поскольку размерности не совпадают, сначала вам нужно транспонировать массив. Это можно сделать с помощью метода `.T`, который возвращает транспонированный массив:

```python
# Concatenating with axis=1
a = np.array([[1,2], [3,4]])
b = np.array([[5,6]])

c = np.concatenate((a, b.T), axis=1)
print(c)

# Returns:
# [[1 2 5]
#  [3 4 6]]
```

В следующем разделе вы узнаете, как использовать NumPy для программного создания массивов.

### Генерация массивов NumPy программно

Существует несколько простых способов программно генерировать массивы NumPy. Это может быть невероятно полезно, когда вам нужны фиктивные данные или вам нужно создать единичные матрицы. Давайте рассмотрим несколько функций, которые вы можете использовать в numpy Python:

#### Создание массивов нулей в NumPy

Чтобы создать массив из нулей в NumPy Python, вы можете использовать функцию с говорящим названием `.zeros()`. Функция может принимать размер вашего массива в качестве аргумента. Если вы передаете одно значение, функция возвращает одномерный массив. Передача кортежа породит массив нулей с указанным размером. Давайте рассмотрим несколько примеров:

```python
# Creating arrays of zeroes
array1 = np.zeros(3)
array2 = np.zeros((2,3))

print('array1 looks like:')
print(array1)
print('\narray2 looks like:')
print(array2)

# Returns:
# array1 looks like:
# [0. 0. 0.]

# array2 looks like:
# [[0. 0. 0.]
#  [0. 0. 0.]]
```

#### Создание массивов единиц в NumPy

Аналогичным образом, в NumPy есть функция для создания массива из единиц. Эта функция так же уместно называется `.ones()`. Функция работает так же, как `.zeroes()`, за исключением того, что возвращает 1 вместо 0:

```python
# Creating arrays of 1s
array1 = np.ones(3)
array2 = np.ones((2,3))

print('array1 looks like:')
print(array1)
print('\narray2 looks like:')
print(array2)

# Returns:
# array1 looks like:
# [1. 1. 1.]

# array2 looks like:
# [[1. 1. 1.]
#  [1. 1. 1.]]
```

#### Создание матриц идентичности в NumPy

Матрица идентичности — это матрица размера `n на n`, в которой элементы, расположенные на главной диагонали, равны 1, а все остальные элементы равны 0. Создать такую матрицу можно с помощью функции `.eye()`. Поскольку матрица должна быть квадратной, достаточно передать единственный размер. Давайте посмотрим, как это выглядит:

```python
array = np.eye(4)
print(array)

# Returns:
# [[1. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [0. 0. 1. 0.]
#  [0. 0. 0. 1.]]
```

#### Создание массива диапазона в NumPy

NumPy также предлагает полезную функцию для создания массива из диапазона значений. Эта функция называется `.arange()`, которую можно использовать для создания диапазона значений от 0 до (но не включая) введенного числа. Давайте создадим массив, содержащий значения от 0 до 5:

```python
array = np.arange(6)
print(array)

# Returns: [0 1 2 3 4 5]
```

Аналогично функции `range()` в Python, где вы можете указать параметры начала, конца и шага, для создания массива, содержащего значения от 0 до 10 с шагом в 2, вы можете написать:

```python
array = np.arange(0, 11, 2)
print(array)

# Returns: [ 0  2  4  6  8 10]
```

### Генерация случайных чисел в NumPy

NumPy также предлагает мощные функции для создания массивов случайных значений. Например, вы можете создать равномерные случайные распределения или нормальные (гауссовские) распределения.

#### Создание равномерно случайных значений в NumPy

Чтобы создать равномерно случайное распределение, вы можете использовать функцию `np.random.random()`. Как и в предыдущих примерах, передача одного значения возвращает одномерный массив указанной длины. Передача кортежа создает многомерный массив с указанными размерами. Давайте создадим массив `3x2` случайных значений от 0 до 1.

```python
# Uniformly Random Values
array = np.random.random((3, 2))
print(array)

# Returns:
# [[0.56942196 0.55263432]
#  [0.12823255 0.60557413]
#  [0.36275958 0.46599701]]
```

### Создание нормального (гауссовского) распределения в NumPy

Чтобы создать массив с нормальным распределением в NumPy Python, вы можете использовать функцию `np.random.randn()`. Среднее значение массива будет равно 0, а дисперсия будет единичной. Посмотрим, как мы можем создать массив `3x2` со значениями, распределенными нормально:

```python
# Normal Distribution in NumPy
array = np.random.randn(3,2)
print(array)

# Returns:
# [[-1.07816465  1.3593095 ]
#  [ 0.5428646  -0.55262844]
#  [-0.46369626  0.56692646]]
```

### Создание массива случайных целых чисел в NumPy

Давайте теперь рассмотрим, как создать массив случайных целых чисел в NumPy. Для этого вы можете использовать функцию `np.random.randint()`. Функция принимает аргументы `low=`, `high=` и `size=`. Аргумент `high=` является исключающим, что означает, что значения будут достигать этого значения, но не включать его. Давайте создадим массив `5x5` со случайными значениями от 4 до 12.

```python
# Random Integer Arrays
array = np.random.randint(low=4, high=13, size=(5,5))
print(array)

# Returns:
# [[ 8  7  9  6  9]
#  [ 4  5 10 12 11]
#  [10  6 12  6  9]
#  [11 12 11  6 12]
#  [ 4  7 12  6  4]]
```

### Упражнения

Теперь пришло время проверить ваше понимание! Попытайтесь выполнить упражнения ниже. Если вам нужна помощь или вы хотите проверить свое решение, просто разверните раздел под вопросом.

Чтобы ответить на вопросы, используйте следующие массивы:

```python
array1 = np.array([1,2,3,4,5])
array2 = np.array([[1,2,3], [4,5,6], [7,8,9]])
```

{% tabs %}
{% tab title="Задание 1" %}
**Как бы вы отфильтровали array = np.array(\[1,2,3,4,5]), чтобы включать только четные числа?**

Вы можете использовать оператор модуля для фильтрации, чтобы получать только четные числа, так как любое четное число, к которому применен модуль 2, будет равняться 0.

```python
array = np.array([1,2,3,4,5])
filtered = array[array % 2 == 0]

print(filtered)

# Returns: [2 4]
```
{% endtab %}

{% tab title="Задание 2" %}
**Как бы вы проиндексировали значение 6 в массиве2?**

Вы можете использовать как положительную, так и отрицательную индексацию:

```python
print(array2[1][2])
print(array2[1][-1])
```
{% endtab %}

{% tab title="Задание 3" %}
**Найти максимальное значение в каждой «строке» массива2?**

Используйте `axis=1` в методе `.mean()`:

```python
print(array2.mean(axis=1))
```
{% endtab %}
{% endtabs %}

### Заключение и резюме

В этом учебнике вы научились начать работу с библиотекой NumPy и использовать её структуру данных массива. Ниже представлено краткое изложение того, что вы узнали:

* NumPy — важная фундаментальная библиотека для науки о данных в Python.
* NumPy можно установить с помощью установщика пакетов pip.
* Массивы могут выглядеть как списки Python, но функционировать совершенно по-другому.
* NumPy использует предварительно скомпилированный код C для повышения скорости и эффективности.
* Массивы NumPy можно нарезать, индексировать и логически индексировать аналогично спискам Python.
* Массивы NumPy используют ось для идентификации «строк» и «столбцов» матрицы.
* Вы можете эффективно применять функции и методы к массивам.

### Дополнительные ресурсы

Чтобы узнать больше о связанных темах, ознакомьтесь с обучающими материалами ниже:

* Скалярное произведение Numpy: вычисление скалярного произведения Python
* Python: получить индекс максимального элемента в списке
* Python: умножение списков (6 разных способов)
* [Официальная документация](https://numpy.org/doc/stable/reference/arrays.ndarray.html)
