"""HDF5ExtensionArray module."""
import numpy as np
import h5py
import uuid
from h5pandas.h5datatype import HDF5Dtype
import numbers
from typing import (
Any,
)
from pandas._typing import (
AxisInt,
Dtype,
InterpolateOptions,
NpDtype,
)
import pandas
from pandas import (
Index,
Series,
)
from pandas.compat.numpy import function as nv
from pandas._libs import lib
from pandas.core.dtypes.common import (
is_scalar,
)
from pandas.core import (
arraylike,
nanops,
missing,
)
from pandas.core.indexers import (
check_array_indexer,
)
from pandas.core.dtypes.generic import (
ABCDataFrame,
ABCIndex,
ABCSeries,
)
from pandas.core.dtypes.missing import isna
from pandas._typing import (
ArrayLike,
Iterator,
PositionalIndexer,
)
from pandas.core.algorithms import _ensure_arraylike, value_counts_arraylike
[docs]
class HDF5ExtensionArray(
np.lib.mixins.NDArrayOperatorsMixin, pandas.api.extensions.ExtensionArray
):
"""HDF5ExtensionArray."""
_HANDLED_TYPES = (np.ndarray, numbers.Number)
# __pandas_priority__ = 5000
# __array_priority__ = 1000
def __init__(
self, dataset: h5py.Dataset, column_index: int = 0, dtype=None
) -> None:
"""
Create a HDF5ExtensionArray from a dataset and a column number.
Parameters
----------
dataset : h5py.Dataset
column_index : int, optional
index of the column inside the dataset that will become an
HDF5ExtensionArray. The default is 0.
dtype : dtype or None, optional
The dtype of the array. The default is None.
"""
if isinstance(dataset, HDF5ExtensionArray):
dataset = dataset._dataset
if not isinstance(dataset, (h5py.Dataset)):
# if dtype is None:
# if isinstance(dataset, (list, tuple)):
# dataset = np.array()
# dtype = dataset.dtype
if not isinstance(dataset, np.ndarray):
dataset = np.array(dataset, dtype=dtype)
column_index = 0
self._dataset = dataset
self._column_index = column_index
self._dtype = HDF5Dtype(self._dataset.dtype)
@classmethod
def _from_sequence(
cls, scalars, *, dtype: np.dtype | None = None, copy: bool = False
):
"""
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`` or be converted into this type in this method.
dtype: dtype, optional
Construct for this particular dtype. This should be a np.dtype
compatible with the ExtensionArray.
copy: bool, default False
If True, copy the underlying data.
Returns
-------
ExtensionArray
"""
if dtype is None:
return HDF5ExtensionArray(np.array(scalars, dtype=dtype))
if not isinstance(dtype, np.dtype):
dtype = np.dtype(dtype.type)
return HDF5ExtensionArray(np.array(scalars, dtype=dtype))
@classmethod
def _from_sequence_of_strings(
cls, strings, *, dtype: np.dtype | None = None, copy: bool = False
):
"""
Construct a new ExtensionArray from a sequence of strings.
Parameters
----------
strings: Sequence
Each element will be an instance of the scalar type for this
array, ``cls.dtype.type``.
dtype: dtype, optional
Construct for this particular dtype. This should be a np.dtype
compatible with the ExtensionArray.
copy: bool, default False
If True, copy the underlying data.
Returns
-------
ExtensionArray
"""
return HDF5ExtensionArray(np.array(strings, dtype=dtype))
@classmethod
def _from_factorized(cls, values, original):
"""
Reconstruct an ExtensionArray after factorization.
Parameters
----------
values: ndarray
An integer ndarray with the factorized values.
original: ExtensionArray
The original ExtensionArray that factorize was called on.
See Also
--------
factorize: Top-level factorize method that dispatches here.
ExtensionArray.factorize: Encode the extension array as an enumerated type.
"""
return HDF5ExtensionArray(values)
def __getitem__(self, item: PositionalIndexer):
"""
Select a subset of self.
Parameters
----------
item: int, slice, or ndarray
* int: The position in 'self' to get.
* slice: A slice object, where 'start', 'stop', and 'step' are
integers or None
* ndarray: A 1-d boolean NumPy ndarray the same length as 'self'
* list[int]: A list of int
Returns
-------
item: scalar or ExtensionArray
Notes
-----
For scalar ``item``, return a scalar value suitable for the array's
type. This should be an instance of ``self.dtype.type``.
For slice ``key``, return an instance of ``ExtensionArray``, even
if the slice is length 0 or 1.
For a boolean mask, return an instance of ``ExtensionArray``, filtered
to the values where ``item`` is True.
"""
item = check_array_indexer(self, item)
if is_scalar(item):
try:
if self._dataset.ndim > 1:
return self._dataset[item, self._column_index, ...]
else:
return self._dataset[item]
except (ValueError, TypeError):
raise IndexError(
"only integers, slices (`:`), ellipsis (`...`), numpy.newaxis "
"(`None`) and integer or boolean arrays are valid indices"
)
elif isinstance(item, slice) and item == slice(None):
return HDF5ExtensionArray(self._dataset, self._column_index)
elif isinstance(item, tuple) and (slice(None) in item or Ellipsis in item):
if self._dataset.ndim > 1:
dataset = self._dataset[*item, self._column_index, ...]
else:
dataset = self._dataset[*item]
return HDF5ExtensionArray(dataset)
else:
# FIXME : AVOID COPY HERE
if self._dataset.ndim > 1:
dataset = self._dataset[item, self._column_index, ...]
else:
dataset = self._dataset[item]
return HDF5ExtensionArray(dataset)
def __setitem__(self, key, value) -> None:
"""
Set one or more values inplace.
This method is not required to satisfy the pandas extension array
interface.
Parameters
----------
key: int, ndarray, or slice
When called from , e.g. ``Series.__setitem__``, ``key`` will be
one of
* scalar int
* ndarray of integers.
* boolean ndarray
* slice object
value: Extensionnp.dtype.type, Sequence[Extensionnp.dtype.type], or object
value or values to be set of ``key``.
Returns
-------
None
"""
# Some notes to the ExtensionArray implementor who may have ended up
# here. While this method is not required for the interface, if you
# *do* choose to implement __setitem__, then some semantics should be
# observed:
#
# * Setting multiple values : ExtensionArrays should support setting
# multiple values at once, 'key' will be a sequence of integers and
# 'value' will be a same-length sequence.
#
# * Broadcasting : For a sequence 'key' and a scalar 'value',
# each position in 'key' should be set to 'value'.
#
# * Coercion : Most users will expect basic coercion to work. For
# example, a string like '2018-01-01' is coerced to a datetime
# when setting on a datetime64ns array. In general, if the
# __init__ method coerces that value, then so should __setitem__
# Note, also, that Series/DataFrame.where internally use __setitem__
# on a copy of the data.
# FIXME
if self._dataset.ndim > 1:
return self._dataset.__setitem__((key, self._column_index, Ellipsis), value)
else:
return self._dataset.__setitem__(key, value)
def __len__(self) -> int:
"""
Length of this array.
Returns
-------
length: int
"""
return len(self._dataset)
def __iter__(self) -> Iterator[Any]:
"""Iterate over elements of the array."""
# This needs to be implemented so that pandas recognizes extension
# arrays as list-like. The default implementation makes successive
# calls to ``__getitem__``, which may be slower than necessary.
for i in range(len(self)):
yield self[i]
def isnan(self) -> np.ndarray:
return self.isna()
[docs]
def isna(self) -> np.ndarray:
"""
A 1-D array indicating if each value is missing.
Returns
-------
numpy.ndarray or pandas.api.extensions.ExtensionArray
In most cases, this should return a NumPy ndarray. For
exceptional cases like ``SparseArray``, where returning
an ndarray would be expensive, an ExtensionArray may be
returned.
Notes
-----
If returning an ExtensionArray, then
* ``na_values._is_boolean`` should be True
* `na_values` should implement: func: `ExtensionArray._reduce`
* ``na_values.any`` and ``na_values.all`` should be implemented
"""
return np.isnan(self._ndarray)
def __contains__(self, item: object) -> bool | np.bool_:
"""Return for `item in self`."""
# GH37867
# comparisons of any item to pd.NA always return pd.NA, so e.g. "a" in [pd.NA]
# would raise a TypeError. The implementation below works around that.
if is_scalar(item) and isna(item):
if not self._can_hold_na:
return False
elif item is self.dtype.na_value or isinstance(item, self.dtype.type):
return self._hasna
else:
return False
else:
# error: Item "ExtensionArray" of "Union[ExtensionArray, ndarray]" has no
# attribute "any"
return (item == self._ndarray).any() # type: ignore[union-attr]
@classmethod
def _concat_same_type(cls, to_concat):
"""
Concatenate multiple instances of H5pyArray.
Args:
to_concat(list): List of H5pyArray instances to concatenate.
Returns:
H5pyArray: The concatenated H5pyArray.
"""
f = h5py.File(
"h5pyArray_concat{}".format(uuid.uuid4()),
"w",
libver="latest",
driver="core",
backing_store=False,
locking=False,
)
# TODO : utiliser l'argument out ?
array = np.concatenate(to_concat, axis=0)
dataset = f.create_dataset(
"extension",
data=array,
shape=(len(array), 1),
maxshape=(None, None),
chunks=(len(array), 1),
)
return cls(dataset)
[docs]
def view(self, dtype: Dtype | None = None) -> ArrayLike:
"""
Return a view on the array.
Parameters
----------
dtype : str, np.dtype, or ExtensionDtype, optional
Default None.
Returns
-------
ExtensionArray or np.ndarray
A view on the :class:`ExtensionArray`'s data.
Examples
--------
This gives view on the underlying data of an ``ExtensionArray`` and is not a
copy. Modifications on either the view or the original ``ExtensionArray``
will be reflectd on the underlying data:
>>> arr = pd.array([1, 2, 3])
>>> arr2 = arr.view()
>>> arr[0] = 2
>>> arr2
<IntegerArray>
[2, 2, 3]
Length: 3, dtype: Int64
"""
# NB:
# - This must return a *new* object referencing the same data, not self.
# - The only case that *must* be implemented is with dtype=None,
# giving a view with the same dtype as self.
if dtype is not None:
raise NotImplementedError(dtype)
return self[:]
[docs]
def to_numpy(
self,
dtype: np.dtype | None = None,
copy: bool = False,
na_value: object = lib.no_default,
) -> np.ndarray:
"""
Convert to a NumPy ndarray.
This is similar to: meth: `numpy.asarray`, but may provide additional control
over how the conversion is done.
Parameters
----------
dtype: str or numpy.dtype, optional
The dtype to pass to: meth: `numpy.asarray`.
copy: bool, default False
Whether to ensure that the returned value is a not a view on
another array. Note that ``copy = False`` does not *ensure * that
``to_numpy()`` is no-copy. Rather, ``copy = True`` ensure that
a copy is made, even if not strictly necessary.
na_value: Any, optional
The value to use for missing values. The default value depends
on `dtype` and the type of the array.
Returns
-------
numpy.ndarray
"""
# We do the copy anyway
num_array = self._ndarray
if dtype is None:
return num_array
try:
num_array = num_array.view(dtype)
except TypeError:
num_array = num_array.astype(dtype)
return num_array
def __array__(self, *args, **kwargs):
if self._dataset.ndim > 1:
return self._dataset[:, self._column_index, ...]
else:
return self._dataset
# ------------------------------------------------------------------------
# Required attributes
# ------------------------------------------------------------------------
@property
def dtype(self) -> HDF5Dtype:
"""An instance of 'np.dtype'."""
return self._dtype
@property
def shape(self):
"""Return a tuple of the array dimensions."""
return (len(self),)
@property
def size(self) -> int:
"""The number of elements in the array."""
# error: Incompatible return value type (got "signedinteger[_64Bit]",
# expected "int") [return-value]
return np.prod(self.shape) # type: ignore[return-value]
@property
def ndim(self) -> int:
"""Extension Arrays are only allowed to be 1-dimensional."""
return 1
@property
def nbytes(self) -> int:
"""The number of bytes needed to store this object in memory."""
# If this is expensive to compute, return an approximate lower bound
# on the number of bytes needed.
return self.__len__() * self.dtype._numpy_dtype.itemsize
@property
def _ndarray(self):
if self._dataset.ndim > 1:
return self._dataset[:, self._column_index, ...]
else:
return self._dataset
[docs]
def astype(self, dtype, copy: bool = True) -> ArrayLike:
"""
Cast to a NumPy array or ExtensionArray with 'dtype'.
Parameters
----------
dtype: str or dtype
Typecode or data-type to which the array is cast.
copy: bool, default True
Whether to copy the data, even if not necessary. If False,
a copy is made only if the old dtype does not match the
new dtype.
Returns
-------
np.ndarray or pandas.api.extensions.ExtensionArray
An ExtensionArray if dtype is Extensionnp.dtype,
Otherwise a NumPy ndarray with 'dtype' for its dtype.
"""
try:
dtype = dtype._numpy_dtype
except Exception:
pass
return HDF5ExtensionArray(
self._ndarray.astype(dtype), self._column_index, dtype=dtype
)
[docs]
def take(self, indices, allow_fill=False, fill_value=None):
from pandas.core.algorithms import take
# is worth to try to select only a few indices ?
data = self._ndarray
if allow_fill and fill_value is None:
fill_value = self.dtype.na_value
# fill value should always be translated from the scalar
# type for the array, to the physical storage type for
# the data, before passing to take.
result = take(data, indices, fill_value=fill_value, allow_fill=allow_fill)
return self._from_sequence(result, dtype=self.dtype)
[docs]
def copy(self):
"""
Return a copy of the array.
Returns
-------
ExtensionArray
"""
return HDF5ExtensionArray(np.array(self._ndarray))
def _accumulate(self, name: str, *, skipna: bool = True, **kwargs):
"""
Return an ExtensionArray performing an accumulation operation.
The underlying data type might change.
Parameters
----------
name: str
Name of the function, supported values are:
- cummin
- cummax
- cumsum
- cumprod
skipna: bool, default True
If True, skip NA values.
**kwargs
Additional keyword arguments passed to the accumulation function.
Currently, there is no supported kwarg.
Returns
-------
array
Raises
------
NotImplementedError: subclass does not define accumulations
"""
if skipna:
meth = getattr(self._ndarray, name, None)
else:
array = self._ndarray
meth = getattr(array[not np.isnan(array)], name, None)
if meth is None:
raise TypeError(
f"'{type(self).__name__}' with dtype {self.dtype} "
f"does not support reduction '{name}'"
)
return meth(**kwargs)
def _reduce(
self,
name: str,
*,
skipna: bool = True,
keepdims: bool = False,
min_count: int = 0,
**kwargs,
):
"""
Return a scalar result of performing the reduction operation.
Parameters
----------
name : str
Name of the function, supported values are:
{ any, all, min, max, sum, mean, median, prod,
std, var, sem, kurt, skew }.
skipna : bool, default True
If True, skip NaN values.
keepdims : bool, default False
If False, a scalar is returned.
If True, the result has dimension with size one along the reduced axis.
min_count : int,
The required number of valid values to perform the operation. If fewer
than ``min_count`` non-NA values are present the result will be NA.
**kwargs
Additional keyword arguments passed to the reduction function.
Currently, `ddof` is the only supported kwarg.
Returns
-------
scalar
Raises
------
TypeError : subclass does not define reductions
Examples
--------
>>> pd.array([1, 2, 3])._reduce("min")
1
"""
if skipna:
array = self._ndarray
else:
array = self._ndarray[~np.isnan(self._ndarray)]
if len(array) < min_count:
return self.dtype.na_value
meth = getattr(array, name, None)
if meth is None:
# kurt, skew etc...
meth = getattr(self, name, None)
if meth is None:
# median is not attribute of
meth = lambda x=None: getattr(np, name, None)(self, x)
if meth is None:
raise TypeError(
f"'{type(self).__name__}' with dtype {self.dtype} "
f"does not support reduction '{name}'"
)
result = meth(**kwargs)
if keepdims:
result = type(self)([result])
return result
def __array_ufunc__(self, ufunc: np.ufunc, method: str, *inputs, **kwargs):
# Lightly modified version of
# https://numpy.org/doc/stable/reference/generated/numpy.lib.mixins.NDArrayOperatorsMixin.html
# The primary modification is not boxing scalar return values
# in NumpyExtensionArray, since pandas' ExtensionArrays are 1-d.
if any(
isinstance(other, (ABCSeries, ABCIndex, ABCDataFrame)) for other in inputs
):
return NotImplemented
# Operations likes cos are much faster with ufunc
out = kwargs.get("out", ())
inputs = tuple(
inp._ndarray if isinstance(inp, HDF5ExtensionArray) else inp
for inp in inputs
)
result = arraylike.maybe_dispatch_ufunc_to_dunder_op(
self._ndarray, ufunc, method, *inputs, **kwargs
)
if result is not NotImplemented:
if ufunc.nout > 1:
# multiple return values; re-box array-like results
return tuple(type(self)(x) for x in result)
else:
return type(self)(result)
if "out" in kwargs:
# e.g. test_ufunc_unary
return arraylike.dispatch_ufunc_with_out(
self._ndarray, ufunc, method, *inputs, **kwargs
)
if method == "reduce":
result = arraylike.dispatch_reduction_ufunc(
self._ndarray, ufunc, method, *inputs, **kwargs
)
if result is not NotImplemented:
# e.g. tests.series.test_ufunc.TestNumpyReductions
return type(self)(result)
# Defer to the implementation of the ufunc on unwrapped values.
if out:
kwargs["out"] = tuple(inputs)
result = getattr(ufunc, method)(*inputs, **kwargs)
if ufunc.nout > 1:
# multiple return values; re-box array-like results
return tuple(type(self)(x) for x in result)
elif method == "at":
# no return value
return None
elif method == "reduce":
if isinstance(result, np.ndarray):
# e.g. test_np_reduce_2d
return type(self)(result)
# e.g. test_np_max_nested_tuples
return type(self)(result)
else:
# one return value; re-box array-like results
return type(self)(result)
[docs]
def value_counts(self, dropna: bool = True) -> Series:
"""
Return a Series containing counts of unique values.
Parameters
----------
dropna : bool, optional
Don't include counts of NaN. The default is True.
Returns
-------
Series
value_counts.
"""
values = _ensure_arraylike(self._ndarray, func_name="value_counts")
keys, counts, _ = value_counts_arraylike(values, dropna)
if keys.dtype == np.float16:
keys = keys.astype(np.float32)
# For backwards compatibility, we let Index do its normal type
# inference, _except_ for if if infers from object to bool.
idx = Index(keys)
if idx.dtype == bool and keys.dtype == object:
idx = idx.astype(object)
result = Series(counts, index=idx, copy=False)
return result
[docs]
def argsort(self):
"""Return for indices of sorted values."""
return type(self)(np.argsort(self._ndarray))
[docs]
def argmin(self):
"""Return for indice of min value."""
return type(self)(np.argmin(self._ndarray))
[docs]
def argmax(self):
"""Return for indice of max value."""
return type(self)(np.argmax(self._ndarray))
[docs]
def dropna(self):
"""Remove missing values."""
return type(self)(self._ndarray[~np.isnan(self._ndarray)])
[docs]
def interpolate(
self,
*,
method: InterpolateOptions,
axis: int,
index: Index,
limit,
limit_direction,
limit_area,
copy: bool,
**kwargs,
):
"""See NDFrame.interpolate.__doc__."""
# NB: we return type(self) even if copy=False
if not copy:
out_data = self._ndarray
else:
out_data = self._ndarray.copy()
# TODO: assert we have floating dtype?
missing.interpolate_2d_inplace(
out_data,
method=method,
axis=axis,
index=index,
limit=limit,
limit_direction=limit_direction,
limit_area=limit_area,
**kwargs,
)
if not copy:
return self
return type(self)._simple_new(out_data, dtype=self.dtype)
def memory_usage(self, *args, **kwargs):
return self.nbytes
# error: Signature of "__eq__" incompatible with supertype "object"
def __eq__(self, other: Any) -> ArrayLike: # type: ignore[override]
"""Return for `self == other` (element-wise equality)."""
# Implementer note: this should return a boolean numpy ndarray or
# a boolean ExtensionArray.
# When `other` is one of Series, Index, or DataFrame, this method should
# return NotImplemented (to ensure that those objects are responsible for
# first unpacking the arrays, and then dispatch the operation to the
# underlying arrays)
if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
return NotImplemented
else:
return HDF5ExtensionArray(self._ndarray == other)
# def __invert__(self):
# """Return for `~self`."""
# return type(self)(~self._ndarray)
# def __neg__(self):
# """Return for `-self`."""
# return type(self)(-self._ndarray)
# def __pos__(self):
# """Return for `+self`."""
# return type(self)(+self._ndarray)
# def __abs__(self):
# """Return for `abs(self)`."""
# return type(self)(abs(self._ndarray))
# # error: Signature of "__ne__" incompatible with supertype "object"
# def __ne__(self, other: Any) -> ArrayLike: # type: ignore[override]
# """Return for `self != other` (element-wise in -equality)."""
# return type(self)(self._ndarray != other)
# # error: Signature of "__ne__" incompatible with supertype "object"
# def __lt__(self, other: Any) -> ArrayLike: # type: ignore[override]
# """Return for `self < other` (element-wise in -equality)."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray < other)
# def __gt__(self, other: Any) -> ArrayLike: # type: ignore[override]
# """Return for `self < other` (element-wise in -equality)."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray > other)
# def __le__(self, other: Any) -> ArrayLike: # type: ignore[override]
# """Return for `self < other` (element-wise in -equality)."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray <= other)
# def __ge__(self, other: Any) -> ArrayLike: # type: ignore[override]
# """Return for `self < other` (element-wise in -equality)."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray >= other)
# def __add__(self, other: Any) -> ArrayLike:
# """Return for `self + other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray + other)
# def __radd__(self, other: Any) -> ArrayLike:
# """Return for `other + self`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(other + self._ndarray)
# def __sub__(self, other: Any) -> ArrayLike:
# """Return for `self - other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray - other)
# def __rsub__(self, other: Any) -> ArrayLike:
# """Return for `other - self`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(other - self._ndarray)
# def __mod__(self, other: Any) -> ArrayLike:
# """Return for `self % other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray % other)
# def __divmod__(self, other: Any) -> ArrayLike:
# """Return for `self % other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(np.divmod(self, other))
# def __rdivmod__(self, other: Any) -> ArrayLike:
# """Return for `other % self`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(np.divmod(other, self))
# # def __mul__(self, other: Any) -> ArrayLike:
# # """Return for `self * other`."""
# # if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# # return NotImplemented
# # else:
# # return type(self)(self._ndarray * other)
# def __rmul__(self, other: Any) -> ArrayLike:
# """Return for `other * self`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(other * self._ndarray)
# def __truediv__(self, other: Any) -> ArrayLike:
# """Return for `self / other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray / other)
# def __floordiv__(self, other: Any) -> ArrayLike:
# """Return for `self // other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray // other)
# def __rfloordiv__(self, other: Any) -> ArrayLike:
# """Return for `other // self`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(other // self._ndarray)
# def __lshift__(self, value: Any) -> ArrayLike:
# """Return for `self << other`."""
# if isinstance(value, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray << value)
# def __rshift__(self, value: Any) -> ArrayLike:
# """Return for `self >> other`."""
# if isinstance(value, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray >> value)
# def __and__(self, other: Any) -> ArrayLike:
# """Return for `self & other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray & other)
# def __rand__(self, other: Any) -> ArrayLike:
# """Return for `self & other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(other & self._ndarray)
# def __or__(self, other: Any) -> ArrayLike:
# """Return for `self | other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray | other)
# def __xor__(self, other: Any) -> ArrayLike:
# """Return for `self ^ other`."""
# if isinstance(other, (pandas.Series, pandas.Index, pandas.DataFrame)):
# return NotImplemented
# else:
# return type(self)(self._ndarray ^ other)
# Additional functions that are copy of the NumpyExtensionArray functions
def sem(
self,
*,
axis: AxisInt | None = None,
dtype: NpDtype | None = None,
out=None,
ddof: int = 1,
keepdims: bool = False,
skipna: bool = True,
):
nv.validate_stat_ddof_func(
(), {"dtype": dtype, "out": out, "keepdims": keepdims}, fname="sem"
)
result = nanops.nansem(self._ndarray, axis=axis, skipna=skipna, ddof=ddof)
return result
def kurt(
self,
*,
axis: AxisInt | None = None,
dtype: NpDtype | None = None,
out=None,
keepdims: bool = False,
skipna: bool = True,
):
nv.validate_stat_ddof_func(
(),
{"dtype": dtype, "out": out, "keepdims": keepdims},
fname="kurt",
)
result = nanops.nankurt(self._ndarray, axis=axis, skipna=skipna)
return result
def skew(
self,
*,
axis: AxisInt | None = None,
dtype: NpDtype | None = None,
out=None,
keepdims: bool = False,
skipna: bool = True,
):
nv.validate_stat_ddof_func(
(),
{"dtype": dtype, "out": out, "keepdims": keepdims},
fname="skew",
)
result = nanops.nanskew(self._ndarray, axis=axis, skipna=skipna)
return result