123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- import numpy as np
- import pytest
- from pandas.core.dtypes.common import is_categorical_dtype
- import pandas as pd
- from pandas import (
- CategoricalIndex,
- DataFrame,
- Index,
- MultiIndex,
- Series,
- crosstab,
- )
- import pandas._testing as tm
- @pytest.fixture
- def df():
- df = DataFrame(
- {
- "A": [
- "foo",
- "foo",
- "foo",
- "foo",
- "bar",
- "bar",
- "bar",
- "bar",
- "foo",
- "foo",
- "foo",
- ],
- "B": [
- "one",
- "one",
- "one",
- "two",
- "one",
- "one",
- "one",
- "two",
- "two",
- "two",
- "one",
- ],
- "C": [
- "dull",
- "dull",
- "shiny",
- "dull",
- "dull",
- "shiny",
- "shiny",
- "dull",
- "shiny",
- "shiny",
- "shiny",
- ],
- "D": np.random.randn(11),
- "E": np.random.randn(11),
- "F": np.random.randn(11),
- }
- )
- return pd.concat([df, df], ignore_index=True)
- class TestCrosstab:
- def test_crosstab_single(self, df):
- result = crosstab(df["A"], df["C"])
- expected = df.groupby(["A", "C"]).size().unstack()
- tm.assert_frame_equal(result, expected.fillna(0).astype(np.int64))
- def test_crosstab_multiple(self, df):
- result = crosstab(df["A"], [df["B"], df["C"]])
- expected = df.groupby(["A", "B", "C"]).size()
- expected = expected.unstack("B").unstack("C").fillna(0).astype(np.int64)
- tm.assert_frame_equal(result, expected)
- result = crosstab([df["B"], df["C"]], df["A"])
- expected = df.groupby(["B", "C", "A"]).size()
- expected = expected.unstack("A").fillna(0).astype(np.int64)
- tm.assert_frame_equal(result, expected)
- @pytest.mark.parametrize("box", [np.array, list, tuple])
- def test_crosstab_ndarray(self, box):
- # GH 44076
- a = box(np.random.randint(0, 5, size=100))
- b = box(np.random.randint(0, 3, size=100))
- c = box(np.random.randint(0, 10, size=100))
- df = DataFrame({"a": a, "b": b, "c": c})
- result = crosstab(a, [b, c], rownames=["a"], colnames=("b", "c"))
- expected = crosstab(df["a"], [df["b"], df["c"]])
- tm.assert_frame_equal(result, expected)
- result = crosstab([b, c], a, colnames=["a"], rownames=("b", "c"))
- expected = crosstab([df["b"], df["c"]], df["a"])
- tm.assert_frame_equal(result, expected)
- # assign arbitrary names
- result = crosstab(a, c)
- expected = crosstab(df["a"], df["c"])
- expected.index.names = ["row_0"]
- expected.columns.names = ["col_0"]
- tm.assert_frame_equal(result, expected)
- def test_crosstab_non_aligned(self):
- # GH 17005
- a = Series([0, 1, 1], index=["a", "b", "c"])
- b = Series([3, 4, 3, 4, 3], index=["a", "b", "c", "d", "f"])
- c = np.array([3, 4, 3], dtype=np.int64)
- expected = DataFrame(
- [[1, 0], [1, 1]],
- index=Index([0, 1], name="row_0"),
- columns=Index([3, 4], name="col_0"),
- )
- result = crosstab(a, b)
- tm.assert_frame_equal(result, expected)
- result = crosstab(a, c)
- tm.assert_frame_equal(result, expected)
- def test_crosstab_margins(self):
- a = np.random.randint(0, 7, size=100)
- b = np.random.randint(0, 3, size=100)
- c = np.random.randint(0, 5, size=100)
- df = DataFrame({"a": a, "b": b, "c": c})
- result = crosstab(a, [b, c], rownames=["a"], colnames=("b", "c"), margins=True)
- assert result.index.names == ("a",)
- assert result.columns.names == ["b", "c"]
- all_cols = result["All", ""]
- exp_cols = df.groupby(["a"]).size().astype("i8")
- # to keep index.name
- exp_margin = Series([len(df)], index=Index(["All"], name="a"))
- exp_cols = pd.concat([exp_cols, exp_margin])
- exp_cols.name = ("All", "")
- tm.assert_series_equal(all_cols, exp_cols)
- all_rows = result.loc["All"]
- exp_rows = df.groupby(["b", "c"]).size().astype("i8")
- exp_rows = pd.concat([exp_rows, Series([len(df)], index=[("All", "")])])
- exp_rows.name = "All"
- exp_rows = exp_rows.reindex(all_rows.index)
- exp_rows = exp_rows.fillna(0).astype(np.int64)
- tm.assert_series_equal(all_rows, exp_rows)
- def test_crosstab_margins_set_margin_name(self):
- # GH 15972
- a = np.random.randint(0, 7, size=100)
- b = np.random.randint(0, 3, size=100)
- c = np.random.randint(0, 5, size=100)
- df = DataFrame({"a": a, "b": b, "c": c})
- result = crosstab(
- a,
- [b, c],
- rownames=["a"],
- colnames=("b", "c"),
- margins=True,
- margins_name="TOTAL",
- )
- assert result.index.names == ("a",)
- assert result.columns.names == ["b", "c"]
- all_cols = result["TOTAL", ""]
- exp_cols = df.groupby(["a"]).size().astype("i8")
- # to keep index.name
- exp_margin = Series([len(df)], index=Index(["TOTAL"], name="a"))
- exp_cols = pd.concat([exp_cols, exp_margin])
- exp_cols.name = ("TOTAL", "")
- tm.assert_series_equal(all_cols, exp_cols)
- all_rows = result.loc["TOTAL"]
- exp_rows = df.groupby(["b", "c"]).size().astype("i8")
- exp_rows = pd.concat([exp_rows, Series([len(df)], index=[("TOTAL", "")])])
- exp_rows.name = "TOTAL"
- exp_rows = exp_rows.reindex(all_rows.index)
- exp_rows = exp_rows.fillna(0).astype(np.int64)
- tm.assert_series_equal(all_rows, exp_rows)
- msg = "margins_name argument must be a string"
- for margins_name in [666, None, ["a", "b"]]:
- with pytest.raises(ValueError, match=msg):
- crosstab(
- a,
- [b, c],
- rownames=["a"],
- colnames=("b", "c"),
- margins=True,
- margins_name=margins_name,
- )
- def test_crosstab_pass_values(self):
- a = np.random.randint(0, 7, size=100)
- b = np.random.randint(0, 3, size=100)
- c = np.random.randint(0, 5, size=100)
- values = np.random.randn(100)
- table = crosstab(
- [a, b], c, values, aggfunc=np.sum, rownames=["foo", "bar"], colnames=["baz"]
- )
- df = DataFrame({"foo": a, "bar": b, "baz": c, "values": values})
- expected = df.pivot_table(
- "values", index=["foo", "bar"], columns="baz", aggfunc=np.sum
- )
- tm.assert_frame_equal(table, expected)
- def test_crosstab_dropna(self):
- # GH 3820
- a = np.array(["foo", "foo", "foo", "bar", "bar", "foo", "foo"], dtype=object)
- b = np.array(["one", "one", "two", "one", "two", "two", "two"], dtype=object)
- c = np.array(
- ["dull", "dull", "dull", "dull", "dull", "shiny", "shiny"], dtype=object
- )
- res = crosstab(a, [b, c], rownames=["a"], colnames=["b", "c"], dropna=False)
- m = MultiIndex.from_tuples(
- [("one", "dull"), ("one", "shiny"), ("two", "dull"), ("two", "shiny")],
- names=["b", "c"],
- )
- tm.assert_index_equal(res.columns, m)
- def test_crosstab_no_overlap(self):
- # GS 10291
- s1 = Series([1, 2, 3], index=[1, 2, 3])
- s2 = Series([4, 5, 6], index=[4, 5, 6])
- actual = crosstab(s1, s2)
- expected = DataFrame(
- index=Index([], dtype="int64", name="row_0"),
- columns=Index([], dtype="int64", name="col_0"),
- )
- tm.assert_frame_equal(actual, expected)
- def test_margin_dropna(self):
- # GH 12577
- # pivot_table counts null into margin ('All')
- # when margins=true and dropna=true
- df = DataFrame({"a": [1, 2, 2, 2, 2, np.nan], "b": [3, 3, 4, 4, 4, 4]})
- actual = crosstab(df.a, df.b, margins=True, dropna=True)
- expected = DataFrame([[1, 0, 1], [1, 3, 4], [2, 3, 5]])
- expected.index = Index([1.0, 2.0, "All"], name="a")
- expected.columns = Index([3, 4, "All"], name="b")
- tm.assert_frame_equal(actual, expected)
- def test_margin_dropna2(self):
- df = DataFrame(
- {"a": [1, np.nan, np.nan, np.nan, 2, np.nan], "b": [3, np.nan, 4, 4, 4, 4]}
- )
- actual = crosstab(df.a, df.b, margins=True, dropna=True)
- expected = DataFrame([[1, 0, 1], [0, 1, 1], [1, 1, 2]])
- expected.index = Index([1.0, 2.0, "All"], name="a")
- expected.columns = Index([3.0, 4.0, "All"], name="b")
- tm.assert_frame_equal(actual, expected)
- def test_margin_dropna3(self):
- df = DataFrame(
- {"a": [1, np.nan, np.nan, np.nan, np.nan, 2], "b": [3, 3, 4, 4, 4, 4]}
- )
- actual = crosstab(df.a, df.b, margins=True, dropna=True)
- expected = DataFrame([[1, 0, 1], [0, 1, 1], [1, 1, 2]])
- expected.index = Index([1.0, 2.0, "All"], name="a")
- expected.columns = Index([3, 4, "All"], name="b")
- tm.assert_frame_equal(actual, expected)
- def test_margin_dropna4(self):
- # GH 12642
- # _add_margins raises KeyError: Level None not found
- # when margins=True and dropna=False
- df = DataFrame({"a": [1, 2, 2, 2, 2, np.nan], "b": [3, 3, 4, 4, 4, 4]})
- actual = crosstab(df.a, df.b, margins=True, dropna=False)
- expected = DataFrame([[1, 0, 1], [1, 3, 4], [2, 4, 6]])
- expected.index = Index([1.0, 2.0, "All"], name="a")
- expected.columns = Index([3, 4, "All"], name="b")
- tm.assert_frame_equal(actual, expected)
- def test_margin_dropna5(self):
- df = DataFrame(
- {"a": [1, np.nan, np.nan, np.nan, 2, np.nan], "b": [3, np.nan, 4, 4, 4, 4]}
- )
- actual = crosstab(df.a, df.b, margins=True, dropna=False)
- expected = DataFrame([[1, 0, 1], [0, 1, 1], [1, 4, 6]])
- expected.index = Index([1.0, 2.0, "All"], name="a")
- expected.columns = Index([3.0, 4.0, "All"], name="b")
- tm.assert_frame_equal(actual, expected)
- def test_margin_dropna6(self):
- a = np.array(["foo", "foo", "foo", "bar", "bar", "foo", "foo"], dtype=object)
- b = np.array(["one", "one", "two", "one", "two", np.nan, "two"], dtype=object)
- c = np.array(
- ["dull", "dull", "dull", "dull", "dull", "shiny", "shiny"], dtype=object
- )
- actual = crosstab(
- a, [b, c], rownames=["a"], colnames=["b", "c"], margins=True, dropna=False
- )
- m = MultiIndex.from_arrays(
- [
- ["one", "one", "two", "two", "All"],
- ["dull", "shiny", "dull", "shiny", ""],
- ],
- names=["b", "c"],
- )
- expected = DataFrame(
- [[1, 0, 1, 0, 2], [2, 0, 1, 1, 5], [3, 0, 2, 1, 7]], columns=m
- )
- expected.index = Index(["bar", "foo", "All"], name="a")
- tm.assert_frame_equal(actual, expected)
- actual = crosstab(
- [a, b], c, rownames=["a", "b"], colnames=["c"], margins=True, dropna=False
- )
- m = MultiIndex.from_arrays(
- [["bar", "bar", "foo", "foo", "All"], ["one", "two", "one", "two", ""]],
- names=["a", "b"],
- )
- expected = DataFrame(
- [[1, 0, 1], [1, 0, 1], [2, 0, 2], [1, 1, 2], [5, 2, 7]], index=m
- )
- expected.columns = Index(["dull", "shiny", "All"], name="c")
- tm.assert_frame_equal(actual, expected)
- actual = crosstab(
- [a, b], c, rownames=["a", "b"], colnames=["c"], margins=True, dropna=True
- )
- m = MultiIndex.from_arrays(
- [["bar", "bar", "foo", "foo", "All"], ["one", "two", "one", "two", ""]],
- names=["a", "b"],
- )
- expected = DataFrame(
- [[1, 0, 1], [1, 0, 1], [2, 0, 2], [1, 1, 2], [5, 1, 6]], index=m
- )
- expected.columns = Index(["dull", "shiny", "All"], name="c")
- tm.assert_frame_equal(actual, expected)
- def test_crosstab_normalize(self):
- # Issue 12578
- df = DataFrame(
- {"a": [1, 2, 2, 2, 2], "b": [3, 3, 4, 4, 4], "c": [1, 1, np.nan, 1, 1]}
- )
- rindex = Index([1, 2], name="a")
- cindex = Index([3, 4], name="b")
- full_normal = DataFrame([[0.2, 0], [0.2, 0.6]], index=rindex, columns=cindex)
- row_normal = DataFrame([[1.0, 0], [0.25, 0.75]], index=rindex, columns=cindex)
- col_normal = DataFrame([[0.5, 0], [0.5, 1.0]], index=rindex, columns=cindex)
- # Check all normalize args
- tm.assert_frame_equal(crosstab(df.a, df.b, normalize="all"), full_normal)
- tm.assert_frame_equal(crosstab(df.a, df.b, normalize=True), full_normal)
- tm.assert_frame_equal(crosstab(df.a, df.b, normalize="index"), row_normal)
- tm.assert_frame_equal(crosstab(df.a, df.b, normalize="columns"), col_normal)
- tm.assert_frame_equal(
- crosstab(df.a, df.b, normalize=1),
- crosstab(df.a, df.b, normalize="columns"),
- )
- tm.assert_frame_equal(
- crosstab(df.a, df.b, normalize=0), crosstab(df.a, df.b, normalize="index")
- )
- row_normal_margins = DataFrame(
- [[1.0, 0], [0.25, 0.75], [0.4, 0.6]],
- index=Index([1, 2, "All"], name="a", dtype="object"),
- columns=Index([3, 4], name="b", dtype="object"),
- )
- col_normal_margins = DataFrame(
- [[0.5, 0, 0.2], [0.5, 1.0, 0.8]],
- index=Index([1, 2], name="a", dtype="object"),
- columns=Index([3, 4, "All"], name="b", dtype="object"),
- )
- all_normal_margins = DataFrame(
- [[0.2, 0, 0.2], [0.2, 0.6, 0.8], [0.4, 0.6, 1]],
- index=Index([1, 2, "All"], name="a", dtype="object"),
- columns=Index([3, 4, "All"], name="b", dtype="object"),
- )
- tm.assert_frame_equal(
- crosstab(df.a, df.b, normalize="index", margins=True), row_normal_margins
- )
- tm.assert_frame_equal(
- crosstab(df.a, df.b, normalize="columns", margins=True), col_normal_margins
- )
- tm.assert_frame_equal(
- crosstab(df.a, df.b, normalize=True, margins=True), all_normal_margins
- )
- def test_crosstab_normalize_arrays(self):
- # GH#12578
- df = DataFrame(
- {"a": [1, 2, 2, 2, 2], "b": [3, 3, 4, 4, 4], "c": [1, 1, np.nan, 1, 1]}
- )
- # Test arrays
- crosstab(
- [np.array([1, 1, 2, 2]), np.array([1, 2, 1, 2])], np.array([1, 2, 1, 2])
- )
- # Test with aggfunc
- norm_counts = DataFrame(
- [[0.25, 0, 0.25], [0.25, 0.5, 0.75], [0.5, 0.5, 1]],
- index=Index([1, 2, "All"], name="a", dtype="object"),
- columns=Index([3, 4, "All"], name="b"),
- )
- test_case = crosstab(
- df.a, df.b, df.c, aggfunc="count", normalize="all", margins=True
- )
- tm.assert_frame_equal(test_case, norm_counts)
- df = DataFrame(
- {"a": [1, 2, 2, 2, 2], "b": [3, 3, 4, 4, 4], "c": [0, 4, np.nan, 3, 3]}
- )
- norm_sum = DataFrame(
- [[0, 0, 0.0], [0.4, 0.6, 1], [0.4, 0.6, 1]],
- index=Index([1, 2, "All"], name="a", dtype="object"),
- columns=Index([3, 4, "All"], name="b", dtype="object"),
- )
- test_case = crosstab(
- df.a, df.b, df.c, aggfunc=np.sum, normalize="all", margins=True
- )
- tm.assert_frame_equal(test_case, norm_sum)
- def test_crosstab_with_empties(self, using_array_manager):
- # Check handling of empties
- df = DataFrame(
- {
- "a": [1, 2, 2, 2, 2],
- "b": [3, 3, 4, 4, 4],
- "c": [np.nan, np.nan, np.nan, np.nan, np.nan],
- }
- )
- empty = DataFrame(
- [[0.0, 0.0], [0.0, 0.0]],
- index=Index([1, 2], name="a", dtype="int64"),
- columns=Index([3, 4], name="b"),
- )
- for i in [True, "index", "columns"]:
- calculated = crosstab(df.a, df.b, values=df.c, aggfunc="count", normalize=i)
- tm.assert_frame_equal(empty, calculated)
- nans = DataFrame(
- [[0.0, np.nan], [0.0, 0.0]],
- index=Index([1, 2], name="a", dtype="int64"),
- columns=Index([3, 4], name="b"),
- )
- if using_array_manager:
- # INFO(ArrayManager) column without NaNs can preserve int dtype
- nans[3] = nans[3].astype("int64")
- calculated = crosstab(df.a, df.b, values=df.c, aggfunc="count", normalize=False)
- tm.assert_frame_equal(nans, calculated)
- def test_crosstab_errors(self):
- # Issue 12578
- df = DataFrame(
- {"a": [1, 2, 2, 2, 2], "b": [3, 3, 4, 4, 4], "c": [1, 1, np.nan, 1, 1]}
- )
- error = "values cannot be used without an aggfunc."
- with pytest.raises(ValueError, match=error):
- crosstab(df.a, df.b, values=df.c)
- error = "aggfunc cannot be used without values"
- with pytest.raises(ValueError, match=error):
- crosstab(df.a, df.b, aggfunc=np.mean)
- error = "Not a valid normalize argument"
- with pytest.raises(ValueError, match=error):
- crosstab(df.a, df.b, normalize="42")
- with pytest.raises(ValueError, match=error):
- crosstab(df.a, df.b, normalize=42)
- error = "Not a valid margins argument"
- with pytest.raises(ValueError, match=error):
- crosstab(df.a, df.b, normalize="all", margins=42)
- def test_crosstab_with_categorial_columns(self):
- # GH 8860
- df = DataFrame(
- {
- "MAKE": ["Honda", "Acura", "Tesla", "Honda", "Honda", "Acura"],
- "MODEL": ["Sedan", "Sedan", "Electric", "Pickup", "Sedan", "Sedan"],
- }
- )
- categories = ["Sedan", "Electric", "Pickup"]
- df["MODEL"] = df["MODEL"].astype("category").cat.set_categories(categories)
- result = crosstab(df["MAKE"], df["MODEL"])
- expected_index = Index(["Acura", "Honda", "Tesla"], name="MAKE")
- expected_columns = CategoricalIndex(
- categories, categories=categories, ordered=False, name="MODEL"
- )
- expected_data = [[2, 0, 0], [2, 0, 1], [0, 1, 0]]
- expected = DataFrame(
- expected_data, index=expected_index, columns=expected_columns
- )
- tm.assert_frame_equal(result, expected)
- def test_crosstab_with_numpy_size(self):
- # GH 4003
- df = DataFrame(
- {
- "A": ["one", "one", "two", "three"] * 6,
- "B": ["A", "B", "C"] * 8,
- "C": ["foo", "foo", "foo", "bar", "bar", "bar"] * 4,
- "D": np.random.randn(24),
- "E": np.random.randn(24),
- }
- )
- result = crosstab(
- index=[df["A"], df["B"]],
- columns=[df["C"]],
- margins=True,
- aggfunc=np.size,
- values=df["D"],
- )
- expected_index = MultiIndex(
- levels=[["All", "one", "three", "two"], ["", "A", "B", "C"]],
- codes=[[1, 1, 1, 2, 2, 2, 3, 3, 3, 0], [1, 2, 3, 1, 2, 3, 1, 2, 3, 0]],
- names=["A", "B"],
- )
- expected_column = Index(["bar", "foo", "All"], dtype="object", name="C")
- expected_data = np.array(
- [
- [2.0, 2.0, 4.0],
- [2.0, 2.0, 4.0],
- [2.0, 2.0, 4.0],
- [2.0, np.nan, 2.0],
- [np.nan, 2.0, 2.0],
- [2.0, np.nan, 2.0],
- [np.nan, 2.0, 2.0],
- [2.0, np.nan, 2.0],
- [np.nan, 2.0, 2.0],
- [12.0, 12.0, 24.0],
- ]
- )
- expected = DataFrame(
- expected_data, index=expected_index, columns=expected_column
- )
- # aggfunc is np.size, resulting in integers
- expected["All"] = expected["All"].astype("int64")
- tm.assert_frame_equal(result, expected)
- def test_crosstab_duplicate_names(self):
- # GH 13279 / 22529
- s1 = Series(range(3), name="foo")
- s2_foo = Series(range(1, 4), name="foo")
- s2_bar = Series(range(1, 4), name="bar")
- s3 = Series(range(3), name="waldo")
- # check result computed with duplicate labels against
- # result computed with unique labels, then relabelled
- mapper = {"bar": "foo"}
- # duplicate row, column labels
- result = crosstab(s1, s2_foo)
- expected = crosstab(s1, s2_bar).rename_axis(columns=mapper, axis=1)
- tm.assert_frame_equal(result, expected)
- # duplicate row, unique column labels
- result = crosstab([s1, s2_foo], s3)
- expected = crosstab([s1, s2_bar], s3).rename_axis(index=mapper, axis=0)
- tm.assert_frame_equal(result, expected)
- # unique row, duplicate column labels
- result = crosstab(s3, [s1, s2_foo])
- expected = crosstab(s3, [s1, s2_bar]).rename_axis(columns=mapper, axis=1)
- tm.assert_frame_equal(result, expected)
- @pytest.mark.parametrize("names", [["a", ("b", "c")], [("a", "b"), "c"]])
- def test_crosstab_tuple_name(self, names):
- s1 = Series(range(3), name=names[0])
- s2 = Series(range(1, 4), name=names[1])
- mi = MultiIndex.from_arrays([range(3), range(1, 4)], names=names)
- expected = Series(1, index=mi).unstack(1, fill_value=0)
- result = crosstab(s1, s2)
- tm.assert_frame_equal(result, expected)
- def test_crosstab_both_tuple_names(self):
- # GH 18321
- s1 = Series(range(3), name=("a", "b"))
- s2 = Series(range(3), name=("c", "d"))
- expected = DataFrame(
- np.eye(3, dtype="int64"),
- index=Index(range(3), name=("a", "b")),
- columns=Index(range(3), name=("c", "d")),
- )
- result = crosstab(s1, s2)
- tm.assert_frame_equal(result, expected)
- def test_crosstab_unsorted_order(self):
- df = DataFrame({"b": [3, 1, 2], "a": [5, 4, 6]}, index=["C", "A", "B"])
- result = crosstab(df.index, [df.b, df.a])
- e_idx = Index(["A", "B", "C"], name="row_0")
- e_columns = MultiIndex.from_tuples([(1, 4), (2, 6), (3, 5)], names=["b", "a"])
- expected = DataFrame(
- [[1, 0, 0], [0, 1, 0], [0, 0, 1]], index=e_idx, columns=e_columns
- )
- tm.assert_frame_equal(result, expected)
- def test_crosstab_normalize_multiple_columns(self):
- # GH 15150
- df = DataFrame(
- {
- "A": ["one", "one", "two", "three"] * 6,
- "B": ["A", "B", "C"] * 8,
- "C": ["foo", "foo", "foo", "bar", "bar", "bar"] * 4,
- "D": [0] * 24,
- "E": [0] * 24,
- }
- )
- result = crosstab(
- [df.A, df.B],
- df.C,
- values=df.D,
- aggfunc=np.sum,
- normalize=True,
- margins=True,
- )
- expected = DataFrame(
- np.array([0] * 29 + [1], dtype=float).reshape(10, 3),
- columns=Index(["bar", "foo", "All"], dtype="object", name="C"),
- index=MultiIndex.from_tuples(
- [
- ("one", "A"),
- ("one", "B"),
- ("one", "C"),
- ("three", "A"),
- ("three", "B"),
- ("three", "C"),
- ("two", "A"),
- ("two", "B"),
- ("two", "C"),
- ("All", ""),
- ],
- names=["A", "B"],
- ),
- )
- tm.assert_frame_equal(result, expected)
- def test_margin_normalize(self):
- # GH 27500
- df = DataFrame(
- {
- "A": ["foo", "foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar"],
- "B": ["one", "one", "one", "two", "two", "one", "one", "two", "two"],
- "C": [
- "small",
- "large",
- "large",
- "small",
- "small",
- "large",
- "small",
- "small",
- "large",
- ],
- "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
- "E": [2, 4, 5, 5, 6, 6, 8, 9, 9],
- }
- )
- # normalize on index
- result = crosstab(
- [df.A, df.B], df.C, margins=True, margins_name="Sub-Total", normalize=0
- )
- expected = DataFrame(
- [[0.5, 0.5], [0.5, 0.5], [0.666667, 0.333333], [0, 1], [0.444444, 0.555556]]
- )
- expected.index = MultiIndex(
- levels=[["Sub-Total", "bar", "foo"], ["", "one", "two"]],
- codes=[[1, 1, 2, 2, 0], [1, 2, 1, 2, 0]],
- names=["A", "B"],
- )
- expected.columns = Index(["large", "small"], dtype="object", name="C")
- tm.assert_frame_equal(result, expected)
- # normalize on columns
- result = crosstab(
- [df.A, df.B], df.C, margins=True, margins_name="Sub-Total", normalize=1
- )
- expected = DataFrame(
- [
- [0.25, 0.2, 0.222222],
- [0.25, 0.2, 0.222222],
- [0.5, 0.2, 0.333333],
- [0, 0.4, 0.222222],
- ]
- )
- expected.columns = Index(
- ["large", "small", "Sub-Total"], dtype="object", name="C"
- )
- expected.index = MultiIndex(
- levels=[["bar", "foo"], ["one", "two"]],
- codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
- names=["A", "B"],
- )
- tm.assert_frame_equal(result, expected)
- # normalize on both index and column
- result = crosstab(
- [df.A, df.B], df.C, margins=True, margins_name="Sub-Total", normalize=True
- )
- expected = DataFrame(
- [
- [0.111111, 0.111111, 0.222222],
- [0.111111, 0.111111, 0.222222],
- [0.222222, 0.111111, 0.333333],
- [0.000000, 0.222222, 0.222222],
- [0.444444, 0.555555, 1],
- ]
- )
- expected.columns = Index(
- ["large", "small", "Sub-Total"], dtype="object", name="C"
- )
- expected.index = MultiIndex(
- levels=[["Sub-Total", "bar", "foo"], ["", "one", "two"]],
- codes=[[1, 1, 2, 2, 0], [1, 2, 1, 2, 0]],
- names=["A", "B"],
- )
- tm.assert_frame_equal(result, expected)
- def test_margin_normalize_multiple_columns(self):
- # GH 35144
- # use multiple columns with margins and normalization
- df = DataFrame(
- {
- "A": ["foo", "foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar"],
- "B": ["one", "one", "one", "two", "two", "one", "one", "two", "two"],
- "C": [
- "small",
- "large",
- "large",
- "small",
- "small",
- "large",
- "small",
- "small",
- "large",
- ],
- "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
- "E": [2, 4, 5, 5, 6, 6, 8, 9, 9],
- }
- )
- result = crosstab(
- index=df.C,
- columns=[df.A, df.B],
- margins=True,
- margins_name="margin",
- normalize=True,
- )
- expected = DataFrame(
- [
- [0.111111, 0.111111, 0.222222, 0.000000, 0.444444],
- [0.111111, 0.111111, 0.111111, 0.222222, 0.555556],
- [0.222222, 0.222222, 0.333333, 0.222222, 1.0],
- ],
- index=["large", "small", "margin"],
- )
- expected.columns = MultiIndex(
- levels=[["bar", "foo", "margin"], ["", "one", "two"]],
- codes=[[0, 0, 1, 1, 2], [1, 2, 1, 2, 0]],
- names=["A", "B"],
- )
- expected.index.name = "C"
- tm.assert_frame_equal(result, expected)
- def test_margin_support_Float(self):
- # GH 50313
- # use Float64 formats and function aggfunc with margins
- df = DataFrame(
- {"A": [1, 2, 2, 1], "B": [3, 3, 4, 5], "C": [-1.0, 10.0, 1.0, 10.0]},
- dtype="Float64",
- )
- result = crosstab(
- df["A"],
- df["B"],
- values=df["C"],
- aggfunc="sum",
- margins=True,
- )
- expected = DataFrame(
- [
- [-1.0, pd.NA, 10.0, 9.0],
- [10.0, 1.0, pd.NA, 11.0],
- [9.0, 1.0, 10.0, 20.0],
- ],
- index=Index([1.0, 2.0, "All"], dtype="object", name="A"),
- columns=Index([3.0, 4.0, 5.0, "All"], dtype="object", name="B"),
- dtype="Float64",
- )
- tm.assert_frame_equal(result, expected)
- @pytest.mark.parametrize("a_dtype", ["category", "int64"])
- @pytest.mark.parametrize("b_dtype", ["category", "int64"])
- def test_categoricals(a_dtype, b_dtype):
- # https://github.com/pandas-dev/pandas/issues/37465
- g = np.random.RandomState(25982704)
- a = Series(g.randint(0, 3, size=100)).astype(a_dtype)
- b = Series(g.randint(0, 2, size=100)).astype(b_dtype)
- result = crosstab(a, b, margins=True, dropna=False)
- columns = Index([0, 1, "All"], dtype="object", name="col_0")
- index = Index([0, 1, 2, "All"], dtype="object", name="row_0")
- values = [[18, 16, 34], [18, 16, 34], [16, 16, 32], [52, 48, 100]]
- expected = DataFrame(values, index, columns)
- tm.assert_frame_equal(result, expected)
- # Verify when categorical does not have all values present
- a.loc[a == 1] = 2
- a_is_cat = is_categorical_dtype(a.dtype)
- assert not a_is_cat or a.value_counts().loc[1] == 0
- result = crosstab(a, b, margins=True, dropna=False)
- values = [[18, 16, 34], [0, 0, 0], [34, 32, 66], [52, 48, 100]]
- expected = DataFrame(values, index, columns)
- if not a_is_cat:
- expected = expected.loc[[0, 2, "All"]]
- expected["All"] = expected["All"].astype("int64")
- repr(result)
- repr(expected)
- repr(expected.loc[[0, 2, "All"]])
- tm.assert_frame_equal(result, expected)
|