Alexey Potapov 18 июня 2021

🤖 Numpy, Pandas, matplotlib – необходимый минимум для старта в Machine Learning

В одном обзоре мы рассмотрим базовый программный минимум для старта в Machine Learning. Для этого понадобятся: Python 3.8+, Jupyter Notebook, numpy, pandas и matplotlib.

Поговорим о Jupyter

Jupiter Notebook – открытое веб-приложение, позволяющее создавать интерактивные документы. Документ объединяет код программы и его интерактивный вывод, позволяет оформлять код текстом, формулами, анимациями, изображениями. В разделе Install официального сайта Jupyter Notebook приведены все необходимые сведения для его установки, а краткая инструкция доступна и на нашем сайте. Альтернативой использования Jupyter Notebook может быть Visual Studio Code или PyCharm с установленными соответствующими плагинами.

Для создания нового блокнота необходимо нажать на кнопку New в верхнем правом углу экрана.

Главная страницы Jupyter Notebook
Главная страницы Jupyter Notebook
Вы можете увидеть несколько различных версий Python. Выберите нужную, помня, что хорошим тоном является использование виртуальных окружений. Я использую виртуальное окружение, созданное miniconda.

Дальше мы увидим, следующую картину:

Новый блокнот в Jupyter Notebook
Новый блокнот в Jupyter Notebook

По умолчанию доступна пустая ячейка для вставки кода. Введем в нее print("Hello proglib.io") и нажмём Ctrl+Enter. Это заставить Jupyter выполнить этот участок кода и вывести под ячейкой результат его выполнения.

Результат выполнения ячейки
Результат выполнения ячейки

Доступна похожая на предыдущую комбинация клавиш Shift+Enter, которая выполняет код в текущей активной ячейке и добавляет под ней новую.

Второй тип ячеек, поддерживаемый в Jupyter Notebook, позволяет использовать разметку markdown. Чтобы сменить тип ячейки, можно нажать латинскую клавишу M или выбрать тип в выпадающем списке вверху.

Makdown ячейка
Makdown ячейка

Полное описание синтаксиса этих ячеек можно найти здесь.

Дополнительные материалы:

NumPy. Массивы, базовые операции, индексация

Прежде, чем начать погружение в NumPy, его необходимо установить. Чтобы потом не отвлекаться, поставим и остальные необходимый пакеты. Откроем терминал и выполним:

        pip install numpy pandas matplotlib
    
NumPy – открытая библиотека, добавляющая поддержку n-мерных массивов в Python и огромное количество быстрых удобных функций для работы с ними.

NumPy предоставляет свой тип данных для работы с многомерными массивами – ndarray. Измерения массива называются осями (axis). Например, матрица 2х2 типа ndarray имеет 2 оси (axis).

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

И первым делом необходимо импортировать numpy в проект. Для этого добавьте в ячейку следующий код и запустите его на выполнение.

        import numpy as np
    

Для получения количества осей в массиве (здесь и далее под словом массив будем подразумевать массив NumPy), необходимо обратиться к свойству ndim:

Исследуем свойство ndim
Исследуем свойство ndim

В приведенном коде, создаем массив a1 из списка чисел. Обращаемся к свойству массива ndim, получаем одномерный массив.

Создадим двумерный массив. Для этого в качестве параметра будем метода array будем использовать не список, а список списков.

Исследуем свойство ndim
Исследуем свойство ndim

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

Исследуем свойство shape
Исследуем свойство shape

Список а1 представляет собой одну ось из пяти элементов. Список a2 – одна ось из двух элементов, т.е. из двух строк, и вторая ось из пяти элементов, т.е. из пяти столбцов.

NumPy предоставляет довольно много различных функций для построение массивов. Рассмотрим некоторые из них. Мы уже сталкивались с методом array, позволяющим обернуть обычный список python в массив NumPy.

Существует довольно много специальных массивов, применяемых в различных расчетах: единичные, нулевые, пустые и т.д. Чтобы получить массив, состоящий строго из одних единиц, используется метод ones((n,m)).

        np.ones((5, 6))
    

Чтобы получить массив, состоящий строго из одних нулей, используется метод zeros((n,m)). Его синтаксис аналогичен предыдущему.

Использование методов ones() и zeros()
Использование методов ones() и zeros()

Квадратная матрица, состоящая из единиц на главной диагонали и остальных нулей, называется единичной и строится с помощью метода identity(n). Единственный входной параметр – количество строк и столбцов.

        np.identity(3)
    

Пустая матрица создается с помощью метода empty((n, m)). Вызов этого метода возвращает матрицу следующего вида:

Использование метода empty()
Использование метода empty()

Обратите внимание, что матрица состоит не из нулей, а из очень маленьких чисел типа float.

В NumPy есть аналог стандартной функции range: np.arange(<start>, stop, <step>). Он возвращает равномерно распределенные значения в заданном интервале и может принимать как целые числа, так и числа с плавающей точкой. Например:

        np.arange(3, 22, 3)
np.arange(1.5, 6.7, 0.7)
    
Построение диапазонов с помощью метода arange()
Построение диапазонов с помощью метода arange()

Метод reshape(a, newshape, order='C') позволяет переделать форму (количество осей) существующего массива. Для наглядности будем использовать следующий массив и приведем несколько примеров:

        A = np.arange(9) # подопытный одномерный массив
A.reshape((4,2)) # изменяем его форму в матрицу из четырех строк и двух столбцов
A..reshape((2, -1)) # делаем новую форму как матрицу из двух строк
    
Изменение формы массива с помощью reshape()
Изменение формы массива с помощью reshape()

NumPy позволяет транспонировать матрицы. Создадим себе матрицу С следующим образом:

        C = A.reshape((2, -1))
    

Чтобы получить транспонированную матрицу C, нужно просто обратиться к свойству Т следующим образом:

р
        C.T 
    
Транспонирование матриц
Транспонирование матриц

Часто возникает необходимость создать массив, состоящий из повторов другого массива. Для этого существует метод numpy.tile(A, reps), где A – какой-то массив, а resp – шаблон, по которому нужно скопировать массив А. Рассмотрим несколько примеров. Для этого создадим список A следующим образом:

        A = np.arange(3)
    
  • Вызов np.tile(A, (2,2)) построит новый массив повторами массива A два раза по-вертикали и два раза по-горизонтали.
  • Вызов np.tile(A, (3,1)) построит новый массив повторами массива A три раза по-вертикали и один раза по-горизонтали.
Создание матриц с помощью метода tile()
Создание матриц с помощью метода tile()

Операции с массивами NumPy

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

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

Создадим матрицы D и E размера 3х3. Для примера, сложим обе матрицы, перемножим друг с другом, перемножим матрицу E с числом 10. Остальные операции доступны в блокноте, ссылка на который в конце статьи:

        D = np.arange(9).reshape((3,3))
E = np.arange(2, 11).reshape((3,3))

    
Математические операции над массивами numpy
Математические операции над массивами numpy

Стоит отдельно отметить, что выбор знака * для поэлементного перемножения матриц выглядит странным. От него, согласно правилам линейной алгебры, интуитивно ожидается другое поведение – перемножение матриц по следующим правилам: каждый элемент результирующей матрицы – сумма произведений каждого элемента соответствующей строки в первой матрице с соответствующим элементом из колонки второй. Но он позволяет поэлементно перемножить две матрицы.

Для классического умножения в NumPy используется метод dot(a, b). Применим его к нашим матрицам D и E.

        np.dot(D,E)
    
Умножение матриц
Умножение матриц

Чтобы найти максимум матрицы, можно использовать метод max().

        E.max() 
    
Максимум матрицы
Максимум матрицы

Входным параметром данного метода является axis - возвращение максимум по соответствующему измерению.

        E.max(axis=0) # находит максимумы в столбцах
E.max(axis=1) # находит максимумы в строках

    
Поиски максимума в строках и столбцах матрицы
Поиски максимума в строках и столбцах матрицы

Аналогичным образом работает метод sum():

        E.sum(axis=0) # находит сумму каждого столбца
E.sum(axis=1) # находит сумму каждой строк

    
Поиск суммы строк и столбцов матрицы
Поиск суммы строк и столбцов матрицы

Индексация массивов NumPy

Пакет NumPy поддерживает несколько различных способ индексации массивов. Чтобы их рассмотреть, создадим себе массив A:

        A = np.arange(10)
    

Поддерживаются классические срезы, как одномерных, так и двумерных массивов. Для демонстрации срезов двумерного массива, применим к массиву A метод reshape, чтобы превратить ее в матрицу с пятью строками и двумя столбцами:

Классические python срезы
Классические python срезы
Классические python срезы
Классические python срезы

Для сортировки можно использовать логические операции. Выведем четные элементы списка от нуля до девяти. Для этого вместо индексов можно использовать логические выражения. Для связки выражений используются функции np.logical_and() и np.logical_or().

        A[A % 2 == 0] # выведет все четные элементы массива A
A[np.logical_and(A != 5, A != 0)] # выведет все элементы массива A, которые не равны нулю и пяти

    
Сортировка элементов с помощью логических операций
Сортировка элементов с помощью логических операций

Pandas

Pandas – очень популярная библиотека для обработки данных на python. Работает на основе numpy и предоставляет специальные структуры данных для манипулирования с таблицами и временны́ми рядами. Мы с вами уже установили этот модуль, осталось импортировать его в блокнот.
        import os
import pandas as pd

    

И, для интереса, попробуем поработать с pandas на примере классического датасета Titanic из соревнований kaggle. Датасет представляет собой таблицу, поставляемую формате *.csv. Для его чтения pandas предоставляет метод, который преобразует таблицу в формат данных pandas – dataframe.

Загрузим датасет с помощью команды:

        os.system('wget -O titanic.csv https://www.dropbox.com/s/1qtgllk0d89bt3d/titanic.csv?dl=1')
    

Пользователи системы Windows могут просто кликнуть по ссылке и сохранить скачанный файл в папку с рабочим блокнотом.

В результате, в папке с блокнотом будет создан файл titanic.csv. Откроем его с помощью pandas.

        data = pd.read_csv('titanic.csv')
    

Переменная data представляет собой датафрейм pandas и по сути своей является таблицей с данными.

Чтобы оценить таблицу, узнать ее структуру можно использовать метод head(n=5), возвращающий первые n строк датафрейма.

        data.head()
    
Метод head()
Метод head()

Для получения имен колонок существует свойство columns.

        data.columns
    
Имена колонок датафрейма
Имена колонок датафрейма

Индексация датафреймов аналогична стандартной индексации списков в python и индексации numpy.

Индексация датафреймов
Индексация датафреймов

Помимо такого способа, pandas предоставляет два метода индексации:

  • iloc для индексации по номера столбцов и строк
  • loc для индексации по строковым значением строк и названиям столбцов.

Например, есть задача получить пол пассажиров для строк с первой по седьмую. Используем loc следующий образом:

        data.loc[1:7, 'sex']
    

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

Использование loc
Использование loc

iloc работает аналогичным образом, но только с номерами строк и столбцов, а не с их именами.

Получим для примера первые два столбца первых десяти строк.

        data.iloc[:10, :2]
    
Использование iloc
Использование iloc

Есть возможность обращаться по имени к каждому конкретному столбцу. Получим первые пять имен пассажиров нашей таблицы:

        data['name'].head()
    
Первые пять имен датафрейма
Первые пять имен датафрейма

При индексации можно строить логические запросы к данным датафрефма. Для примера выведем пять первых женщин пассажиров.

        data[(data['sex'] == 'female')]['name'].head()
    
Первые пять женщин датафрейма
Первые пять женщин датафрейма

Логика работы следующая: получаем строки таблицы, в которых колонка sex = female. Выбираем только колонку name и с помощью метода head() получаем верхние пять строк.

Усложним пример. Получим датафрейм, содержащий первые пять мужчин или женщин старше 50 лет.

        data[(data['sex'] == 'women') & (data['age'] > 50) | (data['sex'] == 'male')].head()
    
Результаты выполнения запроса
Результаты выполнения запроса

Связка условий происходит с помощью логических операторов И (&) и ИЛИ (|).

Изменение датафреймов Pandas

Пусть нам необходимо изменить название колонки. Pandas позволяет сделать это одной левой с помощью метода rename(). Переименуем колонку name в Name. В параметр columns передаем словарь, в котором указываются пары старое_имя:новое_имя колонок. Параметр inplace=True заставляет Pandas менять текущий датафрейм, а не создавать новый.

        data.rename(columns={'name':'Name'}, inplace=True)
    
Переименование колонки
Переименование колонки

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

Функция получения фамилии выглядит следующим образом:

        def get_last_name(name):
    return name.split(',')[0].strip()
    
Функция выделения фамилии из полного имени и ее тест
Функция выделения фамилии из полного имени и ее тест

Теперь, чтобы применить ее ко всем элементам датафрейма, используется метод apply(), в которую передается объект функции. Например:

        last_names = data['Name'].apply(get_last_name)
    
Использование метода apply
Использование метода apply

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

Добавление нового столбца
Добавление нового столбца

Чтобы удалить столбец, используется метод drop(). Удалим только что добавленный столбец фамилий. Параметр axis необходим, чтобы явно указать pandas работать со столбцами. Попробуйте поменять его на ноль и посмотреть, что произойдет.

        data.drop('Last name', axis=1, inplace=True)
    
Удаление столбца
Удаление столбца

Очень часто датасеты поставляются неполными. Это значит, что в некоторых ячейках таблицы может не быть данных. В машинном обучении стараются или избавиться от таких строк данных, или как-то их заполнить. Для получения пустых строк используется метод isnull(), а для получения непустых строк – метод notnull(). Работает достаточно интуитивно.

        data['boat'].isnull().head() # первые пять пустых ячеек столбца boat
data[data['boat'].notnull()].head() # первые пять непустых ячеек столбца boat

    
Работа с методами isnull() и notnull()
Работа с методами isnull() и notnull()

Если вы работали с SQL, то вам наверняка знаком метод GROUP BY. Он позволяет группировать данные по какому-то заданному критерию. Pandas обладает такими же возможностями. Для этого используется метод groupby().

Сгруппируем пассажиров по полу в зависимости от класса каюты и посчитаем их количество:

        data.groupby('sex')['pclass'].value_counts()
    
Количество пассажиров в зависимости от класса каюты и пола
Количество пассажиров в зависимости от класса каюты и пола

Pandas содержит набор статистических методов, которые позволяют найти среднее значение, максимум, минимум, стандартное отклонение какого-либо набора данных из датафрейма. Кроме того, для быстрой оценки всех популярных параметров используется метод describe(). Рассчитаем все эти показатели для стоимости проезда пассажиров по разным классам:

        data.groupby('pclass')['fare'].describe()
    
Результат работы метода describe()
Результат работы метода describe()

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

Эти же функции доступны отдельно. Например, определим в каком соотношении выжили мужчины и женщины:

        data.groupby('sex')['survived'].mean()
    

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

        data.to_csv('titanic_2.csv', index=False)
    

Сохраняем датафрейм в файл csv с именем titanic_2.

Основы matplotlib

Matplotlib – библиотека для визуализации данных. Подключим библиотеку.

        %matplotlib inline
import matplotlib.pyplot as plt

    

Jupyter Notebook поддерживает вывод графиков “на месте”. Для активации этого режима используется магическая команда %matplotlib inline.

Для построение графиков, используется объект plt, который был импортирован из пакета matplotlib.

Первым этапом построения графика является создание холста. Делается это следующим образом. Параметр figsize определяет размер холста в дюймах. Параметры передаются в виде кортежа. Числа могут быть дробными.

        fig = plt.figure(figsize=(10, 6))
    

После этого добавляет оси, строим кривые и наносим легенду на холст.

        fig = plt.figure(figsize=(10, 6))

axes = fig.add_axes([0,0,1,1]) # создаем прямоуголник для построение графиков в виде списка [left, bottom, width, height]

axes.plot(x, x**2, 'r') # добавить первую кривую на холст красного цвета
axes.plot(x, x**3, 'b*--') # добавить вторую крикую на холст синего цвета с маркерами другого типа

axes.set_xlabel('x') # добавить название оси Х
axes.set_ylabel('y') # добавить название оси Y
axes.set_title('Hello proglib') # добавить название всего графика
axes.legend([r'$x^2$', r'$x^3$'], loc=0) # добавить легенду

plt.show() # вывести график на экран

    
Результат построение двух графиков
Результат построение двух графиков

Пакет позволяет строить семейства графиков.

1. График в графике. Схема состроения абсолютно такая же, как в предудыщем примере

        fig = plt.figure()

axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # основной график
axes2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # внутренный график. Его размер меньше, цифры показывают долю от figsize

# Основной график
axes1.plot(x, x**2, 'r')
axes1.set_xlabel('x')
axes1.set_ylabel('y')
axes1.set_title('Я внешний график')

# Вложенный график
axes2.plot(x**2, x, 'b')
axes2.set_xlabel('y')
axes2.set_ylabel('x')
axes2.set_title('Я внутренний график')

plt.show()

    
Построение графика в графике
Построение графика в графике

2. Семейство графиков. В этом случае, холст создается методом subplots(), параметром которого является кортеж чисел, сколько графиков по-горизонтали и по-вертикали он включает в себя. Построим одну строку из трех графиков.

        fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(16, 5))

for pow, ax in enumerate(axes):
    ax.plot(x, x**(pow + 1), 'b')
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_title(f'$y = x^{pow + 1}$', fontsize=18)
fig.tight_layout() # автоматически вписывает все графики в размер холста

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

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

        fig = plt.figure()
axes = fig.add_axes([0.0, 0.0, 1.0, 1.0])
bins = 20 #  количество столбцов
index = np.arange(bins) # создаем список от 0 до bins - 1 
axes.hist(data[data['sex'] == 'male']['age'].dropna(), bins=bins, alpha=0.6, label='Мужчины') # добавляем на холст гистограмму распределения возрастов среди мужчин
axes.hist(data[data['sex'] == 'female']['age'].dropna(), bins=bins, alpha=0.6, label='Женщины') # добавляем на холст гистограмму распределения возрастов среди женщин

axes.legend() # строим легенду
axes.set_xlabel('Возраст', fontsize=18) 
axes.set_ylabel('Количество', fontsize=18)
axes.set_title('Распределение возрастов по полу человека', fontsize=18)

plt.show()

    
Гистограмма распределение возрастов по полам
Гистограмма распределение возрастов по полам
***

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

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ