308 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import pickle
 | 
						|
 | 
						|
import numpy as np
 | 
						|
import pytest
 | 
						|
 | 
						|
from pandas._libs.missing import NA
 | 
						|
 | 
						|
from pandas.core.dtypes.common import is_scalar
 | 
						|
 | 
						|
import pandas as pd
 | 
						|
import pandas._testing as tm
 | 
						|
 | 
						|
 | 
						|
def test_singleton():
 | 
						|
    assert NA is NA
 | 
						|
    new_NA = type(NA)()
 | 
						|
    assert new_NA is NA
 | 
						|
 | 
						|
 | 
						|
def test_repr():
 | 
						|
    assert repr(NA) == "<NA>"
 | 
						|
    assert str(NA) == "<NA>"
 | 
						|
 | 
						|
 | 
						|
def test_format():
 | 
						|
    # GH-34740
 | 
						|
    assert format(NA) == "<NA>"
 | 
						|
    assert format(NA, ">10") == "      <NA>"
 | 
						|
    assert format(NA, "xxx") == "<NA>"  # NA is flexible, accept any format spec
 | 
						|
 | 
						|
    assert f"{NA}" == "<NA>"
 | 
						|
    assert f"{NA:>10}" == "      <NA>"
 | 
						|
    assert f"{NA:xxx}" == "<NA>"
 | 
						|
 | 
						|
 | 
						|
def test_truthiness():
 | 
						|
    msg = "boolean value of NA is ambiguous"
 | 
						|
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        bool(NA)
 | 
						|
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        not NA
 | 
						|
 | 
						|
 | 
						|
def test_hashable():
 | 
						|
    assert hash(NA) == hash(NA)
 | 
						|
    d = {NA: "test"}
 | 
						|
    assert d[NA] == "test"
 | 
						|
 | 
						|
 | 
						|
def test_arithmetic_ops(all_arithmetic_functions):
 | 
						|
    op = all_arithmetic_functions
 | 
						|
 | 
						|
    for other in [NA, 1, 1.0, "a", np.int64(1), np.nan]:
 | 
						|
        if op.__name__ in ("pow", "rpow", "rmod") and isinstance(other, str):
 | 
						|
            continue
 | 
						|
        if op.__name__ in ("divmod", "rdivmod"):
 | 
						|
            assert op(NA, other) is (NA, NA)
 | 
						|
        else:
 | 
						|
            if op.__name__ == "rpow":
 | 
						|
                # avoid special case
 | 
						|
                other += 1
 | 
						|
            assert op(NA, other) is NA
 | 
						|
 | 
						|
 | 
						|
def test_comparison_ops():
 | 
						|
 | 
						|
    for other in [NA, 1, 1.0, "a", np.int64(1), np.nan, np.bool_(True)]:
 | 
						|
        assert (NA == other) is NA
 | 
						|
        assert (NA != other) is NA
 | 
						|
        assert (NA > other) is NA
 | 
						|
        assert (NA >= other) is NA
 | 
						|
        assert (NA < other) is NA
 | 
						|
        assert (NA <= other) is NA
 | 
						|
        assert (other == NA) is NA
 | 
						|
        assert (other != NA) is NA
 | 
						|
        assert (other > NA) is NA
 | 
						|
        assert (other >= NA) is NA
 | 
						|
        assert (other < NA) is NA
 | 
						|
        assert (other <= NA) is NA
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "value",
 | 
						|
    [
 | 
						|
        0,
 | 
						|
        0.0,
 | 
						|
        -0,
 | 
						|
        -0.0,
 | 
						|
        False,
 | 
						|
        np.bool_(False),
 | 
						|
        np.int_(0),
 | 
						|
        np.float_(0),
 | 
						|
        np.int_(-0),
 | 
						|
        np.float_(-0),
 | 
						|
    ],
 | 
						|
)
 | 
						|
@pytest.mark.parametrize("asarray", [True, False])
 | 
						|
def test_pow_special(value, asarray):
 | 
						|
    if asarray:
 | 
						|
        value = np.array([value])
 | 
						|
    result = NA**value
 | 
						|
 | 
						|
    if asarray:
 | 
						|
        result = result[0]
 | 
						|
    else:
 | 
						|
        # this assertion isn't possible for ndarray.
 | 
						|
        assert isinstance(result, type(value))
 | 
						|
    assert result == 1
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "value", [1, 1.0, True, np.bool_(True), np.int_(1), np.float_(1)]
 | 
						|
)
 | 
						|
@pytest.mark.parametrize("asarray", [True, False])
 | 
						|
def test_rpow_special(value, asarray):
 | 
						|
    if asarray:
 | 
						|
        value = np.array([value])
 | 
						|
    result = value**NA
 | 
						|
 | 
						|
    if asarray:
 | 
						|
        result = result[0]
 | 
						|
    elif not isinstance(value, (np.float_, np.bool_, np.int_)):
 | 
						|
        # this assertion isn't possible with asarray=True
 | 
						|
        assert isinstance(result, type(value))
 | 
						|
 | 
						|
    assert result == value
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("value", [-1, -1.0, np.int_(-1), np.float_(-1)])
 | 
						|
@pytest.mark.parametrize("asarray", [True, False])
 | 
						|
def test_rpow_minus_one(value, asarray):
 | 
						|
    if asarray:
 | 
						|
        value = np.array([value])
 | 
						|
    result = value**NA
 | 
						|
 | 
						|
    if asarray:
 | 
						|
        result = result[0]
 | 
						|
 | 
						|
    assert pd.isna(result)
 | 
						|
 | 
						|
 | 
						|
def test_unary_ops():
 | 
						|
    assert +NA is NA
 | 
						|
    assert -NA is NA
 | 
						|
    assert abs(NA) is NA
 | 
						|
    assert ~NA is NA
 | 
						|
 | 
						|
 | 
						|
def test_logical_and():
 | 
						|
 | 
						|
    assert NA & True is NA
 | 
						|
    assert True & NA is NA
 | 
						|
    assert NA & False is False
 | 
						|
    assert False & NA is False
 | 
						|
    assert NA & NA is NA
 | 
						|
 | 
						|
    msg = "unsupported operand type"
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        NA & 5
 | 
						|
 | 
						|
 | 
						|
def test_logical_or():
 | 
						|
 | 
						|
    assert NA | True is True
 | 
						|
    assert True | NA is True
 | 
						|
    assert NA | False is NA
 | 
						|
    assert False | NA is NA
 | 
						|
    assert NA | NA is NA
 | 
						|
 | 
						|
    msg = "unsupported operand type"
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        NA | 5
 | 
						|
 | 
						|
 | 
						|
def test_logical_xor():
 | 
						|
 | 
						|
    assert NA ^ True is NA
 | 
						|
    assert True ^ NA is NA
 | 
						|
    assert NA ^ False is NA
 | 
						|
    assert False ^ NA is NA
 | 
						|
    assert NA ^ NA is NA
 | 
						|
 | 
						|
    msg = "unsupported operand type"
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        NA ^ 5
 | 
						|
 | 
						|
 | 
						|
def test_logical_not():
 | 
						|
    assert ~NA is NA
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("shape", [(3,), (3, 3), (1, 2, 3)])
 | 
						|
def test_arithmetic_ndarray(shape, all_arithmetic_functions):
 | 
						|
    op = all_arithmetic_functions
 | 
						|
    a = np.zeros(shape)
 | 
						|
    if op.__name__ == "pow":
 | 
						|
        a += 5
 | 
						|
    result = op(NA, a)
 | 
						|
    expected = np.full(a.shape, NA, dtype=object)
 | 
						|
    tm.assert_numpy_array_equal(result, expected)
 | 
						|
 | 
						|
 | 
						|
def test_is_scalar():
 | 
						|
    assert is_scalar(NA) is True
 | 
						|
 | 
						|
 | 
						|
def test_isna():
 | 
						|
    assert pd.isna(NA) is True
 | 
						|
    assert pd.notna(NA) is False
 | 
						|
 | 
						|
 | 
						|
def test_series_isna():
 | 
						|
    s = pd.Series([1, NA], dtype=object)
 | 
						|
    expected = pd.Series([False, True])
 | 
						|
    tm.assert_series_equal(s.isna(), expected)
 | 
						|
 | 
						|
 | 
						|
def test_ufunc():
 | 
						|
    assert np.log(NA) is NA
 | 
						|
    assert np.add(NA, 1) is NA
 | 
						|
    result = np.divmod(NA, 1)
 | 
						|
    assert result[0] is NA and result[1] is NA
 | 
						|
 | 
						|
    result = np.frexp(NA)
 | 
						|
    assert result[0] is NA and result[1] is NA
 | 
						|
 | 
						|
 | 
						|
def test_ufunc_raises():
 | 
						|
    msg = "ufunc method 'at'"
 | 
						|
    with pytest.raises(ValueError, match=msg):
 | 
						|
        np.log.at(NA, 0)
 | 
						|
 | 
						|
 | 
						|
def test_binary_input_not_dunder():
 | 
						|
    a = np.array([1, 2, 3])
 | 
						|
    expected = np.array([NA, NA, NA], dtype=object)
 | 
						|
    result = np.logaddexp(a, NA)
 | 
						|
    tm.assert_numpy_array_equal(result, expected)
 | 
						|
 | 
						|
    result = np.logaddexp(NA, a)
 | 
						|
    tm.assert_numpy_array_equal(result, expected)
 | 
						|
 | 
						|
    # all NA, multiple inputs
 | 
						|
    assert np.logaddexp(NA, NA) is NA
 | 
						|
 | 
						|
    result = np.modf(NA, NA)
 | 
						|
    assert len(result) == 2
 | 
						|
    assert all(x is NA for x in result)
 | 
						|
 | 
						|
 | 
						|
def test_divmod_ufunc():
 | 
						|
    # binary in, binary out.
 | 
						|
    a = np.array([1, 2, 3])
 | 
						|
    expected = np.array([NA, NA, NA], dtype=object)
 | 
						|
 | 
						|
    result = np.divmod(a, NA)
 | 
						|
    assert isinstance(result, tuple)
 | 
						|
    for arr in result:
 | 
						|
        tm.assert_numpy_array_equal(arr, expected)
 | 
						|
        tm.assert_numpy_array_equal(arr, expected)
 | 
						|
 | 
						|
    result = np.divmod(NA, a)
 | 
						|
    for arr in result:
 | 
						|
        tm.assert_numpy_array_equal(arr, expected)
 | 
						|
        tm.assert_numpy_array_equal(arr, expected)
 | 
						|
 | 
						|
 | 
						|
def test_integer_hash_collision_dict():
 | 
						|
    # GH 30013
 | 
						|
    result = {NA: "foo", hash(NA): "bar"}
 | 
						|
 | 
						|
    assert result[NA] == "foo"
 | 
						|
    assert result[hash(NA)] == "bar"
 | 
						|
 | 
						|
 | 
						|
def test_integer_hash_collision_set():
 | 
						|
    # GH 30013
 | 
						|
    result = {NA, hash(NA)}
 | 
						|
 | 
						|
    assert len(result) == 2
 | 
						|
    assert NA in result
 | 
						|
    assert hash(NA) in result
 | 
						|
 | 
						|
 | 
						|
def test_pickle_roundtrip():
 | 
						|
    # https://github.com/pandas-dev/pandas/issues/31847
 | 
						|
    result = pickle.loads(pickle.dumps(NA))
 | 
						|
    assert result is NA
 | 
						|
 | 
						|
 | 
						|
def test_pickle_roundtrip_pandas():
 | 
						|
    result = tm.round_trip_pickle(NA)
 | 
						|
    assert result is NA
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "values, dtype", [([1, 2, NA], "Int64"), (["A", "B", NA], "string")]
 | 
						|
)
 | 
						|
@pytest.mark.parametrize("as_frame", [True, False])
 | 
						|
def test_pickle_roundtrip_containers(as_frame, values, dtype):
 | 
						|
    s = pd.Series(pd.array(values, dtype=dtype))
 | 
						|
    if as_frame:
 | 
						|
        s = s.to_frame(name="A")
 | 
						|
    result = tm.round_trip_pickle(s)
 | 
						|
    tm.assert_equal(result, s)
 |