Skip to content

Commit

Permalink
API: Added ExtensionArray constructor from scalars (#19913)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAugspurger authored and jorisvandenbossche committed Mar 2, 2018
1 parent d30d165 commit 49f09cc
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 2 deletions.
20 changes: 20 additions & 0 deletions pandas/core/arrays/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ExtensionArray(object):
The interface includes the following abstract methods that must be
implemented by subclasses:
* _constructor_from_sequence
* __getitem__
* __len__
* dtype
Expand Down Expand Up @@ -56,6 +57,25 @@ class ExtensionArray(object):
# '_typ' is for pandas.core.dtypes.generic.ABCExtensionArray.
# Don't override this.
_typ = 'extension'

# ------------------------------------------------------------------------
# Constructors
# ------------------------------------------------------------------------
@classmethod
def _constructor_from_sequence(cls, scalars):
"""Construct a new ExtensionArray from a sequence of scalars.
Parameters
----------
scalars : Sequence
Each element will be an instance of the scalar type for this
array, ``cls.dtype.type``.
Returns
-------
ExtensionArray
"""
raise AbstractMethodError(cls)

# ------------------------------------------------------------------------
# Must be a Sequence
# ------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions pandas/core/arrays/categorical.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ def __init__(self, values, categories=None, ordered=None, dtype=None,
self._dtype = self._dtype.update_dtype(dtype)
self._codes = coerce_indexer_dtype(codes, dtype.categories)

@classmethod
def _constructor_from_sequence(cls, scalars):
return cls(scalars)

@property
def categories(self):
"""The categories of this categorical.
Expand Down
5 changes: 5 additions & 0 deletions pandas/tests/extension/base/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

class BaseConstructorsTests(BaseExtensionTests):

def test_array_from_scalars(self, data):
scalars = [data[0], data[1], data[2]]
result = data._constructor_from_sequence(scalars)
assert isinstance(result, type(data))

def test_series_constructor(self, data):
result = pd.Series(data)
assert result.dtype == data.dtype
Expand Down
4 changes: 4 additions & 0 deletions pandas/tests/extension/decimal/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ def __init__(self, values):

self.values = values

@classmethod
def _constructor_from_sequence(cls, scalars):
return cls(scalars)

def __getitem__(self, item):
if isinstance(item, numbers.Integral):
return self.values[item]
Expand Down
10 changes: 8 additions & 2 deletions pandas/tests/extension/json/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@ def __init__(self, values):
raise TypeError
self.data = values

@classmethod
def _constructor_from_sequence(cls, scalars):
return cls(scalars)

def __getitem__(self, item):
if isinstance(item, numbers.Integral):
return self.data[item]
elif isinstance(item, np.ndarray) and item.dtype == 'bool':
return type(self)([x for x, m in zip(self, item) if m])
return self._constructor_from_sequence([
x for x, m in zip(self, item) if m
])
else:
return type(self)(self.data[item])

Expand Down Expand Up @@ -77,7 +83,7 @@ def isna(self):
def take(self, indexer, allow_fill=True, fill_value=None):
output = [self.data[loc] if loc != -1 else self._na_value
for loc in indexer]
return type(self)(output)
return self._constructor_from_sequence(output)

def copy(self, deep=False):
return type(self)(self.data[:])
Expand Down

0 comments on commit 49f09cc

Please sign in to comment.