diff --git a/pandas/compat/numpy/function.py b/pandas/compat/numpy/function.py
index f5f018ae23a4b..ef009f8ff4d77 100644
--- a/pandas/compat/numpy/function.py
+++ b/pandas/compat/numpy/function.py
@@ -287,23 +287,6 @@ def validate_take_with_convert(convert, args, kwargs):
                                      method='both', max_fname_arg_count=0)
 
 
-def validate_transpose_for_generic(inst, kwargs):
-    try:
-        validate_transpose(tuple(), kwargs)
-    except ValueError as e:
-        klass = type(inst).__name__
-        msg = str(e)
-
-        # the Panel class actual relies on the 'axes' parameter if called
-        # via the 'numpy' library, so let's make sure the error is specific
-        # about saying that the parameter is not supported for particular
-        # implementations of 'transpose'
-        if "the 'axes' parameter is not supported" in msg:
-            msg += " for {klass} instances".format(klass=klass)
-
-        raise ValueError(msg)
-
-
 def validate_window_func(name, args, kwargs):
     numpy_args = ('axis', 'dtype', 'out')
     msg = ("numpy operations are not "
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
index 8d263b2dffd51..d5842e0eb3458 100644
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -368,9 +368,7 @@ def _constructor(self):
 
     @property
     def _constructor_expanddim(self):
-        # TODO: Raise NotImplementedError or change note in extending.rst
-        from pandas.core.panel import Panel
-        return Panel
+        raise NotImplementedError("Not supported for DataFrames!")
 
     # ----------------------------------------------------------------------
     # Constructors
@@ -1934,22 +1932,6 @@ def to_sparse(self, fill_value=None, kind='block'):
                                columns=self.columns, default_kind=kind,
                                default_fill_value=fill_value)
 
-    def to_panel(self):
-        """
-        Transform long (stacked) format (DataFrame) into wide (3D, Panel)
-        format.
-
-        .. deprecated:: 0.20.0
-
-        Currently the index of the DataFrame must be a 2-level MultiIndex. This
-        may be generalized later
-
-        Returns
-        -------
-        Panel
-        """
-        raise NotImplementedError("Panel is being removed in pandas 0.25.0.")
-
     @deprecate_kwarg(old_arg_name='encoding', new_arg_name=None)
     def to_stata(self, fname, convert_dates=None, write_index=True,
                  encoding="latin-1", byteorder=None, time_stamp=None,
@@ -6638,8 +6620,7 @@ def append(self, other, ignore_index=False,
 
         See Also
         --------
-        concat : General function to concatenate DataFrame, Series
-            or Panel objects.
+        concat : General function to concatenate DataFrame or Series objects.
 
         Notes
         -----
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index debffb80dde27..f57b300d1864a 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -29,7 +29,7 @@
     is_dict_like, is_extension_array_dtype, is_integer, is_list_like,
     is_number, is_numeric_dtype, is_object_dtype, is_period_arraylike,
     is_re_compilable, is_scalar, is_timedelta64_dtype, pandas_dtype)
-from pandas.core.dtypes.generic import ABCDataFrame, ABCPanel, ABCSeries
+from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries
 from pandas.core.dtypes.inference import is_hashable
 from pandas.core.dtypes.missing import isna, notna
 
@@ -688,7 +688,7 @@ def transpose(self, *args, **kwargs):
         if kwargs.pop('copy', None) or (len(args) and args[-1]):
             new_values = new_values.copy()
 
-        nv.validate_transpose_for_generic(self, kwargs)
+        nv.validate_transpose(tuple(), kwargs)
         return self._constructor(new_values, **new_axes).__finalize__(self)
 
     def swapaxes(self, axis1, axis2, copy=True):
@@ -978,7 +978,7 @@ def rename(self, *args, **kwargs):
         ----------
         %(axes)s : scalar, list-like, dict-like or function, optional
             Scalar or list-like will alter the ``Series.name`` attribute,
-            and raise on DataFrame or Panel.
+            and raise on DataFrame.
             dict-like or functions are transformations to apply to
             that axis' values
         copy : bool, default True
@@ -1852,16 +1852,14 @@ def __iter__(self):
     def keys(self):
         """Get the 'info axis' (see Indexing for more)
 
-        This is index for Series, columns for DataFrame and major_axis for
-        Panel.
+        This is index for Series, columns for DataFrame.
         """
         return self._info_axis
 
     def iteritems(self):
         """Iterate over (label, values) on info axis
 
-        This is index for Series, columns for DataFrame, major_axis for Panel,
-        and so on.
+        This is index for Series, columns for DataFrame and so on.
         """
         for h in self._info_axis:
             yield h, self[h]
@@ -3063,8 +3061,9 @@ def _create_indexer(cls, name, indexer):
 
     def get(self, key, default=None):
         """
-        Get item from object for given key (DataFrame column, Panel slice,
-        etc.). Returns default value if not found.
+        Get item from object for given key (ex: DataFrame column).
+
+        Returns default value if not found.
 
         Parameters
         ----------
@@ -4091,8 +4090,7 @@ def sort_values(self, by=None, axis=0, ascending=True, inplace=False,
         0   A    2    0
         1   A    1    1
         """
-        raise NotImplementedError("sort_values has not been implemented "
-                                  "on Panel or Panel4D objects.")
+        raise AbstractMethodError(self)
 
     def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
                    kind='quicksort', na_position='last', sort_remaining=True):
@@ -4770,7 +4768,7 @@ def sample(self, n=None, frac=None, replace=False, weights=None,
             object.
         axis : int or string, optional
             Axis to sample. Accepts axis number or name. Default is stat axis
-            for given data type (0 for Series and DataFrames, 1 for Panels).
+            for given data type (0 for Series and DataFrames).
 
         Returns
         -------
@@ -4853,7 +4851,7 @@ def sample(self, n=None, frac=None, replace=False, weights=None,
                                          "a DataFrame")
                 else:
                     raise ValueError("Strings cannot be passed as weights "
-                                     "when sampling from a Series or Panel.")
+                                     "when sampling from a Series.")
 
             weights = pd.Series(weights, dtype='float64')
 
@@ -5697,8 +5695,7 @@ def astype(self, dtype, copy=True, errors='raise', **kwargs):
             elif self.ndim > 2:
                 raise NotImplementedError(
                     'astype() only accepts a dtype arg of type dict when '
-                    'invoked on Series and DataFrames. A single dtype must be '
-                    'specified when invoked on a Panel.'
+                    'invoked on Series and DataFrames.'
                 )
             for col_name in dtype.keys():
                 if col_name not in self:
@@ -5751,7 +5748,7 @@ def copy(self, deep=True):
 
         Returns
         -------
-        copy : Series, DataFrame or Panel
+        copy : Series or DataFrame
             Object type matches caller.
 
         Notes
@@ -6822,8 +6819,7 @@ def interpolate(self, method='linear', axis=0, limit=None, inplace=False,
         inplace = validate_bool_kwarg(inplace, 'inplace')
 
         if self.ndim > 2:
-            raise NotImplementedError("Interpolate has not been implemented "
-                                      "on Panel and Panel 4D objects.")
+            raise NotImplementedError("Interpolate has not been implemented ")
 
         if axis == 0:
             ax = self._info_axis_name
@@ -7326,9 +7322,6 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False,
         3      6      8
         4      5      3
         """
-        if isinstance(self, ABCPanel):
-            raise NotImplementedError("clip is not supported yet for panels")
-
         inplace = validate_bool_kwarg(inplace, 'inplace')
 
         axis = nv.validate_clip_with_axis(axis, args, kwargs)
@@ -9824,10 +9817,7 @@ def describe(self, percentiles=None, include=None, exclude=None):
         75%            NaN      2.5
         max            NaN      3.0
         """
-        if self.ndim >= 3:
-            msg = "describe is not implemented on Panel objects."
-            raise NotImplementedError(msg)
-        elif self.ndim == 2 and self.columns.size == 0:
+        if self.ndim == 2 and self.columns.size == 0:
             raise ValueError("Cannot describe a DataFrame without columns")
 
         if percentiles is not None:
diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py
index dc0d44ef9ef5c..40f335ad1d6f7 100644
--- a/pandas/core/groupby/groupby.py
+++ b/pandas/core/groupby/groupby.py
@@ -51,7 +51,6 @@ class providing the base-class of operations.
         --------
         Series.%(name)s
         DataFrame.%(name)s
-        Panel.%(name)s
 """
 
 _apply_docs = dict(
diff --git a/pandas/core/ops.py b/pandas/core/ops.py
index 8c423e0cf304a..e6c49666b66ba 100644
--- a/pandas/core/ops.py
+++ b/pandas/core/ops.py
@@ -23,7 +23,7 @@
     is_integer_dtype, is_list_like, is_object_dtype, is_period_dtype,
     is_scalar, is_timedelta64_dtype, needs_i8_conversion)
 from pandas.core.dtypes.generic import (
-    ABCDataFrame, ABCIndex, ABCIndexClass, ABCPanel, ABCSeries, ABCSparseArray,
+    ABCDataFrame, ABCIndex, ABCIndexClass, ABCSeries, ABCSparseArray,
     ABCSparseSeries)
 from pandas.core.dtypes.missing import isna, notna
 
@@ -234,7 +234,7 @@ def _gen_eval_kwargs(name):
     """
     kwargs = {}
 
-    # Series and Panel appear to only pass __add__, __radd__, ...
+    # Series appear to only pass __add__, __radd__, ...
     # but DataFrame gets both these dunder names _and_ non-dunder names
     # add, radd, ...
     name = name.replace('__', '')
@@ -991,40 +991,6 @@ def _get_op_name(op, special):
    C   True    False
 """
 
-_flex_doc_PANEL = """
-Return {desc} of series and other, element-wise (binary operator `{op_name}`).
-Equivalent to ``{equiv}``.
-
-Parameters
-----------
-other : DataFrame or Panel
-axis : {{items, major_axis, minor_axis}}
-    Axis to broadcast over
-
-Returns
--------
-Panel
-
-See Also
---------
-Panel.{reverse}
-"""
-
-
-_agg_doc_PANEL = """
-Wrapper method for {op_name}
-
-Parameters
-----------
-other : DataFrame or Panel
-axis : {{items, major_axis, minor_axis}}
-    Axis to broadcast over
-
-Returns
--------
-Panel
-"""
-
 
 def _make_flex_doc(op_name, typ):
     """
@@ -1069,14 +1035,6 @@ def _make_flex_doc(op_name, typ):
             equiv=equiv,
             reverse=op_desc['reverse']
         )
-    elif typ == 'panel':
-        base_doc = _flex_doc_PANEL
-        doc = base_doc.format(
-            desc=op_desc['desc'],
-            op_name=op_name,
-            equiv=equiv,
-            reverse=op_desc['reverse']
-        )
     else:
         raise AssertionError('Invalid typ argument.')
     return doc
@@ -1457,12 +1415,6 @@ def _get_method_wrappers(cls):
         arith_special = _arith_method_SPARSE_ARRAY
         comp_special = _arith_method_SPARSE_ARRAY
         bool_special = _arith_method_SPARSE_ARRAY
-    elif issubclass(cls, ABCPanel):
-        arith_flex = _flex_method_PANEL
-        comp_flex = _comp_method_PANEL
-        arith_special = _arith_method_PANEL
-        comp_special = _comp_method_PANEL
-        bool_special = _arith_method_PANEL
     elif issubclass(cls, ABCDataFrame):
         # Same for DataFrame and SparseDataFrame
         arith_flex = _arith_method_FRAME
@@ -2295,94 +2247,6 @@ def f(self, other):
     return f
 
 
-# -----------------------------------------------------------------------------
-# Panel
-
-def _arith_method_PANEL(cls, op, special):
-    # work only for scalars
-    op_name = _get_op_name(op, special)
-
-    def f(self, other):
-        if not is_scalar(other):
-            raise ValueError('Simple arithmetic with {name} can only be '
-                             'done with scalar values'
-                             .format(name=self._constructor.__name__))
-
-        return self._combine(other, op)
-
-    f.__name__ = op_name
-    return f
-
-
-def _comp_method_PANEL(cls, op, special):
-    str_rep = _get_opstr(op, cls)
-    op_name = _get_op_name(op, special)
-
-    def na_op(x, y):
-        import pandas.core.computation.expressions as expressions
-
-        try:
-            result = expressions.evaluate(op, str_rep, x, y)
-        except TypeError:
-            result = mask_cmp_op(x, y, op, np.ndarray)
-        return result
-
-    @Appender('Wrapper for comparison method {name}'.format(name=op_name))
-    def f(self, other, axis=None):
-        # Validate the axis parameter
-        if axis is not None:
-            self._get_axis_number(axis)
-
-        if isinstance(other, self._constructor):
-            return self._compare_constructor(other, na_op)
-        elif isinstance(other, (self._constructor_sliced, ABCDataFrame,
-                                ABCSeries)):
-            raise Exception("input needs alignment for this object [{object}]"
-                            .format(object=self._constructor))
-        else:
-            return self._combine_const(other, na_op)
-
-    f.__name__ = op_name
-
-    return f
-
-
-def _flex_method_PANEL(cls, op, special):
-    str_rep = _get_opstr(op, cls)
-    op_name = _get_op_name(op, special)
-    eval_kwargs = _gen_eval_kwargs(op_name)
-    fill_zeros = _gen_fill_zeros(op_name)
-
-    def na_op(x, y):
-        import pandas.core.computation.expressions as expressions
-
-        try:
-            result = expressions.evaluate(op, str_rep, x, y,
-                                          errors='raise',
-                                          **eval_kwargs)
-        except TypeError:
-            result = op(x, y)
-
-        # handles discrepancy between numpy and numexpr on division/mod
-        # by 0 though, given that these are generally (always?)
-        # non-scalars, I'm not sure whether it's worth it at the moment
-        result = missing.fill_zeros(result, x, y, op_name, fill_zeros)
-        return result
-
-    if op_name in _op_descriptions:
-        doc = _make_flex_doc(op_name, 'panel')
-    else:
-        # doc strings substitors
-        doc = _agg_doc_PANEL.format(op_name=op_name)
-
-    @Appender(doc)
-    def f(self, other, axis=0):
-        return self._combine(other, na_op, axis=axis)
-
-    f.__name__ = op_name
-    return f
-
-
 # -----------------------------------------------------------------------------
 # Sparse
 
diff --git a/pandas/core/panel.py b/pandas/core/panel.py
index c642d96a4acfd..95a979c361cd7 100644
--- a/pandas/core/panel.py
+++ b/pandas/core/panel.py
@@ -26,7 +26,6 @@
 from pandas.core.internals import (
     BlockManager, create_block_manager_from_arrays,
     create_block_manager_from_blocks)
-import pandas.core.ops as ops
 from pandas.core.reshape.util import cartesian_product
 from pandas.core.series import Series
 
@@ -1574,6 +1573,4 @@ def sort_values(self, *args, **kwargs):
                            'minor_axis': 'columns'},
                   docs={})
 
-ops.add_special_arithmetic_methods(Panel)
-ops.add_flex_arithmetic_methods(Panel)
 Panel._add_numeric_operations()
diff --git a/pandas/core/reshape/concat.py b/pandas/core/reshape/concat.py
index fe0445dfca7ca..043efbdc5dc10 100644
--- a/pandas/core/reshape/concat.py
+++ b/pandas/core/reshape/concat.py
@@ -34,7 +34,7 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
 
     Parameters
     ----------
-    objs : a sequence or mapping of Series, DataFrame, or Panel objects
+    objs : a sequence or mapping of Series or DataFrame objects.
         If a dict is passed, the sorted keys will be used as the `keys`
         argument, unless it is passed, in which case the values will be
         selected (see below). Any None objects will be dropped silently unless
diff --git a/pandas/core/series.py b/pandas/core/series.py
index 5d8de03f91e10..25e8006b1b89f 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -2440,8 +2440,7 @@ def append(self, to_append, ignore_index=False, verify_integrity=False):
 
         See Also
         --------
-        concat : General function to concatenate DataFrame, Series
-            or Panel objects.
+        concat : General function to concatenate DataFrame or Series objects.
 
         Notes
         -----
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
index 18479b3420419..55dee4d065392 100644
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -3876,8 +3876,6 @@ def read(self, where=None, columns=None, **kwargs):
         if not self.read_axes(where=where, **kwargs):
             return None
 
-        raise NotImplementedError("Panel is removed in pandas 0.25.0")
-
 
 class AppendableTable(LegacyTable):
     """ support the new appendable table formats """
diff --git a/pandas/util/_validators.py b/pandas/util/_validators.py
index d392a7709061b..41faaf68d7f40 100644
--- a/pandas/util/_validators.py
+++ b/pandas/util/_validators.py
@@ -236,7 +236,7 @@ def validate_axis_style_args(data, args, kwargs, arg_name, method_name):
 
     Parameters
     ----------
-    data : DataFrame or Panel
+    data : DataFrame
     args : tuple
         All positional arguments from the user
     kwargs : dict