NumPy для Data Science на Python

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

  • Чем массивы NumPy отличаются от списков Python

  • Почему NumPy такой быстрый и эффективный

  • Как создавать одномерные и многомерные массивы NumPy

  • Как применять методы и функции к массивам NumPy

  • Как программно создавать массивы NumPy

Оглавление

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

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

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

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

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

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

# Installing NumPy with pip
pip install numpy

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

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

# Importing NumPy
import numpy as np

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

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

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

# Creating your first array
import numpy as np
array = np.array([1,2,3,4,5])

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

# Checking the type of the array
print(type(array))

# Returns: <class 'numpy.ndarray'>

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

# Checking the data type of an array
print(array.dtype)

# Returns: int64

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

# Creating an array with a data type
array = np.array([1,2,3,4,5], dtype='float64')
print(array.dtype)

# Returns: 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]. С массивом мы можем просто умножить массив на это значение:

# Multiplying an array by a scalar
array = np.array([1,2,3,4,5])
array2 = 2 * array
print(array2)

# Returns: [ 2  4  6  8 10]

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

list1 = [1,2,3,4,5]
list2 = 2 * list1
print(list2)

# Returns: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

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

list1 = [1,2,3,4,5]
list2 = []
for item in list1:
    list2.append(2 * item)
print(list2)

# Returns: [2, 4, 6, 8, 10]

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

  1. Мы достигаем большей читаемости того, что мы надеемся выполнить

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

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

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

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

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

# Creating a two-dimensional array
array = np.array([[1,2,3],[4,5,6]])
print(array)

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

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

# Checking the dimensions of an array
array = np.array([[1,2,3],[4,5,6]])
print(array.ndim)

# Returns: 2

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

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

# Returns: (2, 3)

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

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

# Returns: 6

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

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

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

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

  • Индексы начинаются с 0 и продолжаются до конца списка

  • Отрицательные индексы начинаются с

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

  • Конец среза подразумевает либо полную левую, либо правую сторону массива

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

# Indexing and Slicing a 1-D NumPy Array
import numpy as np
array = np.array([1,2,3,4,5])

print(array[0])     # Returns: 1
print(array[-1])    # Returns: 5
print(array[1:3])   # Returns [2 3]

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

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

# Creating a Boolean Index
import numpy as np
array = np.array([1,2,3,4,5])
bool_array = array > 2

print(bool_array)

# Returns: [False False  True  True  True]

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

# Filtering an array
import numpy as np
array = np.array([1,2,3,4,5])
bool_array = array > 2

filtered = array[bool_array]
# Same as: filtered = array[bool_array > 2]
print(filtered)

# Returns: [3 4 5]

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

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

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

# Creating a 2-dimensional NumPy array
import numpy as np

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

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

print(array[0])

# Returns: [1 2 3]

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

print(array[0][0])

# Returns: 1

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

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

# Boolean indexing a 2-dimensional array
array = np.array([[1,2,3], [4,5,6]])
filtered = array[array > 2]
print(filtered)

# Returns: [3 4 5 6]

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

# Using np.where() to filter an array
import numpy as np

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

print(filtered)

# Returns:
# [[nan nan  3.]
#  [ 4.  5.  6.]]

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

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

# Applying Operations to a NumPy array
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]

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

# Adding, Subtracting, and Multiplying Arrays
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 также обладают рядом очень полезных методов. Например, вы можете легко вычислить сумму всех значений или среднее всех значений, применив соответствующий метод. Давайте рассмотрим несколько примеров:

# NumPy Array Methods
array = np.array([1,2,3,4,5])

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

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

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

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

# Applying Methods to 2-D Arrays
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]

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

print(array)

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

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

  • axis=None плоское представление массивов и возвращает сумму всех элементов

  • axis=0 возвращает сумму по размерности столбца

  • axis=1 возвращает сумму по направлению строки

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

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

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

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

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

# 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. Важно, чтобы длина каждого массива была одинаковой.

# 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, который возвращает транспонированный массив:

# 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(). Функция может принимать размер вашего массива в качестве аргумента. Если вы передаете одно значение, функция возвращает одномерный массив. Передача кортежа породит массив нулей с указанным размером. Давайте рассмотрим несколько примеров:

# 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:

# 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(). Поскольку матрица должна быть квадратной, достаточно передать единственный размер. Давайте посмотрим, как это выглядит:

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:

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

# Returns: [0 1 2 3 4 5]

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

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

# Returns: [ 0  2  4  6  8 10]

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

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

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

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

# 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 со значениями, распределенными нормально:

# 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.

# 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]]

Упражнения

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

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

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

Как бы вы отфильтровали array = np.array([1,2,3,4,5]), чтобы включать только четные числа?

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

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

print(filtered)

# Returns: [2 4]

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

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

  • NumPy — важная фундаментальная библиотека для науки о данных в Python.

  • NumPy можно установить с помощью установщика пакетов pip.

  • Массивы могут выглядеть как списки Python, но функционировать совершенно по-другому.

  • NumPy использует предварительно скомпилированный код C для повышения скорости и эффективности.

  • Массивы NumPy можно нарезать, индексировать и логически индексировать аналогично спискам Python.

  • Массивы NumPy используют ось для идентификации «строк» и «столбцов» матрицы.

  • Вы можете эффективно применять функции и методы к массивам.

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

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

  • Скалярное произведение Numpy: вычисление скалярного произведения Python

  • Python: получить индекс максимального элемента в списке

  • Python: умножение списков (6 разных способов)

Last updated