La forma más rápida de poblar QTableView desde el dataframe de Pandas

Soy muy nuevo en PyQt y me cuesta rellenar un control QTableView.

Mi código es el siguiente:

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) 

En realidad, en el código tengo éxito al rellenar un QListView, pero los valores que establecí en QTableView no se muestran, también se puede ver que truncé las filas a 10 porque demora una eternidad en mostrar los cientos de filas del dataframe.

Entonces, ¿cuál es la forma más rápida de rellenar el modelo de tabla desde un dataframe de pandas?

Gracias por adelantado.

Personalmente, solo crearía mi propia clase de modelo para facilitar su manejo.

Por ejemplo:

 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.values[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_()) 

Esto funciona:

 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.values[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 

Usándolo así:

 model = PandasModel(your_pandas_data_frame) your_tableview.setModel(model) 

Leí aquí para evitar QVariant() desde PyQT 4.6.

He encontrado todas las respuestas propuestas dolorosamente lentas para DataFrames con más de 1000 filas. Lo que me funciona increíblemente rápido:

 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 

En realidad, hay algunos códigos en pandas admiten la integración con Qt.

Al momento de escribir esta respuesta, la última versión de los pandas es 0.18.1 y usted podría hacer:

 from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget 

Ese código parece estar acoplado a PySide, sin embargo, debería ser relativamente trivial para que funcione con PyQt. Además, ese código ha quedado en desuso y la advertencia dice que el módulo se eliminará en el futuro.

Afortunadamente, lo extrajeron en un proyecto separado en GitHub llamado pandas-qt :

https://github.com/datalyze-solutions/pandas-qt

Trataré de usar eso antes de intentar desplegar mi propio modelo y ver la implementación.

Además de usar QtCore.QAbstractTableModel, también se puede heredar de QtGui.QStandardItemModel. Me parece que es más fácil admitir el evento handleChanged emitido desde QTableView.

 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