From d481895ad1dc31e4780be910ccf4ab24878f91d1 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 6 Apr 2020 20:37:11 -0700 Subject: [PATCH 1/4] PERF: fastpath constructor _from_mgr --- pandas/core/apply.py | 4 +- pandas/core/frame.py | 10 ++--- pandas/core/generic.py | 88 ++++++++++++++++++++++++++---------------- 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/pandas/core/apply.py b/pandas/core/apply.py index a0351cb687d02..075e9e3cebb35 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -166,9 +166,7 @@ def get_result(self): elif isinstance(self.f, np.ufunc): with np.errstate(all="ignore"): results = self.obj._mgr.apply("apply", func=self.f) - return self.obj._constructor( - data=results, index=self.index, columns=self.columns, copy=False - ) + return type(self.obj)._from_mgr(results) # broadcasting if self.result_type == "broadcast": diff --git a/pandas/core/frame.py b/pandas/core/frame.py index aedbba755227d..dd7cb60f8a0fb 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4469,7 +4469,7 @@ def _maybe_casted_values(index, labels=None): @Appender(_shared_docs["isna"] % _shared_doc_kwargs) def isna(self) -> "DataFrame": - result = self._constructor(self._data.isna(func=isna)) + result = type(self)._from_mgr(self._mgr.isna(func=isna)) return result.__finalize__(self, method="isna") @Appender(_shared_docs["isna"] % _shared_doc_kwargs) @@ -4794,7 +4794,7 @@ def sort_values( if ignore_index: new_data.axes[1] = ibase.default_index(len(indexer)) - result = self._constructor(new_data) + result = type(self)._from_mgr(new_data) if inplace: return self._update_inplace(result) else: @@ -4930,7 +4930,7 @@ def sort_index( if ignore_index: new_data.axes[1] = ibase.default_index(len(indexer)) - result = self._constructor(new_data) + result = type(self)._from_mgr(new_data) if inplace: return self._update_inplace(result) else: @@ -6662,7 +6662,7 @@ def diff(self, periods: int = 1, axis: Axis = 0) -> "DataFrame": """ bm_axis = self._get_block_manager_axis(axis) new_data = self._mgr.diff(n=periods, axis=bm_axis) - return self._constructor(new_data) + return type(self)._from_mgr(new_data) # ---------------------------------------------------------------------- # Function application @@ -8426,7 +8426,7 @@ def quantile(self, q=0.5, axis=0, numeric_only=True, interpolation="linear"): ) if result.ndim == 2: - result = self._constructor(result) + result = type(self)._from_mgr(result) else: result = self._constructor_sliced(result, name=q) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 052a4adddca27..1d957fd404d6e 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -213,13 +213,13 @@ def __init__( object.__setattr__(self, "_attrs", attrs) @classmethod - def _init_mgr(cls, mgr, axes=None, dtype=None, copy=False): + def _init_mgr(cls, mgr, axes, dtype=None, copy: bool = False) -> BlockManager: """ passed a manager and a axes dict """ for a, axe in axes.items(): if axe is not None: - mgr = mgr.reindex_axis( - axe, axis=cls._get_block_manager_axis(a), copy=False - ) + axe = ensure_index(axe) + bm_axis = cls._get_block_manager_axis(a) + mgr = mgr.reindex_axis(axe, axis=bm_axis, copy=False) # make a copy if explicitly requested if copy: @@ -230,6 +230,15 @@ def _init_mgr(cls, mgr, axes=None, dtype=None, copy=False): mgr = mgr.astype(dtype=dtype) return mgr + @classmethod + def _from_mgr(cls, mgr: BlockManager): + """ + Fastpath constructor for if we have only a BlockManager. + """ + obj = object.__new__(cls) + NDFrame.__init__(obj, mgr) + return obj + # ---------------------------------------------------------------------- @property @@ -1359,8 +1368,8 @@ def __invert__(self): return self new_data = self._mgr.apply(operator.invert) - result = self._constructor(new_data).__finalize__(self, method="__invert__") - return result + result = type(self)._from_mgr(new_data) + return result.__finalize__(self, method="__invert__") def __nonzero__(self): raise ValueError( @@ -3365,7 +3374,7 @@ class max_speed new_data = self._mgr.take( indices, axis=self._get_block_manager_axis(axis), verify=True ) - return self._constructor(new_data).__finalize__(self, method="take") + return type(self)._from_mgr(new_data).__finalize__(self, method="take") def _take_with_is_copy(self: FrameOrSeries, indices, axis=0) -> FrameOrSeries: """ @@ -3571,7 +3580,7 @@ def _slice(self: FrameOrSeries, slobj: slice, axis=0) -> FrameOrSeries: """ assert isinstance(slobj, slice), type(slobj) axis = self._get_block_manager_axis(axis) - result = self._constructor(self._mgr.get_slice(slobj, axis=axis)) + result = type(self)._from_mgr(self._mgr.get_slice(slobj, axis=axis)) result = result.__finalize__(self) # this could be a view @@ -4508,7 +4517,7 @@ def _reindex_with_indexers( if copy and new_data is self._mgr: new_data = new_data.copy() - return self._constructor(new_data).__finalize__(self) + return type(self)._from_mgr(new_data).__finalize__(self) def filter( self: FrameOrSeries, @@ -5274,7 +5283,7 @@ def _consolidate(self, inplace: bool_t = False): else: f = lambda: self._mgr.consolidate() cons_data = self._protect_consolidate(f) - return self._constructor(cons_data).__finalize__(self) + return type(self)._from_mgr(cons_data).__finalize__(self) @property def _is_mixed_type(self) -> bool_t: @@ -5303,10 +5312,10 @@ def _check_inplace_setting(self, value) -> bool_t: return True def _get_numeric_data(self): - return self._constructor(self._mgr.get_numeric_data()).__finalize__(self,) + return type(self)._from_mgr(self._mgr.get_numeric_data()).__finalize__(self) def _get_bool_data(self): - return self._constructor(self._mgr.get_bool_data()).__finalize__(self,) + return type(self)._from_mgr(self._mgr.get_bool_data()).__finalize__(self) # ---------------------------------------------------------------------- # Internal Interface Methods @@ -5573,7 +5582,7 @@ def astype( else: # else, only a single dtype is given new_data = self._mgr.astype(dtype=dtype, copy=copy, errors=errors,) - return self._constructor(new_data).__finalize__(self, method="astype") + return type(self)._from_mgr(new_data).__finalize__(self, method="astype") # GH 19920: retain column metadata after concat result = pd.concat(results, axis=1, copy=False) @@ -5687,7 +5696,7 @@ def copy(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: """ data = self._mgr.copy(deep=deep) self._clear_item_cache() - return self._constructor(data).__finalize__(self, method="copy") + return type(self)._from_mgr(data).__finalize__(self, method="copy") def __copy__(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: return self.copy(deep=deep) @@ -5738,15 +5747,19 @@ def _convert( validate_bool_kwarg(timedelta, "timedelta") validate_bool_kwarg(coerce, "coerce") validate_bool_kwarg(copy, "copy") - return self._constructor( - self._mgr.convert( - datetime=datetime, - numeric=numeric, - timedelta=timedelta, - coerce=coerce, - copy=copy, + return ( + type(self) + ._from_mgr( + self._mgr.convert( + datetime=datetime, + numeric=numeric, + timedelta=timedelta, + coerce=coerce, + copy=copy, + ) ) - ).__finalize__(self) + .__finalize__(self) + ) def infer_objects(self: FrameOrSeries) -> FrameOrSeries: """ @@ -5789,11 +5802,19 @@ def infer_objects(self: FrameOrSeries) -> FrameOrSeries: # numeric=False necessary to only soft convert; # python objects will still be converted to # native numpy numeric types - return self._constructor( - self._mgr.convert( - datetime=True, numeric=False, timedelta=True, coerce=False, copy=True + return ( + type(self) + ._from_mgr( + self._mgr.convert( + datetime=True, + numeric=False, + timedelta=True, + coerce=False, + copy=True, + ) ) - ).__finalize__(self, method="infer_objects") + .__finalize__(self, method="infer_objects") + ) def convert_dtypes( self: FrameOrSeries, @@ -6896,7 +6917,7 @@ def interpolate( **kwargs, ) - result = self._constructor(new_data) + result = type(self)._from_mgr(new_data) if axis == 1: result = result.T if inplace: @@ -8511,7 +8532,7 @@ def _align_series( if copy and fdata is self._mgr: fdata = fdata.copy() - left = self._constructor(fdata) + left = type(self)._from_mgr(fdata) if ridx is None: right = other @@ -8663,7 +8684,7 @@ def _where( new_data = self._mgr.putmask( mask=cond, new=other, align=align, axis=block_axis, ) - result = self._constructor(new_data) + result = type(self)._from_mgr(new_data) return self._update_inplace(result) else: @@ -8675,7 +8696,7 @@ def _where( try_cast=try_cast, axis=block_axis, ) - result = self._constructor(new_data) + result = type(self)._from_mgr(new_data) return result.__finalize__(self) _shared_docs[ @@ -8948,7 +8969,7 @@ def shift( else: return self.tshift(periods, freq) - return self._constructor(new_data).__finalize__(self, method="shift") + return type(self)._from_mgr(new_data).__finalize__(self, method="shift") def slice_shift(self: FrameOrSeries, periods: int = 1, axis=0) -> FrameOrSeries: """ @@ -11202,9 +11223,8 @@ def block_accum_func(blk_values): result = self._mgr.apply(block_accum_func) - d = self._construct_axes_dict() - d["copy"] = False - return self._constructor(result, **d).__finalize__(self, method=name) + obj = type(self)._from_mgr(result) + return obj.__finalize__(self, method=name) return set_function_name(cum_func, name, cls) From 4502f068d00605087db081aab5962037dd773967 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 7 Apr 2020 10:12:43 -0700 Subject: [PATCH 2/4] annotate, constructor --- pandas/core/generic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 1d957fd404d6e..26d3e2ed8acfa 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -231,11 +231,12 @@ def _init_mgr(cls, mgr, axes, dtype=None, copy: bool = False) -> BlockManager: return mgr @classmethod - def _from_mgr(cls, mgr: BlockManager): + def _from_mgr(cls: Type[FrameOrSeries], mgr: BlockManager) -> FrameOrSeries: """ Fastpath constructor for if we have only a BlockManager. """ - obj = object.__new__(cls) + constructor = cls._constructor.fget(cls) # type: ignore + obj = object.__new__(constructor) NDFrame.__init__(obj, mgr) return obj From 262b005f017c7723e555bd92594d1eb0448139fc Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 7 Apr 2020 13:50:38 -0700 Subject: [PATCH 3/4] subclass compat --- pandas/core/apply.py | 2 +- pandas/core/frame.py | 10 +++--- pandas/core/generic.py | 81 +++++++++++++++++++----------------------- 3 files changed, 42 insertions(+), 51 deletions(-) diff --git a/pandas/core/apply.py b/pandas/core/apply.py index 075e9e3cebb35..a1da980059d2a 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -166,7 +166,7 @@ def get_result(self): elif isinstance(self.f, np.ufunc): with np.errstate(all="ignore"): results = self.obj._mgr.apply("apply", func=self.f) - return type(self.obj)._from_mgr(results) + return self.obj._from_mgr(results) # broadcasting if self.result_type == "broadcast": diff --git a/pandas/core/frame.py b/pandas/core/frame.py index dd7cb60f8a0fb..d6424db61fbda 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4469,7 +4469,7 @@ def _maybe_casted_values(index, labels=None): @Appender(_shared_docs["isna"] % _shared_doc_kwargs) def isna(self) -> "DataFrame": - result = type(self)._from_mgr(self._mgr.isna(func=isna)) + result = self._from_mgr(self._mgr.isna(func=isna)) return result.__finalize__(self, method="isna") @Appender(_shared_docs["isna"] % _shared_doc_kwargs) @@ -4794,7 +4794,7 @@ def sort_values( if ignore_index: new_data.axes[1] = ibase.default_index(len(indexer)) - result = type(self)._from_mgr(new_data) + result = self._from_mgr(new_data) if inplace: return self._update_inplace(result) else: @@ -4930,7 +4930,7 @@ def sort_index( if ignore_index: new_data.axes[1] = ibase.default_index(len(indexer)) - result = type(self)._from_mgr(new_data) + result = self._from_mgr(new_data) if inplace: return self._update_inplace(result) else: @@ -6662,7 +6662,7 @@ def diff(self, periods: int = 1, axis: Axis = 0) -> "DataFrame": """ bm_axis = self._get_block_manager_axis(axis) new_data = self._mgr.diff(n=periods, axis=bm_axis) - return type(self)._from_mgr(new_data) + return self._from_mgr(new_data) # ---------------------------------------------------------------------- # Function application @@ -8426,7 +8426,7 @@ def quantile(self, q=0.5, axis=0, numeric_only=True, interpolation="linear"): ) if result.ndim == 2: - result = type(self)._from_mgr(result) + result = self._from_mgr(result) else: result = self._constructor_sliced(result, name=q) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 26d3e2ed8acfa..b369b259d108d 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -230,15 +230,18 @@ def _init_mgr(cls, mgr, axes, dtype=None, copy: bool = False) -> BlockManager: mgr = mgr.astype(dtype=dtype) return mgr - @classmethod - def _from_mgr(cls: Type[FrameOrSeries], mgr: BlockManager) -> FrameOrSeries: + def _from_mgr(self: FrameOrSeries, mgr: BlockManager) -> FrameOrSeries: """ Fastpath constructor for if we have only a BlockManager. """ - constructor = cls._constructor.fget(cls) # type: ignore - obj = object.__new__(constructor) - NDFrame.__init__(obj, mgr) - return obj + constr = self._constructor + if constr is type(self): + # See GH#33357 for why this check is needed + obj = object.__new__(constr) + NDFrame.__init__(obj, mgr) + return obj + + return constr(mgr) # ---------------------------------------------------------------------- @@ -1369,7 +1372,7 @@ def __invert__(self): return self new_data = self._mgr.apply(operator.invert) - result = type(self)._from_mgr(new_data) + result = self._from_mgr(new_data) return result.__finalize__(self, method="__invert__") def __nonzero__(self): @@ -3375,7 +3378,7 @@ class max_speed new_data = self._mgr.take( indices, axis=self._get_block_manager_axis(axis), verify=True ) - return type(self)._from_mgr(new_data).__finalize__(self, method="take") + return self._from_mgr(new_data).__finalize__(self, method="take") def _take_with_is_copy(self: FrameOrSeries, indices, axis=0) -> FrameOrSeries: """ @@ -3581,7 +3584,7 @@ def _slice(self: FrameOrSeries, slobj: slice, axis=0) -> FrameOrSeries: """ assert isinstance(slobj, slice), type(slobj) axis = self._get_block_manager_axis(axis) - result = type(self)._from_mgr(self._mgr.get_slice(slobj, axis=axis)) + result = self._from_mgr(self._mgr.get_slice(slobj, axis=axis)) result = result.__finalize__(self) # this could be a view @@ -4518,7 +4521,7 @@ def _reindex_with_indexers( if copy and new_data is self._mgr: new_data = new_data.copy() - return type(self)._from_mgr(new_data).__finalize__(self) + return self._from_mgr(new_data).__finalize__(self) def filter( self: FrameOrSeries, @@ -5284,7 +5287,7 @@ def _consolidate(self, inplace: bool_t = False): else: f = lambda: self._mgr.consolidate() cons_data = self._protect_consolidate(f) - return type(self)._from_mgr(cons_data).__finalize__(self) + return self._from_mgr(cons_data).__finalize__(self) @property def _is_mixed_type(self) -> bool_t: @@ -5313,10 +5316,10 @@ def _check_inplace_setting(self, value) -> bool_t: return True def _get_numeric_data(self): - return type(self)._from_mgr(self._mgr.get_numeric_data()).__finalize__(self) + return self._from_mgr(self._mgr.get_numeric_data()).__finalize__(self) def _get_bool_data(self): - return type(self)._from_mgr(self._mgr.get_bool_data()).__finalize__(self) + return self._from_mgr(self._mgr.get_bool_data()).__finalize__(self) # ---------------------------------------------------------------------- # Internal Interface Methods @@ -5583,7 +5586,7 @@ def astype( else: # else, only a single dtype is given new_data = self._mgr.astype(dtype=dtype, copy=copy, errors=errors,) - return type(self)._from_mgr(new_data).__finalize__(self, method="astype") + return self._from_mgr(new_data).__finalize__(self, method="astype") # GH 19920: retain column metadata after concat result = pd.concat(results, axis=1, copy=False) @@ -5697,7 +5700,7 @@ def copy(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: """ data = self._mgr.copy(deep=deep) self._clear_item_cache() - return type(self)._from_mgr(data).__finalize__(self, method="copy") + return self._from_mgr(data).__finalize__(self, method="copy") def __copy__(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: return self.copy(deep=deep) @@ -5748,19 +5751,15 @@ def _convert( validate_bool_kwarg(timedelta, "timedelta") validate_bool_kwarg(coerce, "coerce") validate_bool_kwarg(copy, "copy") - return ( - type(self) - ._from_mgr( - self._mgr.convert( - datetime=datetime, - numeric=numeric, - timedelta=timedelta, - coerce=coerce, - copy=copy, - ) + return self._from_mgr( + self._mgr.convert( + datetime=datetime, + numeric=numeric, + timedelta=timedelta, + coerce=coerce, + copy=copy, ) - .__finalize__(self) - ) + ).__finalize__(self) def infer_objects(self: FrameOrSeries) -> FrameOrSeries: """ @@ -5803,19 +5802,11 @@ def infer_objects(self: FrameOrSeries) -> FrameOrSeries: # numeric=False necessary to only soft convert; # python objects will still be converted to # native numpy numeric types - return ( - type(self) - ._from_mgr( - self._mgr.convert( - datetime=True, - numeric=False, - timedelta=True, - coerce=False, - copy=True, - ) + return self._from_mgr( + self._mgr.convert( + datetime=True, numeric=False, timedelta=True, coerce=False, copy=True, ) - .__finalize__(self, method="infer_objects") - ) + ).__finalize__(self, method="infer_objects") def convert_dtypes( self: FrameOrSeries, @@ -6918,7 +6909,7 @@ def interpolate( **kwargs, ) - result = type(self)._from_mgr(new_data) + result = self._from_mgr(new_data) if axis == 1: result = result.T if inplace: @@ -8533,7 +8524,7 @@ def _align_series( if copy and fdata is self._mgr: fdata = fdata.copy() - left = type(self)._from_mgr(fdata) + left = self._from_mgr(fdata) if ridx is None: right = other @@ -8685,7 +8676,7 @@ def _where( new_data = self._mgr.putmask( mask=cond, new=other, align=align, axis=block_axis, ) - result = type(self)._from_mgr(new_data) + result = self._from_mgr(new_data) return self._update_inplace(result) else: @@ -8697,7 +8688,7 @@ def _where( try_cast=try_cast, axis=block_axis, ) - result = type(self)._from_mgr(new_data) + result = self._from_mgr(new_data) return result.__finalize__(self) _shared_docs[ @@ -8970,7 +8961,7 @@ def shift( else: return self.tshift(periods, freq) - return type(self)._from_mgr(new_data).__finalize__(self, method="shift") + return self._from_mgr(new_data).__finalize__(self, method="shift") def slice_shift(self: FrameOrSeries, periods: int = 1, axis=0) -> FrameOrSeries: """ @@ -11224,7 +11215,7 @@ def block_accum_func(blk_values): result = self._mgr.apply(block_accum_func) - obj = type(self)._from_mgr(result) + obj = self._from_mgr(result) return obj.__finalize__(self, method=name) return set_function_name(cum_func, name, cls) From 0343389133714f1fd70edd59884e9164f4d71205 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 8 Apr 2020 10:55:40 -0700 Subject: [PATCH 4/4] move faspath check to constructor --- pandas/core/apply.py | 3 ++- pandas/core/frame.py | 15 ++++++++---- pandas/core/generic.py | 54 ++++++++++++++++-------------------------- pandas/core/series.py | 16 ++++++++++--- 4 files changed, 46 insertions(+), 42 deletions(-) diff --git a/pandas/core/apply.py b/pandas/core/apply.py index a1da980059d2a..a013434491589 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -166,7 +166,8 @@ def get_result(self): elif isinstance(self.f, np.ufunc): with np.errstate(all="ignore"): results = self.obj._mgr.apply("apply", func=self.f) - return self.obj._from_mgr(results) + # _constructor will retain self.index and self.columns + return self.obj._constructor(data=results) # broadcasting if self.result_type == "broadcast": diff --git a/pandas/core/frame.py b/pandas/core/frame.py index d6424db61fbda..aa190c35c4c18 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -434,6 +434,11 @@ def __init__( data = data._mgr if isinstance(data, BlockManager): + if index is None and columns is None and dtype is None and copy is False: + # GH#33357 fastpath + NDFrame.__init__(self, data) + return + mgr = self._init_mgr( data, axes=dict(index=index, columns=columns), dtype=dtype, copy=copy ) @@ -4469,7 +4474,7 @@ def _maybe_casted_values(index, labels=None): @Appender(_shared_docs["isna"] % _shared_doc_kwargs) def isna(self) -> "DataFrame": - result = self._from_mgr(self._mgr.isna(func=isna)) + result = self._constructor(self._data.isna(func=isna)) return result.__finalize__(self, method="isna") @Appender(_shared_docs["isna"] % _shared_doc_kwargs) @@ -4794,7 +4799,7 @@ def sort_values( if ignore_index: new_data.axes[1] = ibase.default_index(len(indexer)) - result = self._from_mgr(new_data) + result = self._constructor(new_data) if inplace: return self._update_inplace(result) else: @@ -4930,7 +4935,7 @@ def sort_index( if ignore_index: new_data.axes[1] = ibase.default_index(len(indexer)) - result = self._from_mgr(new_data) + result = self._constructor(new_data) if inplace: return self._update_inplace(result) else: @@ -6662,7 +6667,7 @@ def diff(self, periods: int = 1, axis: Axis = 0) -> "DataFrame": """ bm_axis = self._get_block_manager_axis(axis) new_data = self._mgr.diff(n=periods, axis=bm_axis) - return self._from_mgr(new_data) + return self._constructor(new_data) # ---------------------------------------------------------------------- # Function application @@ -8426,7 +8431,7 @@ def quantile(self, q=0.5, axis=0, numeric_only=True, interpolation="linear"): ) if result.ndim == 2: - result = self._from_mgr(result) + result = self._constructor(result) else: result = self._constructor_sliced(result, name=q) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index ff2ab4f1cf2f5..056ee70b851ec 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -230,19 +230,6 @@ def _init_mgr(cls, mgr, axes, dtype=None, copy: bool = False) -> BlockManager: mgr = mgr.astype(dtype=dtype) return mgr - def _from_mgr(self: FrameOrSeries, mgr: BlockManager) -> FrameOrSeries: - """ - Fastpath constructor for if we have only a BlockManager. - """ - constr = self._constructor - if constr is type(self): - # See GH#33357 for why this check is needed - obj = object.__new__(constr) - NDFrame.__init__(obj, mgr) - return obj - - return constr(mgr) - # ---------------------------------------------------------------------- @property @@ -1372,8 +1359,8 @@ def __invert__(self): return self new_data = self._mgr.apply(operator.invert) - result = self._from_mgr(new_data) - return result.__finalize__(self, method="__invert__") + result = self._constructor(new_data).__finalize__(self, method="__invert__") + return result def __nonzero__(self): raise ValueError( @@ -3378,7 +3365,7 @@ class max_speed new_data = self._mgr.take( indices, axis=self._get_block_manager_axis(axis), verify=True ) - return self._from_mgr(new_data).__finalize__(self, method="take") + return self._constructor(new_data).__finalize__(self, method="take") def _take_with_is_copy(self: FrameOrSeries, indices, axis=0) -> FrameOrSeries: """ @@ -3584,7 +3571,7 @@ def _slice(self: FrameOrSeries, slobj: slice, axis=0) -> FrameOrSeries: """ assert isinstance(slobj, slice), type(slobj) axis = self._get_block_manager_axis(axis) - result = self._from_mgr(self._mgr.get_slice(slobj, axis=axis)) + result = self._constructor(self._mgr.get_slice(slobj, axis=axis)) result = result.__finalize__(self) # this could be a view @@ -4523,7 +4510,7 @@ def _reindex_with_indexers( if copy and new_data is self._mgr: new_data = new_data.copy() - return self._from_mgr(new_data).__finalize__(self) + return self._constructor(new_data).__finalize__(self) def filter( self: FrameOrSeries, @@ -5289,7 +5276,7 @@ def _consolidate(self, inplace: bool_t = False): else: f = lambda: self._mgr.consolidate() cons_data = self._protect_consolidate(f) - return self._from_mgr(cons_data).__finalize__(self) + return self._constructor(cons_data).__finalize__(self) @property def _is_mixed_type(self) -> bool_t: @@ -5318,10 +5305,10 @@ def _check_inplace_setting(self, value) -> bool_t: return True def _get_numeric_data(self): - return self._from_mgr(self._mgr.get_numeric_data()).__finalize__(self) + return self._constructor(self._mgr.get_numeric_data()).__finalize__(self,) def _get_bool_data(self): - return self._from_mgr(self._mgr.get_bool_data()).__finalize__(self) + return self._constructor(self._mgr.get_bool_data()).__finalize__(self,) # ---------------------------------------------------------------------- # Internal Interface Methods @@ -5588,7 +5575,7 @@ def astype( else: # else, only a single dtype is given new_data = self._mgr.astype(dtype=dtype, copy=copy, errors=errors,) - return self._from_mgr(new_data).__finalize__(self, method="astype") + return self._constructor(new_data).__finalize__(self, method="astype") # GH 19920: retain column metadata after concat result = pd.concat(results, axis=1, copy=False) @@ -5702,7 +5689,7 @@ def copy(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: """ data = self._mgr.copy(deep=deep) self._clear_item_cache() - return self._from_mgr(data).__finalize__(self, method="copy") + return self._constructor(data).__finalize__(self, method="copy") def __copy__(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: return self.copy(deep=deep) @@ -5753,7 +5740,7 @@ def _convert( validate_bool_kwarg(timedelta, "timedelta") validate_bool_kwarg(coerce, "coerce") validate_bool_kwarg(copy, "copy") - return self._from_mgr( + return self._constructor( self._mgr.convert( datetime=datetime, numeric=numeric, @@ -5804,9 +5791,9 @@ def infer_objects(self: FrameOrSeries) -> FrameOrSeries: # numeric=False necessary to only soft convert; # python objects will still be converted to # native numpy numeric types - return self._from_mgr( + return self._constructor( self._mgr.convert( - datetime=True, numeric=False, timedelta=True, coerce=False, copy=True, + datetime=True, numeric=False, timedelta=True, coerce=False, copy=True ) ).__finalize__(self, method="infer_objects") @@ -6910,7 +6897,7 @@ def interpolate( **kwargs, ) - result = self._from_mgr(new_data) + result = self._constructor(new_data) if axis == 1: result = result.T if inplace: @@ -8525,7 +8512,7 @@ def _align_series( if copy and fdata is self._mgr: fdata = fdata.copy() - left = self._from_mgr(fdata) + left = self._constructor(fdata) if ridx is None: right = other @@ -8677,7 +8664,7 @@ def _where( new_data = self._mgr.putmask( mask=cond, new=other, align=align, axis=block_axis, ) - result = self._from_mgr(new_data) + result = self._constructor(new_data) return self._update_inplace(result) else: @@ -8689,7 +8676,7 @@ def _where( try_cast=try_cast, axis=block_axis, ) - result = self._from_mgr(new_data) + result = self._constructor(new_data) return result.__finalize__(self) _shared_docs[ @@ -8962,7 +8949,7 @@ def shift( else: return self.tshift(periods, freq) - return self._from_mgr(new_data).__finalize__(self, method="shift") + return self._constructor(new_data).__finalize__(self, method="shift") def slice_shift(self: FrameOrSeries, periods: int = 1, axis=0) -> FrameOrSeries: """ @@ -11216,8 +11203,9 @@ def block_accum_func(blk_values): result = self._mgr.apply(block_accum_func) - obj = self._from_mgr(result) - return obj.__finalize__(self, method=name) + d = self._construct_axes_dict() + d["copy"] = False + return self._constructor(result, **d).__finalize__(self, method=name) return set_function_name(cum_func, name, cls) diff --git a/pandas/core/series.py b/pandas/core/series.py index c9684d0985173..2f4ca61a402dc 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -204,6 +204,17 @@ def __init__( self, data=None, index=None, dtype=None, name=None, copy=False, fastpath=False ): + if ( + isinstance(data, SingleBlockManager) + and index is None + and dtype is None + and copy is False + ): + # GH#33357 called with just the SingleBlockManager + NDFrame.__init__(self, data) + self.name = name + return + # we are called internally, so short-circuit if fastpath: @@ -827,9 +838,8 @@ def take(self, indices, axis=0, is_copy=None, **kwargs) -> "Series": new_index = self.index.take(indices) new_values = self._values.take(indices) - return self._constructor( - new_values, index=new_index, fastpath=True - ).__finalize__(self, method="take") + result = self._constructor(new_values, index=new_index, fastpath=True) + return result.__finalize__(self, method="take") def _take_with_is_copy(self, indices, axis=0): """