逻辑回归真假新闻分类:Python 图形用户界面

本程序使用 Python 和 PySide6 构建了一个逻辑回归真假新闻分类的图形用户界面,允许用户加载数据、预处理数据、可视化数据、训练模型、预测和评估结果。

导入所需模块

import pathlib  # 路径操作模块
import time  # 时间模块
import traceback  # 异常跟踪模块
from typing import Optional, Callable, Generator, Union  # 类型提示模块
import pandas as pd  # pandas数据处理模块
from PySide6.QtCore import QAbstractTableModel, Qt, QRunnable, Signal, QThreadPool, QThread, QTimer  # PySide6模块
from PySide6.QtGui import QPixmap, QIcon  # PySide6图形用户界面模块
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QFileDialog, QTableView, QStackedWidget, \
    QVBoxLayout, QScrollArea, QWidget, QHeaderView, QMessageBox, QDialog  # PySide6图形用户界面模块
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas  # Matplotlib绘图模块
from matplotlib.figure import Figure  # Matplotlib绘图模块
from ui_windows import Ui_MainWindow  # 导入UI文件
import main  # 导入主程序


# 定义数据表模型,将表中数据显示
class TableModel(QAbstractTableModel):
    def __init__(self, data: pd.DataFrame):
        super().__init__()
        self._data = data

    def rowCount(self, parent=None):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1]

    def data(self, index, role=None):
        if role == Qt.DisplayRole:
            return str(self._data.iloc[index.row(), index.column()])
        return None

    def headerData(self, section, orientation, role=None):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._data.columns[section])
            else:
                return str(self._data.index[section])
        return None

    def sort(self, column, order=None):
        self.layoutAboutToBeChanged.emit()
        self._data.sort_values(self._data.columns[column], ascending=order == Qt.AscendingOrder, inplace=True)
        self.layoutChanged.emit()

    @property
    def df(self):
        return self._data

# 定义数据表格控件
class TableWidget(QTableView):
    def __init__(self, data: Optional[pd.DataFrame] = None):
        super().__init__()
        if data is not None:
            self.setModel(TableModel(data))

# 定义任务,返回指令
class Task:
    def __init__(
            self,
            func: Callable[..., Generator[Union[str, pd.DataFrame], None, None]],
            *args,
            **kwargs
    ):
        self._func = func
        self._args = args
        self._kwargs = kwargs

    def __call__(self):
        try:
            yield from self._func(*self._args, **self._kwargs)
        except Exception as e:
            traceback.print_exc()
            yield str(e)

# 定义工作线程,工作状态汇报
class Worker(QThread):
    resulted = Signal(pd.DataFrame)
    step = Signal(str)
    image = Signal(pathlib.Path)
    wrapper = Signal(list)
    stop = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self._task = None

    def bind_task(self, func, *args, **kwargs):
        self._task = Task(func, *args, **kwargs)

    def run(self):
        if self._task is None:
            raise ValueError('Task not bind')
        print('开始执行')
        for value in self._task():
            if isinstance(value, pd.DataFrame):
                self.resulted.emit(value)
            elif isinstance(value, str):
                print('收到', value)
                self.step.emit(value)
                time.sleep(0.1)
            elif isinstance(value, pathlib.Path):
                print('图片', value)
                self.image.emit(value)
            elif isinstance(value, list):
                self.wrapper.emit(value)
        self.stop.emit()

# 定义Matplotlib可视化图控件
class MatplotlibWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        layout = QVBoxLayout(self)
        layout.addWidget(self.canvas)
        self.setLayout(layout)

    def draw(self, data: pd.DataFrame):
        pass

# 定义后台Matplotlib绘图工作线程
class DrawWorker(QRunnable):
    def __init__(self, func, *args, **kwargs):
        super().__init__()
        self._func = func
        self._args = args
        self._kwargs = kwargs

    def run(self):
        self._func(*self._args, **self._kwargs)

# 定义图片控件,实现图片显示
class ImageWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.label = QLabel()
        self.label.setAlignment(Qt.AlignCenter)
        layout = QVBoxLayout(self)
        layout.addWidget(self.label)
        self.setLayout(layout)
        self.setObjectName('#image')
        self.setStyleSheet(
            '''
            #image {
                background-color: white;
                border: 1px solid black;
                border-radius: 5px;
            }
            '''
        )

    def draw(self, path: pathlib.Path):
        max_width = 1200
        pix = QPixmap(str(path))
        if pix.width() > max_width:
            pix = pix.scaledToWidth(max_width, Qt.SmoothTransformation)
        self.label.setPixmap(
            pix
        )
        self.setFixedSize(
            pix.width() + 2, pix.height() + 2
        )

# 定义提示对话框
class TipsDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle('Tips')
        self.resize(300, 200)
        self.setLayout(QVBoxLayout())
        self.label = QLabel('正在处理数据,请稍后……')
        self.layout().addWidget(self.label)

    def update_text(self, text):
        self.label.setText(text)

# 定义主窗口
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.setWindowTitle('逻辑回归真假新闻分类')
        self.setWindowIcon(
            QIcon(str(pathlib.Path(__file__).parents[1] / 'ui/icon.png'))
        )

        self.stack = QStackedWidget()
        self.widget.setLayout(QVBoxLayout())
        self.widget.layout().addWidget(self.stack)
        self.thread_pool = QThreadPool(self)

        self.table = TableWidget()
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.stack.addWidget(self.table)

        self.area = QScrollArea()
        self.area.setWidgetResizable(True)
        self.container = QWidget()
        self.area.setWidget(self.container)
        self.container.setLayout(QVBoxLayout())
        self.stack.addWidget(self.area)

        self.checkBox.setChecked(True)
        self.checkBox_2.setChecked(True)
        self.checkBox_3.setChecked(True)
        self.checkBox.setEnabled(False)
        self.checkBox_2.setEnabled(False)
        self.checkBox_3.setEnabled(False)

        self.worker = Worker()
        self.worker.resulted.connect(self.update_table)
        self.worker.step.connect(self.update_step)
        self.worker.stop.connect(self.end)
        self.worker.image.connect(self.update_image)
        self.worker.wrapper.connect(self.update_wrapper)
        self.widget_2.setFixedWidth(300)
        self.btn_load.clicked.connect(self.load_file)
        self._model: Optional[TableModel] = None

        self.btn_data_pre.clicked.connect(self.process_data)
        self.btn_chart.clicked.connect(self.draw)
        self.btn_chart.setEnabled(False)

        self.horizontalSlider.valueChanged.connect(self.update_slider)
        self.horizontalSlider.setValue(80)
        self.btn_train.clicked.connect(self.train)
        self.btn_train.setEnabled(False)
        self.btn_pre.clicked.connect(self.predict)
        self.btn_pre.setEnabled(False)
        self.pushButton.clicked.connect(self.evaluate)
        self.pushButton.setEnabled(False)

    # 加载文件
    def load_file(self):
        filename, _ = QFileDialog.getOpenFileName(self, 'Open file', '', 'CSV files (*.csv)')
        if filename:
            self._reset_status(True)
            self.statusBar().showMessage(filename)
            data = main.load_data(filename)
            self._model = TableModel(data)
            self.table.setModel(self._model)

    # 重置状态
    def _reset_status(self, status: bool = True):
        self.btn_data_pre.setEnabled(status)

    def update_step(self, step: str):
        self.statusBar().showMessage(step)

    def update_table(self, data: pd.DataFrame):
        self._model = TableModel(data)
        self.table.setModel(self._model)

    def update_wrapper(self, wrapper):
        print(wrapper)
        text = '\n'.join(wrapper)
        tip = TipsDialog(self)
        tip.setWindowTitle('评估结果')
        tip.update_text(text)
        tip.exec()

    def update_slider(self, value):
        # 显示划分比例
        text = round(value / 100, 1)
        self.label_split.setText(
            f'训练集比例:{text}\n测试集比例:{round(1 - text, 1)}'
        )

    def process_data(self):
        if not self._model:
            self.tips('请先加载数据')
            return
        try:
            self.btn_data_pre.setEnabled(False)
            self.worker.bind_task(main.data_process)
            self.worker.start()
        except Exception as e:
            traceback.print_exc()

    def end(self):
        # self.statusBar().showMessage('处理完成')
        self.btn_chart.setEnabled(True)
        self.btn_train.setEnabled(True)
        self.tips('运行完成')

    def tips(self, message):
        QMessageBox.information(self, '提示', message)  # 弹出提示框,标题为“提示”,内容为message

    def update_image(self, image):
        self.stack.setCurrentIndex(1)
        img = ImageWidget(self)
        img.draw(image)
        self.container.layout().addWidget(img)
        # 滚动到底部
        QTimer.singleShot(5, self.scroll_bottom)

    def scroll_bottom(self):
        self.area.verticalScrollBar().setValue(
            self.area.verticalScrollBar().maximum()
        )

    def draw(self):
        if not self._model:
            self.tips('请先加载数据')
            return
        self.statusBar().showMessage('正在绘制,请稍后……')
        self.worker.bind_task(main.data_visualization)
        self.worker.start()

    def train(self):
        if not self._model:
            self.tips('请先加载数据')
            return
        self.statusBar().showMessage('正在训练,请稍后……')
        self.worker.bind_task(main.train, round(1 - self.horizontalSlider.value() / 100, 1))
        self.worker.start()
        self.btn_pre.setEnabled(True)

    def predict(self):
        self.statusBar().showMessage('正在训练,请稍后……')
        self.worker.bind_task(main.predict)
        self.worker.start()
        self.pushButton.setEnabled(True)

    def evaluate(self):
        self.statusBar().showMessage('正在训练,请稍后……')
        self.worker.bind_task(main.evaluate)
        self.worker.start()


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()

程序说明

这是一个Python程序,用于构建一个逻辑回归真假新闻分类的图形用户界面。程序分为几个部分:

  1. 导入所需模块:程序中使用了许多Python模块,包括路径操作模块、时间模块、异常跟踪模块、类型提示模块、pandas数据处理模块、PySide6图形用户界面模块、Matplotlib绘图模块等。

  2. 定义数据表模型:由于程序需要显示数据表格,因此定义了一个TableModel类,该类继承自QAbstractTableModel,用于将表中数据显示出来。

  3. 定义数据表格控件:定义了一个TableWidget类,该类继承自QTableView,用于显示数据表格控件。

  4. 定义任务:定义了一个Task类,用于返回指令。

  5. 定义工作线程:定义了一个Worker类,继承自QThread,用于执行工作,并在执行过程中汇报工作状态。

  6. 定义Matplotlib可视化图控件:定义了一个MatplotlibWidget类,用于显示Matplotlib可视化图控件。

  7. 定义后台Matplotlib绘图工作线程:定义了一个DrawWorker类,用于在后台执行Matplotlib绘图工作。

  8. 定义图片控件:定义了一个ImageWidget类,用于显示图片控件。

  9. 定义提示对话框:定义了一个TipsDialog类,用于弹出提示对话框。

  10. 定义主窗口:定义了一个MainWindow类,用于构建整个图形用户界面。

  11. 程序入口:在程序入口处,实例化了一个QApplication对象,然后创建MainWindow对象并显示出来,最后通过app.exec()启动程序。

逻辑回归真假新闻分类:Python 图形用户界面

原文地址: https://www.cveoy.top/t/topic/oDI1 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录