123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- import os
- import numpy as np
- import pytest
- import pandas as pd
- from pandas import (
- Categorical,
- DatetimeIndex,
- Interval,
- IntervalIndex,
- NaT,
- Series,
- TimedeltaIndex,
- Timestamp,
- cut,
- date_range,
- isna,
- qcut,
- timedelta_range,
- )
- import pandas._testing as tm
- from pandas.api.types import CategoricalDtype as CDT
- from pandas.tseries.offsets import (
- Day,
- Nano,
- )
- def test_qcut():
- arr = np.random.randn(1000)
- # We store the bins as Index that have been
- # rounded to comparisons are a bit tricky.
- labels, _ = qcut(arr, 4, retbins=True)
- ex_bins = np.quantile(arr, [0, 0.25, 0.5, 0.75, 1.0])
- result = labels.categories.left.values
- assert np.allclose(result, ex_bins[:-1], atol=1e-2)
- result = labels.categories.right.values
- assert np.allclose(result, ex_bins[1:], atol=1e-2)
- ex_levels = cut(arr, ex_bins, include_lowest=True)
- tm.assert_categorical_equal(labels, ex_levels)
- def test_qcut_bounds():
- arr = np.random.randn(1000)
- factor = qcut(arr, 10, labels=False)
- assert len(np.unique(factor)) == 10
- def test_qcut_specify_quantiles():
- arr = np.random.randn(100)
- factor = qcut(arr, [0, 0.25, 0.5, 0.75, 1.0])
- expected = qcut(arr, 4)
- tm.assert_categorical_equal(factor, expected)
- def test_qcut_all_bins_same():
- with pytest.raises(ValueError, match="edges.*unique"):
- qcut([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 3)
- def test_qcut_include_lowest():
- values = np.arange(10)
- ii = qcut(values, 4)
- ex_levels = IntervalIndex(
- [
- Interval(-0.001, 2.25),
- Interval(2.25, 4.5),
- Interval(4.5, 6.75),
- Interval(6.75, 9),
- ]
- )
- tm.assert_index_equal(ii.categories, ex_levels)
- def test_qcut_nas():
- arr = np.random.randn(100)
- arr[:20] = np.nan
- result = qcut(arr, 4)
- assert isna(result[:20]).all()
- def test_qcut_index():
- result = qcut([0, 2], 2)
- intervals = [Interval(-0.001, 1), Interval(1, 2)]
- expected = Categorical(intervals, ordered=True)
- tm.assert_categorical_equal(result, expected)
- def test_qcut_binning_issues(datapath):
- # see gh-1978, gh-1979
- cut_file = datapath(os.path.join("reshape", "data", "cut_data.csv"))
- arr = np.loadtxt(cut_file)
- result = qcut(arr, 20)
- starts = []
- ends = []
- for lev in np.unique(result):
- s = lev.left
- e = lev.right
- assert s != e
- starts.append(float(s))
- ends.append(float(e))
- for (sp, sn), (ep, en) in zip(
- zip(starts[:-1], starts[1:]), zip(ends[:-1], ends[1:])
- ):
- assert sp < sn
- assert ep < en
- assert ep <= sn
- def test_qcut_return_intervals():
- ser = Series([0, 1, 2, 3, 4, 5, 6, 7, 8])
- res = qcut(ser, [0, 0.333, 0.666, 1])
- exp_levels = np.array(
- [Interval(-0.001, 2.664), Interval(2.664, 5.328), Interval(5.328, 8)]
- )
- exp = Series(exp_levels.take([0, 0, 0, 1, 1, 1, 2, 2, 2])).astype(CDT(ordered=True))
- tm.assert_series_equal(res, exp)
- @pytest.mark.parametrize("labels", ["foo", 1, True])
- def test_qcut_incorrect_labels(labels):
- # GH 13318
- values = range(5)
- msg = "Bin labels must either be False, None or passed in as a list-like argument"
- with pytest.raises(ValueError, match=msg):
- qcut(values, 4, labels=labels)
- @pytest.mark.parametrize("labels", [["a", "b", "c"], list(range(3))])
- def test_qcut_wrong_length_labels(labels):
- # GH 13318
- values = range(10)
- msg = "Bin labels must be one fewer than the number of bin edges"
- with pytest.raises(ValueError, match=msg):
- qcut(values, 4, labels=labels)
- @pytest.mark.parametrize(
- "labels, expected",
- [
- (["a", "b", "c"], Categorical(["a", "b", "c"], ordered=True)),
- (list(range(3)), Categorical([0, 1, 2], ordered=True)),
- ],
- )
- def test_qcut_list_like_labels(labels, expected):
- # GH 13318
- values = range(3)
- result = qcut(values, 3, labels=labels)
- tm.assert_categorical_equal(result, expected)
- @pytest.mark.parametrize(
- "kwargs,msg",
- [
- ({"duplicates": "drop"}, None),
- ({}, "Bin edges must be unique"),
- ({"duplicates": "raise"}, "Bin edges must be unique"),
- ({"duplicates": "foo"}, "invalid value for 'duplicates' parameter"),
- ],
- )
- def test_qcut_duplicates_bin(kwargs, msg):
- # see gh-7751
- values = [0, 0, 0, 0, 1, 2, 3]
- if msg is not None:
- with pytest.raises(ValueError, match=msg):
- qcut(values, 3, **kwargs)
- else:
- result = qcut(values, 3, **kwargs)
- expected = IntervalIndex([Interval(-0.001, 1), Interval(1, 3)])
- tm.assert_index_equal(result.categories, expected)
- @pytest.mark.parametrize(
- "data,start,end", [(9.0, 8.999, 9.0), (0.0, -0.001, 0.0), (-9.0, -9.001, -9.0)]
- )
- @pytest.mark.parametrize("length", [1, 2])
- @pytest.mark.parametrize("labels", [None, False])
- def test_single_quantile(data, start, end, length, labels):
- # see gh-15431
- ser = Series([data] * length)
- result = qcut(ser, 1, labels=labels)
- if labels is None:
- intervals = IntervalIndex([Interval(start, end)] * length, closed="right")
- expected = Series(intervals).astype(CDT(ordered=True))
- else:
- expected = Series([0] * length, dtype=np.intp)
- tm.assert_series_equal(result, expected)
- @pytest.mark.parametrize(
- "ser",
- [
- Series(DatetimeIndex(["20180101", NaT, "20180103"])),
- Series(TimedeltaIndex(["0 days", NaT, "2 days"])),
- ],
- ids=lambda x: str(x.dtype),
- )
- def test_qcut_nat(ser):
- # see gh-19768
- intervals = IntervalIndex.from_tuples(
- [(ser[0] - Nano(), ser[2] - Day()), np.nan, (ser[2] - Day(), ser[2])]
- )
- expected = Series(Categorical(intervals, ordered=True))
- result = qcut(ser, 2)
- tm.assert_series_equal(result, expected)
- @pytest.mark.parametrize("bins", [3, np.linspace(0, 1, 4)])
- def test_datetime_tz_qcut(bins):
- # see gh-19872
- tz = "US/Eastern"
- ser = Series(date_range("20130101", periods=3, tz=tz))
- result = qcut(ser, bins)
- expected = Series(
- IntervalIndex(
- [
- Interval(
- Timestamp("2012-12-31 23:59:59.999999999", tz=tz),
- Timestamp("2013-01-01 16:00:00", tz=tz),
- ),
- Interval(
- Timestamp("2013-01-01 16:00:00", tz=tz),
- Timestamp("2013-01-02 08:00:00", tz=tz),
- ),
- Interval(
- Timestamp("2013-01-02 08:00:00", tz=tz),
- Timestamp("2013-01-03 00:00:00", tz=tz),
- ),
- ]
- )
- ).astype(CDT(ordered=True))
- tm.assert_series_equal(result, expected)
- @pytest.mark.parametrize(
- "arg,expected_bins",
- [
- [
- timedelta_range("1day", periods=3),
- TimedeltaIndex(["1 days", "2 days", "3 days"]),
- ],
- [
- date_range("20180101", periods=3),
- DatetimeIndex(["2018-01-01", "2018-01-02", "2018-01-03"]),
- ],
- ],
- )
- def test_date_like_qcut_bins(arg, expected_bins):
- # see gh-19891
- ser = Series(arg)
- result, result_bins = qcut(ser, 2, retbins=True)
- tm.assert_index_equal(result_bins, expected_bins)
- @pytest.mark.parametrize("bins", [6, 7])
- @pytest.mark.parametrize(
- "box, compare",
- [
- (Series, tm.assert_series_equal),
- (np.array, tm.assert_categorical_equal),
- (list, tm.assert_equal),
- ],
- )
- def test_qcut_bool_coercion_to_int(bins, box, compare):
- # issue 20303
- data_expected = box([0, 1, 1, 0, 1] * 10)
- data_result = box([False, True, True, False, True] * 10)
- expected = qcut(data_expected, bins, duplicates="drop")
- result = qcut(data_result, bins, duplicates="drop")
- compare(result, expected)
- @pytest.mark.parametrize("q", [2, 5, 10])
- def test_qcut_nullable_integer(q, any_numeric_ea_dtype):
- arr = pd.array(np.arange(100), dtype=any_numeric_ea_dtype)
- arr[::2] = pd.NA
- result = qcut(arr, q)
- expected = qcut(arr.astype(float), q)
- tm.assert_categorical_equal(result, expected)
|