diff --git a/doc/source/ecosystem.rst b/doc/source/ecosystem.rst
index 8fafe8ec9eaa2..0d010b47f393a 100644
--- a/doc/source/ecosystem.rst
+++ b/doc/source/ecosystem.rst
@@ -93,6 +93,12 @@ targets the IPython Notebook environment.
`Plotly’s `__ `Python API `__ enables interactive figures and web shareability. Maps, 2D, 3D, and live-streaming graphs are rendered with WebGL and `D3.js `__. The library supports plotting directly from a pandas DataFrame and cloud-based collaboration. Users of `matplotlib, ggplot for Python, and Seaborn `__ can convert figures into interactive web-based plots. Plots can be drawn in `IPython Notebooks `__ , edited with R or MATLAB, modified in a GUI, or embedded in apps and dashboards. Plotly is free for unlimited sharing, and has `cloud `__, `offline `__, or `on-premise `__ accounts for private use.
+`Pandas-Qt `__
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Spun off from the main pandas library, the `Pandas-Qt `__
+library enables DataFrame visualization and manipulation in PyQt4 and PySide applications.
+
.. _ecosystem.ide:
IDE
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index e5d659cc31606..d23e0ca59254d 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -110,78 +110,6 @@ details.
Visualizing Data in Qt applications
-----------------------------------
-.. warning::
-
- The ``qt`` support is **deprecated and will be removed in a future version**.
- We refer users to the external package `pandas-qt `_.
-
-There is experimental support for visualizing DataFrames in PyQt4 and PySide
-applications. At the moment you can display and edit the values of the cells
-in the DataFrame. Qt will take care of displaying just the portion of the
-DataFrame that is currently visible and the edits will be immediately saved to
-the underlying DataFrame
-
-To demonstrate this we will create a simple PySide application that will switch
-between two editable DataFrames. For this will use the ``DataFrameModel`` class
-that handles the access to the DataFrame, and the ``DataFrameWidget``, which is
-just a thin layer around the ``QTableView``.
-
-.. code-block:: python
-
- import numpy as np
- import pandas as pd
- from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget
- from PySide import QtGui, QtCore
-
- # Or if you use PyQt4:
- # from PyQt4 import QtGui, QtCore
-
- class MainWidget(QtGui.QWidget):
- def __init__(self, parent=None):
- super(MainWidget, self).__init__(parent)
-
- # Create two DataFrames
- self.df1 = pd.DataFrame(np.arange(9).reshape(3, 3),
- columns=['foo', 'bar', 'baz'])
- self.df2 = pd.DataFrame({
- 'int': [1, 2, 3],
- 'float': [1.5, 2.5, 3.5],
- 'string': ['a', 'b', 'c'],
- 'nan': [np.nan, np.nan, np.nan]
- }, index=['AAA', 'BBB', 'CCC'],
- columns=['int', 'float', 'string', 'nan'])
-
- # Create the widget and set the first DataFrame
- self.widget = DataFrameWidget(self.df1)
-
- # Create the buttons for changing DataFrames
- self.button_first = QtGui.QPushButton('First')
- self.button_first.clicked.connect(self.on_first_click)
- self.button_second = QtGui.QPushButton('Second')
- self.button_second.clicked.connect(self.on_second_click)
-
- # Set the layout
- vbox = QtGui.QVBoxLayout()
- vbox.addWidget(self.widget)
- hbox = QtGui.QHBoxLayout()
- hbox.addWidget(self.button_first)
- hbox.addWidget(self.button_second)
- vbox.addLayout(hbox)
- self.setLayout(vbox)
-
- def on_first_click(self):
- '''Sets the first DataFrame'''
- self.widget.setDataFrame(self.df1)
-
- def on_second_click(self):
- '''Sets the second DataFrame'''
- self.widget.setDataFrame(self.df2)
-
- if __name__ == '__main__':
- import sys
-
- # Initialize the application
- app = QtGui.QApplication(sys.argv)
- mw = MainWidget()
- mw.show()
- app.exec_()
+There is no support for such visualization in pandas. However, the external
+package `pandas-qt `_ does
+provide this functionality.
diff --git a/doc/source/whatsnew/v0.19.0.txt b/doc/source/whatsnew/v0.19.0.txt
index 0b9695125c0a9..34b70826f2bf3 100644
--- a/doc/source/whatsnew/v0.19.0.txt
+++ b/doc/source/whatsnew/v0.19.0.txt
@@ -504,6 +504,7 @@ Deprecations
Removal of prior version deprecations/changes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- The ``pd.sandbox`` module has been removed in favor of the external library ``pandas-qt`` (:issue:`13670`)
- ``DataFrame.to_csv()`` has dropped the ``engine`` parameter, as was deprecated in 0.17.1 (:issue:`11274`, :issue:`13419`)
- ``DataFrame.to_dict()`` has dropped the ``outtype`` parameter in favor of ``orient`` (:issue:`13627`, :issue:`8486`)
- ``pd.Categorical`` has dropped the ``levels`` attribute in favour of ``categories`` (:issue:`8376`)
diff --git a/pandas/api/tests/test_api.py b/pandas/api/tests/test_api.py
index 3f6c97441d659..0aefdbeae0518 100644
--- a/pandas/api/tests/test_api.py
+++ b/pandas/api/tests/test_api.py
@@ -28,7 +28,7 @@ class TestPDApi(Base, tm.TestCase):
# these are optionally imported based on testing
# & need to be ignored
- ignored = ['tests', 'rpy', 'sandbox', 'locale']
+ ignored = ['tests', 'rpy', 'locale']
# top-level sub-packages
lib = ['api', 'compat', 'computation', 'core',
diff --git a/pandas/sandbox/__init__.py b/pandas/sandbox/__init__.py
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/pandas/sandbox/qtpandas.py b/pandas/sandbox/qtpandas.py
deleted file mode 100644
index b6af40a0e2156..0000000000000
--- a/pandas/sandbox/qtpandas.py
+++ /dev/null
@@ -1,145 +0,0 @@
-"""
-Easy integration of DataFrame into pyqt framework
-
-@author: Jev Kuznetsov
-"""
-
-# flake8: noqa
-
-# GH9615
-
-import warnings
-warnings.warn("The pandas.sandbox.qtpandas module is deprecated and will be "
- "removed in a future version. We refer users to the external package "
- "here: https://github.com/datalyze-solutions/pandas-qt")
-
-try:
- from PyQt4.QtCore import QAbstractTableModel, Qt, QVariant, QModelIndex
- from PyQt4.QtGui import (
- QApplication, QDialog, QVBoxLayout, QTableView, QWidget)
-except ImportError:
- from PySide.QtCore import QAbstractTableModel, Qt, QModelIndex
- from PySide.QtGui import (
- QApplication, QDialog, QVBoxLayout, QTableView, QWidget)
- QVariant = lambda value=None: value
-
-from pandas import DataFrame, Index
-
-
-class DataFrameModel(QAbstractTableModel):
- """ data model for a DataFrame class """
- def __init__(self):
- super(DataFrameModel, self).__init__()
- self.df = DataFrame()
-
- def setDataFrame(self, dataFrame):
- self.df = dataFrame
-
- def signalUpdate(self):
- """ tell viewers to update their data (this is full update, not
- efficient)"""
- self.layoutChanged.emit()
-
- #------------- table display functions -----------------
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- if role != Qt.DisplayRole:
- return QVariant()
-
- if orientation == Qt.Horizontal:
- try:
- return self.df.columns.tolist()[section]
- except (IndexError, ):
- return QVariant()
- elif orientation == Qt.Vertical:
- try:
- # return self.df.index.tolist()
- return self.df.index.tolist()[section]
- except (IndexError, ):
- return QVariant()
-
- def data(self, index, role=Qt.DisplayRole):
- if role != Qt.DisplayRole:
- return QVariant()
-
- if not index.isValid():
- return QVariant()
-
- return QVariant(str(self.df.ix[index.row(), index.column()]))
-
- def flags(self, index):
- flags = super(DataFrameModel, self).flags(index)
- flags |= Qt.ItemIsEditable
- return flags
-
- def setData(self, index, value, role):
- row = self.df.index[index.row()]
- col = self.df.columns[index.column()]
- if hasattr(value, 'toPyObject'):
- # PyQt4 gets a QVariant
- value = value.toPyObject()
- else:
- # PySide gets an unicode
- dtype = self.df[col].dtype
- if dtype != object:
- value = None if value == '' else dtype.type(value)
- self.df.set_value(row, col, value)
- return True
-
- def rowCount(self, index=QModelIndex()):
- return self.df.shape[0]
-
- def columnCount(self, index=QModelIndex()):
- return self.df.shape[1]
-
-
-class DataFrameWidget(QWidget):
- """ a simple widget for using DataFrames in a gui """
- def __init__(self, dataFrame, parent=None):
- super(DataFrameWidget, self).__init__(parent)
-
- self.dataModel = DataFrameModel()
- self.dataTable = QTableView()
- self.dataTable.setModel(self.dataModel)
-
- layout = QVBoxLayout()
- layout.addWidget(self.dataTable)
- self.setLayout(layout)
- # Set DataFrame
- self.setDataFrame(dataFrame)
-
- def setDataFrame(self, dataFrame):
- self.dataModel.setDataFrame(dataFrame)
- self.dataModel.signalUpdate()
- self.dataTable.resizeColumnsToContents()
-
-#-----------------stand alone test code
-
-
-def testDf():
- """ creates test dataframe """
- data = {'int': [1, 2, 3], 'float': [1.5, 2.5, 3.5],
- 'string': ['a', 'b', 'c'], 'nan': [np.nan, np.nan, np.nan]}
- return DataFrame(data, index=Index(['AAA', 'BBB', 'CCC']),
- columns=['int', 'float', 'string', 'nan'])
-
-
-class Form(QDialog):
- def __init__(self, parent=None):
- super(Form, self).__init__(parent)
-
- df = testDf() # make up some data
- widget = DataFrameWidget(df)
- widget.resizeColumnsToContents()
-
- layout = QVBoxLayout()
- layout.addWidget(widget)
- self.setLayout(layout)
-
-if __name__ == '__main__':
- import sys
- import numpy as np
-
- app = QApplication(sys.argv)
- form = Form()
- form.show()
- app.exec_()
diff --git a/setup.py b/setup.py
index c77ca4d9e60fe..0bff49c4976b8 100755
--- a/setup.py
+++ b/setup.py
@@ -560,7 +560,6 @@ def pxd(name):
'pandas.io.sas',
'pandas.formats',
'pandas.rpy',
- 'pandas.sandbox',
'pandas.sparse',
'pandas.sparse.tests',
'pandas.stats',