diff --git a/doc/source/release.rst b/doc/source/release.rst index c572aa91c18bb..721276dd383e8 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -193,6 +193,7 @@ See :ref:`Internal Refactoring` - Refactor ``rename`` methods to core/generic.py; fixes ``Series.rename`` for (:issue:`4605`), and adds ``rename`` with the same signature for ``Panel`` - Series (for index) / Panel (for items) now as attribute access to its elements (:issue:`1903`) +- Refactor of ``_get_numeric_data/_get_bool_data`` to core/generic.py, allowing Series/Panel functionaility **Experimental Features** diff --git a/doc/source/v0.13.0.txt b/doc/source/v0.13.0.txt index c0e3f5e6b2f10..16ae57310dae7 100644 --- a/doc/source/v0.13.0.txt +++ b/doc/source/v0.13.0.txt @@ -268,6 +268,7 @@ and behaviors. Series formerly subclassed directly from ``ndarray``. (:issue:`40 - ``Series.copy`` no longer accepts the ``order`` parameter and is now consistent with ``NDFrame`` copy - Refactor ``rename`` methods to core/generic.py; fixes ``Series.rename`` for (:issue`4605`), and adds ``rename`` with the same signature for ``Panel`` +- Refactor of ``_get_numeric_data/_get_bool_data`` to core/generic.py, allowing Series/Panel functionaility - ``Series`` (for index) / ``Panel`` (for items) now allow attribute access to its elements (:issue:`1903`) .. ipython:: python diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 31f7179f8e328..9f470d536e765 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4388,12 +4388,6 @@ def _get_agg_axis(self, axis_num): else: raise Exception('Must have 0<= axis <= 1') - def _get_numeric_data(self): - return self._constructor(self._data.get_numeric_data(), index=self.index, copy=False) - - def _get_bool_data(self): - return self._constructor(self._data.get_bool_data(), index=self.index, copy=False) - def quantile(self, q=0.5, axis=0, numeric_only=True): """ Return values at the given quantile over requested axis, a la diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 56c37ff3c7a0a..d15ce05e84d40 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1319,6 +1319,12 @@ def _protect_consolidate(self, f): self._clear_item_cache() return result + def _get_numeric_data(self): + return self._constructor(self._data.get_numeric_data()) + + def _get_bool_data(self): + return self._constructor(self._data.get_bool_data()) + #---------------------------------------------------------------------- # Internal Interface Methods diff --git a/pandas/core/internals.py b/pandas/core/internals.py index 5a5c541fc3251..cec80b679781c 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -1574,9 +1574,11 @@ def __init__(self, blocks, axes, do_integrity_check=True, fastpath=True): if not self.items.is_unique: self._set_ref_locs(do_refs=True) - @classmethod - def make_empty(cls): - return cls([], [[], []]) + def make_empty(self, axes=None): + """ return an empty BlockManager with the items axis of len 0 """ + if axes is None: + axes = [_ensure_index([]) ] + [ _ensure_index(a) for a in self.axes[1:] ] + return self.__class__(np.array([]), axes) def __nonzero__(self): return True @@ -2074,7 +2076,7 @@ def get_data(self, copy=False, columns=None, **kwargs): blocks = self.get_block_map( typ='list', copy=copy, columns=columns, **kwargs) if len(blocks) == 0: - return self.__class__.make_empty() + return self.make_empty() return self.combine(blocks) diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index d2ca850f0a1c0..c1d5f07bde2f6 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -10667,8 +10667,8 @@ def test_tslib_tz_convert_trans_pos_plus_1__bug(self): test_vector = pd.Series([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5], dtype=int) hours = idx.hour diff --git a/pandas/tests/test_generic.py b/pandas/tests/test_generic.py index 9a147b4e69f38..be27bab46dc47 100644 --- a/pandas/tests/test_generic.py +++ b/pandas/tests/test_generic.py @@ -36,16 +36,43 @@ def setUp(self): import warnings warnings.filterwarnings(action='ignore', category=FutureWarning) + @property + def _ndim(self): + return self._typ._AXIS_LEN + def _axes(self): """ return the axes for my object typ """ return self._typ._AXIS_ORDERS - def _construct(self, shape=None, **kwargs): - """ construct an object for the given shape """ + def _construct(self, shape, value=None, **kwargs): + """ construct an object for the given shape + if value is specified use that if its a scalar + if value is an array, repeat it as needed """ if isinstance(shape,int): - shape = tuple([shape] * self._typ._AXIS_LEN) - return self._typ(np.random.randn(*shape),**kwargs) + shape = tuple([shape] * self._ndim) + if value is not None: + if np.isscalar(value): + if value == 'empty': + arr = None + + # remove the info axis + kwargs.pop(self._typ._info_axis_name,None) + else: + arr = np.empty(shape) + arr.fill(value) + else: + fshape = np.prod(shape) + arr = value.ravel() + new_shape = fshape/arr.shape[0] + if fshape % arr.shape[0] != 0: + raise Exception("invalid value passed in _construct") + + arr = np.repeat(arr,new_shape).reshape(shape) + else: + arr = np.random.randn(*shape) + return self._typ(arr,**kwargs) + def _compare(self, result, expected): self._comparator(result,expected) @@ -68,6 +95,31 @@ def test_rename(self): # multiple axes at once + def test_get_numeric_data(self): + + n = 4 + kwargs = { } + for i in range(self._ndim): + kwargs[self._typ._AXIS_NAMES[i]] = list(range(n)) + + # get the numeric data + o = self._construct(n,**kwargs) + result = o._get_numeric_data() + self._compare(result, o) + + # non-inclusion + result = o._get_bool_data() + expected = self._construct(n,value='empty',**kwargs) + self._compare(result,expected) + + # get the bool data + arr = np.array([True,True,False,True]) + o = self._construct(n,value=arr,**kwargs) + result = o._get_numeric_data() + self._compare(result, o) + + # _get_numeric_data is includes _get_bool_data, so can't test for non-inclusion + class TestSeries(unittest.TestCase, Generic): _typ = Series _comparator = lambda self, x, y: assert_series_equal(x,y) diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py index 9ae88c071eb27..3fbdedf0c5dd0 100644 --- a/pandas/tools/plotting.py +++ b/pandas/tools/plotting.py @@ -932,21 +932,7 @@ def _get_layout(self): return (len(self.data.columns), 1) def _compute_plot_data(self): - try: - # might be an ndframe - numeric_data = self.data._get_numeric_data() - except AttributeError: # TODO: rm in 0.13 (series-inherit-ndframe) - numeric_data = self.data - orig_dtype = numeric_data.dtype - - # possible object array of numeric data - if orig_dtype == np.object_: - numeric_data = numeric_data.convert_objects() # soft convert - - # still an object dtype so we can't plot it - if numeric_data.dtype == np.object_: - raise TypeError('Series has object dtype and cannot be' - ' converted: no numeric data to plot') + numeric_data = self.data.convert_objects()._get_numeric_data() try: is_empty = numeric_data.empty