Alle Dateien aus dem Pythonkurs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

435 lines
13 KiB

"""
This file contains a minimal set of tests for compliance with the extension
array interface test suite, and should contain no other tests.
The test suite for the full functionality of the array is located in
`pandas/tests/arrays/`.
The tests in this file are inherited from the BaseExtensionTests, and only
minimal tweaks should be applied to get the tests passing (by overwriting a
parent method).
Additional tests should either be added to one of the BaseExtensionTests
classes (if they are relevant for the extension interface for all dtypes), or
be added to the array-specific tests in `pandas/tests/arrays/`.
"""
import numpy as np
import pytest
from pandas.compat import (
IS64,
is_platform_windows,
)
import pandas as pd
import pandas._testing as tm
from pandas.core.arrays.boolean import BooleanDtype
from pandas.core.arrays.floating import (
Float32Dtype,
Float64Dtype,
)
from pandas.core.arrays.integer import (
Int8Dtype,
Int16Dtype,
Int32Dtype,
Int64Dtype,
UInt8Dtype,
UInt16Dtype,
UInt32Dtype,
UInt64Dtype,
)
from pandas.tests.extension import base
is_windows_or_32bit = is_platform_windows() or not IS64
pytestmark = [
pytest.mark.filterwarnings(
"ignore:invalid value encountered in divide:RuntimeWarning"
),
pytest.mark.filterwarnings("ignore:Mean of empty slice:RuntimeWarning"),
# overflow only relevant for Floating dtype cases cases
pytest.mark.filterwarnings("ignore:overflow encountered in reduce:RuntimeWarning"),
]
def make_data():
return list(range(1, 9)) + [pd.NA] + list(range(10, 98)) + [pd.NA] + [99, 100]
def make_float_data():
return (
list(np.arange(0.1, 0.9, 0.1))
+ [pd.NA]
+ list(np.arange(1, 9.8, 0.1))
+ [pd.NA]
+ [9.9, 10.0]
)
def make_bool_data():
return [True, False] * 4 + [np.nan] + [True, False] * 44 + [np.nan] + [True, False]
@pytest.fixture(
params=[
Int8Dtype,
Int16Dtype,
Int32Dtype,
Int64Dtype,
UInt8Dtype,
UInt16Dtype,
UInt32Dtype,
UInt64Dtype,
Float32Dtype,
Float64Dtype,
BooleanDtype,
]
)
def dtype(request):
return request.param()
@pytest.fixture
def data(dtype):
if dtype.kind == "f":
data = make_float_data()
elif dtype.kind == "b":
data = make_bool_data()
else:
data = make_data()
return pd.array(data, dtype=dtype)
@pytest.fixture
def data_for_twos(dtype):
if dtype.kind == "b":
return pd.array(np.ones(100), dtype=dtype)
return pd.array(np.ones(100) * 2, dtype=dtype)
@pytest.fixture
def data_missing(dtype):
if dtype.kind == "f":
return pd.array([pd.NA, 0.1], dtype=dtype)
elif dtype.kind == "b":
return pd.array([np.nan, True], dtype=dtype)
return pd.array([pd.NA, 1], dtype=dtype)
@pytest.fixture
def data_for_sorting(dtype):
if dtype.kind == "f":
return pd.array([0.1, 0.2, 0.0], dtype=dtype)
elif dtype.kind == "b":
return pd.array([True, True, False], dtype=dtype)
return pd.array([1, 2, 0], dtype=dtype)
@pytest.fixture
def data_missing_for_sorting(dtype):
if dtype.kind == "f":
return pd.array([0.1, pd.NA, 0.0], dtype=dtype)
elif dtype.kind == "b":
return pd.array([True, np.nan, False], dtype=dtype)
return pd.array([1, pd.NA, 0], dtype=dtype)
@pytest.fixture
def na_cmp():
# we are pd.NA
return lambda x, y: x is pd.NA and y is pd.NA
@pytest.fixture
def data_for_grouping(dtype):
if dtype.kind == "f":
b = 0.1
a = 0.0
c = 0.2
elif dtype.kind == "b":
b = True
a = False
c = b
else:
b = 1
a = 0
c = 2
na = pd.NA
return pd.array([b, b, na, na, a, a, b, c], dtype=dtype)
class TestDtype(base.BaseDtypeTests):
pass
class TestArithmeticOps(base.BaseArithmeticOpsTests):
def _get_expected_exception(self, op_name, obj, other):
try:
dtype = tm.get_dtype(obj)
except AttributeError:
# passed arguments reversed
dtype = tm.get_dtype(other)
if dtype.kind == "b":
if op_name.strip("_").lstrip("r") in ["pow", "truediv", "floordiv"]:
# match behavior with non-masked bool dtype
return NotImplementedError
elif op_name in ["__sub__", "__rsub__"]:
# exception message would include "numpy boolean subtract""
return TypeError
return None
return super()._get_expected_exception(op_name, obj, other)
def _cast_pointwise_result(self, op_name: str, obj, other, pointwise_result):
sdtype = tm.get_dtype(obj)
expected = pointwise_result
if sdtype.kind in "iu":
if op_name in ("__rtruediv__", "__truediv__", "__div__"):
expected = expected.fillna(np.nan).astype("Float64")
else:
# combine method result in 'biggest' (int64) dtype
expected = expected.astype(sdtype)
elif sdtype.kind == "b":
if op_name in (
"__floordiv__",
"__rfloordiv__",
"__pow__",
"__rpow__",
"__mod__",
"__rmod__",
):
# combine keeps boolean type
expected = expected.astype("Int8")
elif op_name in ("__truediv__", "__rtruediv__"):
# combine with bools does not generate the correct result
# (numpy behaviour for div is to regard the bools as numeric)
op = self.get_op_from_name(op_name)
expected = self._combine(obj.astype(float), other, op)
expected = expected.astype("Float64")
if op_name == "__rpow__":
# for rpow, combine does not propagate NaN
result = getattr(obj, op_name)(other)
expected[result.isna()] = np.nan
else:
# combine method result in 'biggest' (float64) dtype
expected = expected.astype(sdtype)
return expected
series_scalar_exc = None
series_array_exc = None
frame_scalar_exc = None
divmod_exc = None
def test_divmod_series_array(self, data, data_for_twos, request):
if data.dtype.kind == "b":
mark = pytest.mark.xfail(
reason="Inconsistency between floordiv and divmod; we raise for "
"floordiv but not for divmod. This matches what we do for "
"non-masked bool dtype."
)
request.node.add_marker(mark)
super().test_divmod_series_array(data, data_for_twos)
class TestComparisonOps(base.BaseComparisonOpsTests):
series_scalar_exc = None
series_array_exc = None
frame_scalar_exc = None
def _cast_pointwise_result(self, op_name: str, obj, other, pointwise_result):
return pointwise_result.astype("boolean")
class TestInterface(base.BaseInterfaceTests):
pass
class TestConstructors(base.BaseConstructorsTests):
pass
class TestReshaping(base.BaseReshapingTests):
pass
# for test_concat_mixed_dtypes test
# concat of an Integer and Int coerces to object dtype
# TODO(jreback) once integrated this would
class TestGetitem(base.BaseGetitemTests):
pass
class TestSetitem(base.BaseSetitemTests):
pass
class TestIndex(base.BaseIndexTests):
pass
class TestMissing(base.BaseMissingTests):
pass
class TestMethods(base.BaseMethodsTests):
def test_combine_le(self, data_repeated):
# TODO: patching self is a bad pattern here
orig_data1, orig_data2 = data_repeated(2)
if orig_data1.dtype.kind == "b":
self._combine_le_expected_dtype = "boolean"
else:
# TODO: can we make this boolean?
self._combine_le_expected_dtype = object
super().test_combine_le(data_repeated)
class TestCasting(base.BaseCastingTests):
pass
class TestGroupby(base.BaseGroupbyTests):
pass
class TestReduce(base.BaseReduceTests):
def _supports_reduction(self, obj, op_name: str) -> bool:
if op_name in ["any", "all"] and tm.get_dtype(obj).kind != "b":
pytest.skip(reason="Tested in tests/reductions/test_reductions.py")
return True
def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
# overwrite to ensure pd.NA is tested instead of np.nan
# https://github.com/pandas-dev/pandas/issues/30958
cmp_dtype = "int64"
if ser.dtype.kind == "f":
# Item "dtype[Any]" of "Union[dtype[Any], ExtensionDtype]" has
# no attribute "numpy_dtype"
cmp_dtype = ser.dtype.numpy_dtype # type: ignore[union-attr]
elif ser.dtype.kind == "b":
if op_name in ["min", "max"]:
cmp_dtype = "bool"
if op_name == "count":
result = getattr(ser, op_name)()
expected = getattr(ser.dropna().astype(cmp_dtype), op_name)()
else:
result = getattr(ser, op_name)(skipna=skipna)
expected = getattr(ser.dropna().astype(cmp_dtype), op_name)(skipna=skipna)
if not skipna and ser.isna().any() and op_name not in ["any", "all"]:
expected = pd.NA
tm.assert_almost_equal(result, expected)
def _get_expected_reduction_dtype(self, arr, op_name: str):
if tm.is_float_dtype(arr.dtype):
cmp_dtype = arr.dtype.name
elif op_name in ["mean", "median", "var", "std", "skew"]:
cmp_dtype = "Float64"
elif op_name in ["max", "min"]:
cmp_dtype = arr.dtype.name
elif arr.dtype in ["Int64", "UInt64"]:
cmp_dtype = arr.dtype.name
elif tm.is_signed_integer_dtype(arr.dtype):
cmp_dtype = "Int32" if is_windows_or_32bit else "Int64"
elif tm.is_unsigned_integer_dtype(arr.dtype):
cmp_dtype = "UInt32" if is_windows_or_32bit else "UInt64"
elif arr.dtype.kind == "b":
if op_name in ["mean", "median", "var", "std", "skew"]:
cmp_dtype = "Float64"
elif op_name in ["min", "max"]:
cmp_dtype = "boolean"
elif op_name in ["sum", "prod"]:
cmp_dtype = "Int32" if is_windows_or_32bit else "Int64"
else:
raise TypeError("not supposed to reach this")
else:
raise TypeError("not supposed to reach this")
return cmp_dtype
class TestAccumulation(base.BaseAccumulateTests):
def _supports_accumulation(self, ser: pd.Series, op_name: str) -> bool:
return True
def check_accumulate(self, ser: pd.Series, op_name: str, skipna: bool):
# overwrite to ensure pd.NA is tested instead of np.nan
# https://github.com/pandas-dev/pandas/issues/30958
length = 64
if not IS64 or is_platform_windows():
# Item "ExtensionDtype" of "Union[dtype[Any], ExtensionDtype]" has
# no attribute "itemsize"
if not ser.dtype.itemsize == 8: # type: ignore[union-attr]
length = 32
if ser.dtype.name.startswith("U"):
expected_dtype = f"UInt{length}"
elif ser.dtype.name.startswith("I"):
expected_dtype = f"Int{length}"
elif ser.dtype.name.startswith("F"):
# Incompatible types in assignment (expression has type
# "Union[dtype[Any], ExtensionDtype]", variable has type "str")
expected_dtype = ser.dtype # type: ignore[assignment]
elif ser.dtype.kind == "b":
if op_name in ("cummin", "cummax"):
expected_dtype = "boolean"
else:
expected_dtype = f"Int{length}"
if op_name == "cumsum":
result = getattr(ser, op_name)(skipna=skipna)
expected = pd.Series(
pd.array(
getattr(ser.astype("float64"), op_name)(skipna=skipna),
dtype=expected_dtype,
)
)
tm.assert_series_equal(result, expected)
elif op_name in ["cummax", "cummin"]:
result = getattr(ser, op_name)(skipna=skipna)
expected = pd.Series(
pd.array(
getattr(ser.astype("float64"), op_name)(skipna=skipna),
dtype=ser.dtype,
)
)
tm.assert_series_equal(result, expected)
elif op_name == "cumprod":
result = getattr(ser[:12], op_name)(skipna=skipna)
expected = pd.Series(
pd.array(
getattr(ser[:12].astype("float64"), op_name)(skipna=skipna),
dtype=expected_dtype,
)
)
tm.assert_series_equal(result, expected)
else:
raise NotImplementedError(f"{op_name} not supported")
class TestUnaryOps(base.BaseUnaryOpsTests):
def test_invert(self, data, request):
if data.dtype.kind == "f":
mark = pytest.mark.xfail(
reason="Looks like the base class test implicitly assumes "
"boolean/integer dtypes"
)
request.node.add_marker(mark)
super().test_invert(data)
class TestPrinting(base.BasePrintingTests):
pass
class TestParsing(base.BaseParsingTests):
pass
class Test2DCompat(base.Dim2CompatTests):
pass