Использование PyQt5 для создания графического интерфейса для панели инструментов LendingClub

Это вторая часть серии из двух частей. Чтобы ознакомиться с первой статьей, нажмите здесь.

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

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

Выбор фреймворка PyQt5

PyQt5 — это набор привязок Python (способ вызова функций C из Python), который позволяет нам создавать GUI (графический пользовательский интерфейс), реализуя библиотеку Qt на C++, используя только Python, позволяя разработчикам создавать приложения из довольно легко измельчается. Я также играл с Kivy и Tkinter, но PyQt5, похоже, лучше всего соответствовал моим потребностям. Tkinter — это довольно стандартная библиотека графического интерфейса, но она немного глючит на Mac, а Kivy отлично подходит для разработки мобильных приложений, но менее эффективен для настольных приложений. В конце концов, я выбрал PyQt5 из-за его интуитивности и гибкости. Если вы знакомы с созданием и определением классов, а также с общими принципами объектно-ориентированного программирования, вам будет очень легко разобраться.

Я активно использовал учебник YouTube с канала программирования Tech With Tim, чтобы изучить основы этой библиотеки и фреймворка. Вы можете найти его серию видео здесь.

Проектирование интерфейса с помощью Qt Designer

Одной из моих любимых функций фреймворка Qt является возможность вручную размещать окна с помощью инструмента под названием Qt Designer. Designer позволяет перетаскивать различные элементы (т. е. кнопки, метки, ползунки и т. д.) в образец окна, поэтому вы можете разработать визуально привлекательную программу, не возясь с координатами x/y или размерами в коде.

Если у вас есть макет, который вам нравится, вы можете сохранить его как файл .ui. Оттуда мы можем использовать команду pyuic5 в командной строке для генерации кода Python из нашего файла .ui. Убедившись, что вы находитесь в правильном рабочем каталоге, просто введите следующее в выбранном вами интерфейсе командной строки:

pyuic5 -x saved_file.ui -o new_file.py

Часть «-x» команды добавляет фрагмент кода Python, который выполняет и отображает программу, когда мы запускаем результирующий скрипт Python. Команда «-o» указывает имя выходного файла.

Сделав это, вы можете открыть этот файл в своем любимом текстовом редакторе и создать работающее приложение!

Вы можете скачать Qt Designer для Mac или Windows здесь. В Windows вы также можете использовать:

pip install pyqt5-tools

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

Добавление функции в интерфейс

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

Эта часть, очевидно, будет варьироваться в зависимости от приложения, которое вы создаете, но я опишу основные шаги, которые я предпринял для создания моей программы Lending Club.

Заполнение приборной панели

В первой части я описал шаги по загрузке оперативных данных из Lending Club и использованию модели для прогнозирования вероятности погашения. После этого мы хотим отобразить информацию о кредите вместе с нашими прогнозами на нашей панели инструментов.

Наш автоматически сгенерированный код создает класс (мой называется Ui_Form), затем метод с именем setupUi. Наше главное окно будет экземпляром этого класса, и, как вы, вероятно, догадались, setupUi() создаст и настроит этот экземпляр при запуске скрипта. Код для создания нашей таблицы, или tableWidget, пишется для нас автоматически, как и установка измерения столбца. Количество строк устанавливается с помощью следующего кода (отображение — это кадр данных, который мы хотим отобразить):

self.tableWidget.setRowCount(display.shape[0])

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

for r in range(display.shape[0]):
    for c in range(7):
        self.tableWidget.setItem(
                                 r,c,
                                 QtWidgets.QTableWidgetItem(
                                     str(display.iloc[r,c]))
                                 )

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

Добавление фильтров

Я хотел, чтобы пользователи могли сузить варианты кредита, которые они просматривали, в соответствии со своими личными предпочтениями. Во-первых, я добавил флажки для различных классов кредитов (Lending Club оценивает все свои кредиты по шкале от A до G). Моя программа автоматически устанавливает флажки для каждой оценки при запуске программы, а затем подключается к методу класса под названием «перерисовка», который будет определен следующим при каждом щелчке по нему.

self.a_box.setCheckState(2)  # A state of 2 means the box is checked
self.a_box.clicked.connect(self.redraw)

Функция .connect() связывает событие (в данном случае self.a_box.clicked или изменение состояния блока A) с методом. Это основной метод, с помощью которого мы оживляем наш интерфейс. Предыдущий код повторяется для каждого флажка, за исключением поля «Все». Этот блок связан с отдельной функцией, которая устанавливает или снимает все флажки (в зависимости от состояния), а затем вызывает метод перерисовки.

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

self.min_int.setMinimum(min(display_raw.intRate)-1)
self.min_int.setMaximum(max(display_raw.intRate)+1)
self.min_int.sliderMoved.connect(self.redraw)
self.max_int.setMinimum(min(display_raw.intRate)-1)
self.max_int.setMaximum(max(display_raw.intRate)+1)
self.max_int.setValue(max(display_raw.intRate)+1)
self.max_int.sliderMoved.connect(self.redraw)

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

Функция перерисовки

Функция перерисовки вызывается при каждом изменении фильтра. По сути, он делает то, на что это похоже. Сначала создайте список отфильтрованных оценок кредита. Метод checkState() имеет значение 2 всякий раз, когда установлен флажок, поэтому disp_rows представляет собой список категорий займов, которые пользователь хочет вернуть. Затем создается объект newdf, подмножество фрейма данных кредитов, отражающее состояние наших фильтров. Наконец, таблица заполняется так, как она была изначально.

def redraw(self):
disp_rows = [(self.a_box.checkState() == 2) * 'A', 
                    (self.b_box.checkState() == 2) * 'B',
                    (self.c_box.checkState() == 2) * 'C',
                    (self.d_box.checkState() == 2) * 'D',
                    (self.e_box.checkState() == 2) * 'E',
                    (self.f_box.checkState() == 2) * 'F',
                    (self.g_box.checkState() == 2) * 'G',
                    ]
newdf = display[[x in disp_rows for x in display.grade] & 
                    (display_raw['intRate']>=self.min_int.value()) & 
                    (display_raw['intRate']<=self.max_int.value()) &
                    (display_raw['EV'] >= self.min_ev.value()) &
                    (display_raw['EV'] <= self.max_ev.value())
                    ]
    
    self.tableWidget.setRowCount(newdf.shape[0])
for r in range(newdf.shape[0]):
        for c in range(7):
            self.tableWidget.setItem(
                                     r,c,
                                     QtWidgets.QTableWidgetItem(
                                     str(newdf.iloc[r,c]))
                                     )

Экран входа

Последнее, что нужно сделать, это включить экран входа в систему. Помимо того, что он больше похож на «настоящее» приложение, экран входа в систему дает пользователю простой способ ввести свою регистрационную информацию (в данном случае ключ API для подключения к API Lending Club).

Для создания отдельного окна определяется новый класс (я снова использовал Qt Designer). Это новое окно входа будет инициализировано при запуске программы, а главное окно будет создано позже.

Как только пользователь вводит свой ключ API, мы должны связать событие нажатия кнопки с функцией.

self.ok_button.clicked.connect(self.logging_in)

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

def logging_in(self):
    global apikey 
    apikey = ui.keybox.text()
    Form = QtWidgets.QDialog()
    uimain = Ui_Form()
    uimain.setupUi(Form)
    Form.show()
    Form.exec_()

Вывод

Прохождение этого процесса научило меня многому о жизненном цикле внедрения модели машинного обучения в производство, объектно-ориентированном программировании и разработке приложений в целом. Я едва поверхностно коснулся того, что можно сделать с помощью такой среды, как PyQt5. Если вам интересно узнать больше, ознакомьтесь с упомянутым ранее Учебником YouTube Тима (не мной), Документацией по PyQt5 или попробуйте создать приложение или проект самостоятельно!

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