从 Pandas 数据框填充 QTableView 的最快方法
Fastest way to populate QTableView from Pandas data frame
我是 PyQt 的新手,我正在努力填充 QTableView 控件。
我的代码如下:
def data_frame_to_ui(self, data_frame):
"""
Displays a pandas data frame into the GUI
"""
list_model = QtGui.QStandardItemModel()
i = 0
for val in data_frame.columns:
# for the list model
if i > 0:
item = QtGui.QStandardItem(val)
#item.setCheckable(True)
item.setEditable(False)
list_model.appendRow(item)
i += 1
self.ui.profilesListView.setModel(list_model)
# for the table model
table_model = QtGui.QStandardItemModel()
# set table headers
table_model.setColumnCount(data_frame.columns.size)
table_model.setHorizontalHeaderLabels(data_frame.columns.tolist())
self.ui.profileTableView.horizontalHeader().setStretchLastSection(True)
# fill table model data
for row_idx in range(10): #len(data_frame.values)
row = list()
for col_idx in range(data_frame.columns.size):
val = QtGui.QStandardItem(str(data_frame.values[row_idx][col_idx]))
row.append(val)
table_model.appendRow(row)
# set table model to table object
self.ui.profileTableView.setModel(table_model)
实际上在代码中我成功填充了一个QListView,但是我设置给QTableView的值没有显示,你也可以看到我将行截断为10因为它需要永远显示数百行数据框。
那么,从 pandas 数据框填充 table 模型的最快方法是什么?
提前致谢。
就我个人而言,我会创建自己的模型 class 以便于处理它。
例如:
import sys
from PyQt4 import QtCore, QtGui
Qt = QtCore.Qt
class PandasModel(QtCore.QAbstractTableModel):
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole:
return QtCore.QVariant(str(
self._data.iloc[index.row()][index.column()]))
return QtCore.QVariant()
if __name__ == '__main__':
application = QtGui.QApplication(sys.argv)
view = QtGui.QTableView()
model = PandasModel(your_pandas_data)
view.setModel(model)
view.show()
sys.exit(application.exec_())
这个有效:
class PandasModel(QtCore.QAbstractTableModel):
"""
Class to populate a table view with a pandas dataframe
"""
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole:
return str(self._data.iloc[index.row()][index.column()])
return None
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[col]
return None
这样使用:
model = PandasModel(your_pandas_data_frame)
your_tableview.setModel(model)
我阅读 here 以避免从 PyQT 4.6 开始 QVariant()
。
pandas
中实际上有一些代码支持与 Qt 的集成。
在撰写此答案时,最新的 pandas 版本是 0.18.1
,您可以:
from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget
该代码似乎与 PySide 耦合,但使其与 PyQt 一起工作应该相对简单。此外,该代码已被弃用,警告说该模块将在未来被删除。
幸运的是,他们将其提取到 GitHub 中的一个单独项目中,名为 pandas-qt
:
https://github.com/datalyze-solutions/pandas-qt
在尝试推出我自己的模型和视图实现之前,我会尝试使用它。
我发现所有建议的答案对于 1000 多行的 DataFrame 来说都非常慢。什么对我来说非常快:
class PandasModel(QtCore.QAbstractTableModel):
"""
Class to populate a table view with a pandas dataframe
"""
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
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=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
return None
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[col]
return None
除了使用QtCore.QAbstractTableModel,还可以继承QtGui.QStandardItemModel。我发现这种方式更容易支持从 QTableView 发出的 handleChanged 事件。
from PyQt5 import QtCore, QtGui
class PandasModel(QtGui.QStandardItemModel):
def __init__(self, data, parent=None):
QtGui.QStandardItemModel.__init__(self, parent)
self._data = data
for row in data.values.tolist():
data_row = [ QtGui.QStandardItem("{0:.6f}".format(x)) for x in row ]
self.appendRow(data_row)
return
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def headerData(self, x, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[x]
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return self._data.index[x]
return None
将数据帧写入 QtableWidget 的简单且快速的方法
# Takes a df and writes it to a qtable provided. df headers become qtable headers
@staticmethod
def write_df_to_qtable(df,table):
headers = list(df)
table.setRowCount(df.shape[0])
table.setColumnCount(df.shape[1])
table.setHorizontalHeaderLabels(headers)
# getting data from df is computationally costly so convert it to array first
df_array = df.values
for row in range(df.shape[0]):
for col in range(df.shape[1]):
table.setItem(row, col, QtGui.QTableWidgetItem(str(df_array[row,col])))
这里是copy-paste PyQT5 的完整工作示例
基于@Frederick Li 的回答稍作修改。
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import Qt
import sys
import pandas as pd
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.centralwidget = QtWidgets.QWidget(self)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
self.centralwidget.setSizePolicy(sizePolicy)
self.pdtable = QtWidgets.QTableView(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
self.pdtable.setSizePolicy(sizePolicy)
dataPD = [['tom', 10.0, 180.3], ['nick', 15.0, 175.7], ['juli', 14.0, 160.6]]
df = pd.DataFrame(dataPD, columns=['Name', 'Age', 'Height'])
print(df.dtypes)
self.model = PandasTableModel(df)
self.pdtable.setModel(self.model)
self.setCentralWidget(self.centralwidget)
class PandasTableModel(QtGui.QStandardItemModel):
def __init__(self, data, parent=None):
QtGui.QStandardItemModel.__init__(self, parent)
self._data = data
for col in data.columns:
data_col = [QtGui.QStandardItem("{}".format(x)) for x in data[col].values]
self.appendColumn(data_col)
return
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def headerData(self, x, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[x]
if orientation == Qt.Vertical and role == Qt.DisplayRole:
return self._data.index[x]
return None
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
main = MainWindow()
main.show()
main.resize(600, 400)
sys.exit(app.exec_())
我是 PyQt 的新手,我正在努力填充 QTableView 控件。
我的代码如下:
def data_frame_to_ui(self, data_frame):
"""
Displays a pandas data frame into the GUI
"""
list_model = QtGui.QStandardItemModel()
i = 0
for val in data_frame.columns:
# for the list model
if i > 0:
item = QtGui.QStandardItem(val)
#item.setCheckable(True)
item.setEditable(False)
list_model.appendRow(item)
i += 1
self.ui.profilesListView.setModel(list_model)
# for the table model
table_model = QtGui.QStandardItemModel()
# set table headers
table_model.setColumnCount(data_frame.columns.size)
table_model.setHorizontalHeaderLabels(data_frame.columns.tolist())
self.ui.profileTableView.horizontalHeader().setStretchLastSection(True)
# fill table model data
for row_idx in range(10): #len(data_frame.values)
row = list()
for col_idx in range(data_frame.columns.size):
val = QtGui.QStandardItem(str(data_frame.values[row_idx][col_idx]))
row.append(val)
table_model.appendRow(row)
# set table model to table object
self.ui.profileTableView.setModel(table_model)
实际上在代码中我成功填充了一个QListView,但是我设置给QTableView的值没有显示,你也可以看到我将行截断为10因为它需要永远显示数百行数据框。
那么,从 pandas 数据框填充 table 模型的最快方法是什么?
提前致谢。
就我个人而言,我会创建自己的模型 class 以便于处理它。
例如:
import sys
from PyQt4 import QtCore, QtGui
Qt = QtCore.Qt
class PandasModel(QtCore.QAbstractTableModel):
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole:
return QtCore.QVariant(str(
self._data.iloc[index.row()][index.column()]))
return QtCore.QVariant()
if __name__ == '__main__':
application = QtGui.QApplication(sys.argv)
view = QtGui.QTableView()
model = PandasModel(your_pandas_data)
view.setModel(model)
view.show()
sys.exit(application.exec_())
这个有效:
class PandasModel(QtCore.QAbstractTableModel):
"""
Class to populate a table view with a pandas dataframe
"""
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole:
return str(self._data.iloc[index.row()][index.column()])
return None
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[col]
return None
这样使用:
model = PandasModel(your_pandas_data_frame)
your_tableview.setModel(model)
我阅读 here 以避免从 PyQT 4.6 开始 QVariant()
。
pandas
中实际上有一些代码支持与 Qt 的集成。
在撰写此答案时,最新的 pandas 版本是 0.18.1
,您可以:
from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget
该代码似乎与 PySide 耦合,但使其与 PyQt 一起工作应该相对简单。此外,该代码已被弃用,警告说该模块将在未来被删除。
幸运的是,他们将其提取到 GitHub 中的一个单独项目中,名为 pandas-qt
:
https://github.com/datalyze-solutions/pandas-qt
在尝试推出我自己的模型和视图实现之前,我会尝试使用它。
我发现所有建议的答案对于 1000 多行的 DataFrame 来说都非常慢。什么对我来说非常快:
class PandasModel(QtCore.QAbstractTableModel):
"""
Class to populate a table view with a pandas dataframe
"""
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
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=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
return None
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[col]
return None
除了使用QtCore.QAbstractTableModel,还可以继承QtGui.QStandardItemModel。我发现这种方式更容易支持从 QTableView 发出的 handleChanged 事件。
from PyQt5 import QtCore, QtGui
class PandasModel(QtGui.QStandardItemModel):
def __init__(self, data, parent=None):
QtGui.QStandardItemModel.__init__(self, parent)
self._data = data
for row in data.values.tolist():
data_row = [ QtGui.QStandardItem("{0:.6f}".format(x)) for x in row ]
self.appendRow(data_row)
return
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def headerData(self, x, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[x]
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return self._data.index[x]
return None
将数据帧写入 QtableWidget 的简单且快速的方法
# Takes a df and writes it to a qtable provided. df headers become qtable headers
@staticmethod
def write_df_to_qtable(df,table):
headers = list(df)
table.setRowCount(df.shape[0])
table.setColumnCount(df.shape[1])
table.setHorizontalHeaderLabels(headers)
# getting data from df is computationally costly so convert it to array first
df_array = df.values
for row in range(df.shape[0]):
for col in range(df.shape[1]):
table.setItem(row, col, QtGui.QTableWidgetItem(str(df_array[row,col])))
这里是copy-paste PyQT5 的完整工作示例 基于@Frederick Li 的回答稍作修改。
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import Qt
import sys
import pandas as pd
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.centralwidget = QtWidgets.QWidget(self)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
self.centralwidget.setSizePolicy(sizePolicy)
self.pdtable = QtWidgets.QTableView(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
self.pdtable.setSizePolicy(sizePolicy)
dataPD = [['tom', 10.0, 180.3], ['nick', 15.0, 175.7], ['juli', 14.0, 160.6]]
df = pd.DataFrame(dataPD, columns=['Name', 'Age', 'Height'])
print(df.dtypes)
self.model = PandasTableModel(df)
self.pdtable.setModel(self.model)
self.setCentralWidget(self.centralwidget)
class PandasTableModel(QtGui.QStandardItemModel):
def __init__(self, data, parent=None):
QtGui.QStandardItemModel.__init__(self, parent)
self._data = data
for col in data.columns:
data_col = [QtGui.QStandardItem("{}".format(x)) for x in data[col].values]
self.appendColumn(data_col)
return
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def headerData(self, x, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[x]
if orientation == Qt.Vertical and role == Qt.DisplayRole:
return self._data.index[x]
return None
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
main = MainWindow()
main.show()
main.resize(600, 400)
sys.exit(app.exec_())