{ "cells": [ { "cell_type": "markdown", "id": "a2152af9-7fb3-4ca6-955c-0d6ee87b3efa", "metadata": {}, "source": [ "# Unit Test for aggregate_spot_compositions" ] }, { "cell_type": "code", "execution_count": 1, "id": "bd067f9c-b79e-4795-9676-d7a52e349384", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import unittest\n", "from pandas.testing import assert_frame_equal\n", "\n", "import os\n", "os.sys.path.append('../../../')\n", "from mesa.ecospatial._utils import aggregate_spot_compositions" ] }, { "cell_type": "code", "execution_count": 9, "id": "69de7af9-c575-4bcf-a9c1-dc6a8de09559", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "test_all_background (__main__.TestAggregateSpotCompositions.test_all_background)\n", "Test the case where all spots are background (label 0). ... ok\n", "test_basic (__main__.TestAggregateSpotCompositions.test_basic)\n", "Test a basic scenario with a small 2x2 labelled array. ... ok\n", "test_union_of_keys (__main__.TestAggregateSpotCompositions.test_union_of_keys)\n", "Test that the union of composition keys is maintained across islands, ... FAIL\n", "test_with_none (__main__.TestAggregateSpotCompositions.test_with_none)\n", "Test with some spots having None for composition. ... ok\n", "\n", "======================================================================\n", "FAIL: test_union_of_keys (__main__.TestAggregateSpotCompositions.test_union_of_keys)\n", "Test that the union of composition keys is maintained across islands,\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/var/folders/7g/phdhh_ld3dlbnrst0t60bwzr0000gn/T/ipykernel_75522/4104004902.py\", line 116, in test_union_of_keys\n", " assert_frame_equal(result, expected_df)\n", " File \"/opt/miniconda3/envs/mesa/lib/python3.11/site-packages/pandas/_testing/asserters.py\", line 1148, in assert_frame_equal\n", " raise_assert_detail(\n", " File \"/opt/miniconda3/envs/mesa/lib/python3.11/site-packages/pandas/_testing/asserters.py\", line 598, in raise_assert_detail\n", " raise AssertionError(msg)\n", "AssertionError: DataFrame are different\n", "\n", "DataFrame shape mismatch\n", "[left]: (2, 1)\n", "[right]: (2, 2)\n", "\n", "----------------------------------------------------------------------\n", "Ran 4 tests in 0.097s\n", "\n", "FAILED (failures=1)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " X Y Z\n", "Island_1 1.0 3.0 5.0\n", "Island_2 3.0 5.0 2.0\n" ] } ], "source": [ "class TestAggregateSpotCompositions(unittest.TestCase):\n", " def test_basic(self):\n", " \"\"\"\n", " Test a basic scenario with a small 2x2 labelled array.\n", " \"\"\"\n", " # labelled: 2x2 array with:\n", " # - (0,0)=0 (background), (0,1)=1, (1,0)=2, (1,1)=2\n", " labelled = np.array([[0, 1],\n", " [2, 2]])\n", " \n", " compositions = [\n", " pd.Series([1.0, 2.0], index=['A', 'B']), # position (0,0) -> background, ignored\n", " pd.Series([3.0, 4.0], index=['A', 'B']), # position (0,1) -> Island 1\n", " pd.Series([5.0, 6.0], index=['A', 'B']), # position (1,0) -> Island 2\n", " pd.Series([7.0, 8.0], index=['A', 'B']) # position (1,1) -> Island 2\n", " ]\n", " \n", " # Expected result:\n", " # - Island_1: composition = [3, 4]\n", " # - Island_2: composition = [5+7, 6+8] = [12, 14]\n", " expected_data = {\n", " 'Island_1': pd.Series([3.0, 4.0], index=['A', 'B']),\n", " 'Island_2': pd.Series([12.0, 14.0], index=['A', 'B'])\n", " }\n", " expected_df = pd.DataFrame.from_dict(expected_data, orient='index')\n", " \n", " result = aggregate_spot_compositions(labelled, compositions)\n", " # Sort the columns for a consistent order before comparing\n", " result = result.sort_index(axis=1)\n", " expected_df = expected_df.sort_index(axis=1)\n", " assert_frame_equal(result, expected_df)\n", " \n", " def test_with_none(self):\n", " \"\"\"\n", " Test with some spots having None for composition.\n", " \"\"\"\n", " # labelled: 3x3 array with islands 1 and 2\n", " labelled = np.array([\n", " [0, 1, 1],\n", " [2, 2, 0],\n", " [1, 2, 2]\n", " ])\n", " \n", " # There are 9 spots (flattened indices 0-8). We assign:\n", " # - Spots with label 0: indices 0 and 5 -> None.\n", " # - Spots with label 1: indices 1, 2, 6.\n", " # - Spots with label 2: indices 3, 4, 7, 8.\n", " comps = []\n", " # index 0: (0,0) label 0 -> None\n", " comps.append(None)\n", " # index 1: (0,1) label 1 -> pd.Series({'X': 1})\n", " comps.append(pd.Series({'X': 1.0}))\n", " # index 2: (0,2) label 1 -> pd.Series({'X': 2, 'Y': 3})\n", " comps.append(pd.Series({'Y': 3.0}))\n", " # index 3: (1,0) label 2 -> pd.Series({'Y': 4})\n", " comps.append(pd.Series({'Y': 4.0}))\n", " # index 4: (1,1) label 2 -> None\n", " comps.append(None)\n", " # index 5: (1,2) label 0 -> None\n", " comps.append(None)\n", " # index 6: (2,0) label 1 -> pd.Series({'Z': 5})\n", " comps.append(pd.Series({'Z': 5.0}))\n", " # index 7: (2,1) label 2 -> pd.Series({'X': 3, 'Z': 2})\n", " comps.append(pd.Series({'X': 3.0, 'Z': 2.0}))\n", " # index 8: (2,2) label 2 -> pd.Series({'Y': 1})\n", " comps.append(pd.Series({'Y': 1.0}))\n", " \n", " # Expected aggregation:\n", " # Island_1 (indices 1, 2, 6):\n", " # Sum: {'X': 1} + {'X': 2, 'Y': 3} + {'Z': 5} = {'X': 3, 'Y': 3, 'Z': 5}\n", " # Island_2 (indices 3, 4, 7, 8):\n", " # Sum: {'Y': 4} + 0 + {'X': 3, 'Z': 2} + {'Y': 1} = {'X': 3, 'Y': 5, 'Z': 2}\n", " expected_data = {\n", " 'Island_1': pd.Series({'X': 1.0, 'Y': 3.0, 'Z': 5.0}),\n", " 'Island_2': pd.Series({'X': 3.0, 'Y': 5.0, 'Z': 2.0})\n", " }\n", " expected_df = pd.DataFrame.from_dict(expected_data, orient='index')\n", " result = aggregate_spot_compositions(labelled, comps)\n", " print(result)\n", " # Sort columns for consistent ordering\n", " result = result.sort_index(axis=1)\n", " expected_df = expected_df.sort_index(axis=1)\n", " assert_frame_equal(result, expected_df)\n", " \n", " def test_all_background(self):\n", " \"\"\"\n", " Test the case where all spots are background (label 0).\n", " \"\"\"\n", " labelled = np.zeros((2, 2), dtype=int)\n", " compositions = [pd.Series({'A': 1}) for _ in range(4)]\n", " result = aggregate_spot_compositions(labelled, compositions)\n", " # Expect an empty DataFrame (no islands)\n", " self.assertEqual(result.shape[0], 0)\n", "\n", " def test_union_of_keys(self):\n", " \"\"\"\n", " Test that the union of composition keys is maintained across islands,\n", " even if an island does not have a composition for some keys.\n", " \"\"\"\n", " # Island 1 has only key 'A', while Island 2 has only key 'B'.\n", " labelled = np.array([[1, 2]])\n", " compositions = [\n", " pd.Series({'A': 5}), # For Island_1 (only key 'A')\n", " pd.Series({'B': 7}) # For Island_2 (only key 'B')\n", " ]\n", " # The expected aggregated DataFrame should include both keys for each island:\n", " expected_data = {\n", " 'Island_1': pd.Series({'A': 5, 'B': 0}),\n", " 'Island_2': pd.Series({'A': 0, 'B': 7})\n", " }\n", " expected_df = pd.DataFrame.from_dict(expected_data, orient='index')\n", " expected_df = expected_df.sort_index(axis=1)\n", " \n", " result = aggregate_spot_compositions(labelled, compositions)\n", " result = result.sort_index(axis=1)\n", " assert_frame_equal(result, expected_df)\n", "\n", "# Run the tests in Jupyter Notebook\n", "if __name__ == '__main__':\n", " unittest.main(argv=[''], verbosity=2, exit=False)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "daee0719-90fd-46c4-ae81-e76923d01e5d", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python3.11 (mesa)", "language": "python", "name": "python311_mesa" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3" } }, "nbformat": 4, "nbformat_minor": 5 }