Skip to content

CLN: split off frozen (immutable) data structures into pandas/indexes/frozen.py #15477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions pandas/compat/pickle_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,40 @@ def load_reduce(self):

stack[-1] = value


# if classes are moved, provide compat here
_class_locations_map = {

# 15477
('pandas.core.base', 'FrozenNDArray'): ('pandas.indexes.frozen', 'FrozenNDArray'),
('pandas.core.base', 'FrozenList'): ('pandas.indexes.frozen', 'FrozenList')
}


# our Unpickler sub-class to override methods and some dispatcher
# functions for compat

if compat.PY3:
class Unpickler(pkl._Unpickler):
pass

def find_class(self, module, name):
# override superclass
key = (module, name)
module, name = _class_locations_map.get(key, key)
return super(Unpickler, self).find_class(module, name)

else:

class Unpickler(pkl.Unpickler):
pass

def find_class(self, module, name):
# override superclass
key = (module, name)
module, name = _class_locations_map.get(key, key)
__import__(module)
mod = sys.modules[module]
klass = getattr(mod, name)
return klass

Unpickler.dispatch = copy.copy(Unpickler.dispatch)
Unpickler.dispatch[pkl.REDUCE[0]] = load_reduce
Expand All @@ -76,8 +104,6 @@ def load_newobj(self):
self.stack[-1] = obj
Unpickler.dispatch[pkl.NEWOBJ[0]] = load_newobj

# py3 compat


def load_newobj_ex(self):
kwargs = self.stack.pop()
Expand Down
105 changes: 0 additions & 105 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from pandas.util.decorators import (Appender, cache_readonly,
deprecate_kwarg, Substitution)
from pandas.core.common import AbstractMethodError
from pandas.formats.printing import pprint_thing

_shared_docs = dict()
_indexops_doc_kwargs = dict(klass='IndexOpsMixin', inplace='',
Expand Down Expand Up @@ -694,110 +693,6 @@ def _gotitem(self, key, ndim, subset=None):
return self


class FrozenList(PandasObject, list):

"""
Container that doesn't allow setting item *but*
because it's technically non-hashable, will be used
for lookups, appropriately, etc.
"""
# Sidenote: This has to be of type list, otherwise it messes up PyTables
# typechecks

def __add__(self, other):
if isinstance(other, tuple):
other = list(other)
return self.__class__(super(FrozenList, self).__add__(other))

__iadd__ = __add__

# Python 2 compat
def __getslice__(self, i, j):
return self.__class__(super(FrozenList, self).__getslice__(i, j))

def __getitem__(self, n):
# Python 3 compat
if isinstance(n, slice):
return self.__class__(super(FrozenList, self).__getitem__(n))
return super(FrozenList, self).__getitem__(n)

def __radd__(self, other):
if isinstance(other, tuple):
other = list(other)
return self.__class__(other + list(self))

def __eq__(self, other):
if isinstance(other, (tuple, FrozenList)):
other = list(other)
return super(FrozenList, self).__eq__(other)

__req__ = __eq__

def __mul__(self, other):
return self.__class__(super(FrozenList, self).__mul__(other))

__imul__ = __mul__

def __reduce__(self):
return self.__class__, (list(self),)

def __hash__(self):
return hash(tuple(self))

def _disabled(self, *args, **kwargs):
"""This method will not function because object is immutable."""
raise TypeError("'%s' does not support mutable operations." %
self.__class__.__name__)

def __unicode__(self):
return pprint_thing(self, quote_strings=True,
escape_chars=('\t', '\r', '\n'))

def __repr__(self):
return "%s(%s)" % (self.__class__.__name__,
str(self))

__setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled
pop = append = extend = remove = sort = insert = _disabled


class FrozenNDArray(PandasObject, np.ndarray):

# no __array_finalize__ for now because no metadata
def __new__(cls, data, dtype=None, copy=False):
if copy is None:
copy = not isinstance(data, FrozenNDArray)
res = np.array(data, dtype=dtype, copy=copy).view(cls)
return res

def _disabled(self, *args, **kwargs):
"""This method will not function because object is immutable."""
raise TypeError("'%s' does not support mutable operations." %
self.__class__)

__setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled
put = itemset = fill = _disabled

def _shallow_copy(self):
return self.view()

def values(self):
"""returns *copy* of underlying array"""
arr = self.view(np.ndarray).copy()
return arr

def __unicode__(self):
"""
Return a string representation for this object.

Invoked by unicode(df) in py2 only. Yields a Unicode String in both
py2/py3.
"""
prepr = pprint_thing(self, escape_chars=('\t', '\r', '\n'),
quote_strings=True)
return "%s(%s, dtype='%s')" % (type(self).__name__, prepr, self.dtype)


class IndexOpsMixin(object):
""" common ops mixin to support a unified inteface / docs for Series /
Index
Expand Down
13 changes: 2 additions & 11 deletions pandas/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,15 @@
needs_i8_conversion,
is_iterator, is_list_like,
is_scalar)
from pandas.types.cast import _coerce_indexer_dtype
from pandas.core.common import (is_bool_indexer,
_values_from_object,
_asarray_tuplesafe)

from pandas.core.base import (PandasObject, FrozenList, FrozenNDArray,
IndexOpsMixin)
from pandas.core.base import PandasObject, IndexOpsMixin
import pandas.core.base as base
from pandas.util.decorators import (Appender, Substitution, cache_readonly,
deprecate, deprecate_kwarg)
from pandas.indexes.frozen import FrozenList
import pandas.core.common as com
import pandas.types.concat as _concat
import pandas.core.missing as missing
Expand Down Expand Up @@ -3844,14 +3843,6 @@ def _get_na_value(dtype):
np.timedelta64: tslib.NaT}.get(dtype, np.nan)


def _ensure_frozen(array_like, categories, copy=False):
array_like = _coerce_indexer_dtype(array_like, categories)
array_like = array_like.view(FrozenNDArray)
if copy:
array_like = array_like.copy()
return array_like


def _ensure_has_len(seq):
"""If seq is an iterator, put its values into a list."""
try:
Expand Down
126 changes: 126 additions & 0 deletions pandas/indexes/frozen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""
frozen (immutable) data structures to support MultiIndexing

These are used for:

- .names (FrozenList)
- .levels & .labels (FrozenNDArray)

"""

import numpy as np
from pandas.core.base import PandasObject
from pandas.types.cast import _coerce_indexer_dtype
from pandas.formats.printing import pprint_thing


class FrozenList(PandasObject, list):

"""
Container that doesn't allow setting item *but*
because it's technically non-hashable, will be used
for lookups, appropriately, etc.
"""
# Sidenote: This has to be of type list, otherwise it messes up PyTables
# typechecks

def __add__(self, other):
if isinstance(other, tuple):
other = list(other)
return self.__class__(super(FrozenList, self).__add__(other))

__iadd__ = __add__

# Python 2 compat
def __getslice__(self, i, j):
return self.__class__(super(FrozenList, self).__getslice__(i, j))

def __getitem__(self, n):
# Python 3 compat
if isinstance(n, slice):
return self.__class__(super(FrozenList, self).__getitem__(n))
return super(FrozenList, self).__getitem__(n)

def __radd__(self, other):
if isinstance(other, tuple):
other = list(other)
return self.__class__(other + list(self))

def __eq__(self, other):
if isinstance(other, (tuple, FrozenList)):
other = list(other)
return super(FrozenList, self).__eq__(other)

__req__ = __eq__

def __mul__(self, other):
return self.__class__(super(FrozenList, self).__mul__(other))

__imul__ = __mul__

def __reduce__(self):
return self.__class__, (list(self),)

def __hash__(self):
return hash(tuple(self))

def _disabled(self, *args, **kwargs):
"""This method will not function because object is immutable."""
raise TypeError("'%s' does not support mutable operations." %
self.__class__.__name__)

def __unicode__(self):
return pprint_thing(self, quote_strings=True,
escape_chars=('\t', '\r', '\n'))

def __repr__(self):
return "%s(%s)" % (self.__class__.__name__,
str(self))

__setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled
pop = append = extend = remove = sort = insert = _disabled


class FrozenNDArray(PandasObject, np.ndarray):

# no __array_finalize__ for now because no metadata
def __new__(cls, data, dtype=None, copy=False):
if copy is None:
copy = not isinstance(data, FrozenNDArray)
res = np.array(data, dtype=dtype, copy=copy).view(cls)
return res

def _disabled(self, *args, **kwargs):
"""This method will not function because object is immutable."""
raise TypeError("'%s' does not support mutable operations." %
self.__class__)

__setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled
put = itemset = fill = _disabled

def _shallow_copy(self):
return self.view()

def values(self):
"""returns *copy* of underlying array"""
arr = self.view(np.ndarray).copy()
return arr

def __unicode__(self):
"""
Return a string representation for this object.

Invoked by unicode(df) in py2 only. Yields a Unicode String in both
py2/py3.
"""
prepr = pprint_thing(self, escape_chars=('\t', '\r', '\n'),
quote_strings=True)
return "%s(%s, dtype='%s')" % (type(self).__name__, prepr, self.dtype)


def _ensure_frozen(array_like, categories, copy=False):
array_like = _coerce_indexer_dtype(array_like, categories)
array_like = array_like.view(FrozenNDArray)
if copy:
array_like = array_like.copy()
return array_like
6 changes: 3 additions & 3 deletions pandas/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
UnsortedIndexError)


from pandas.core.base import FrozenList
import pandas.core.base as base
from pandas.util.decorators import (Appender, cache_readonly,
deprecate, deprecate_kwarg)
Expand All @@ -39,9 +38,10 @@

from pandas.core.config import get_option

from pandas.indexes.base import (Index, _ensure_index, _ensure_frozen,
from pandas.indexes.base import (Index, _ensure_index,
_get_na_value, InvalidIndexError,
_index_shared_docs)
from pandas.indexes.frozen import FrozenNDArray, FrozenList, _ensure_frozen
import pandas.indexes.base as ibase
_index_doc_kwargs = dict(ibase._index_doc_kwargs)
_index_doc_kwargs.update(
Expand Down Expand Up @@ -1276,7 +1276,7 @@ def _assert_take_fillable(self, values, indices, allow_fill=True,
for new_label in taken:
label_values = new_label.values()
label_values[mask] = na_value
masked.append(base.FrozenNDArray(label_values))
masked.append(FrozenNDArray(label_values))
taken = masked
else:
taken = [lab.take(indices) for lab in self.labels]
Expand Down
Loading