{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "aae4bcad-de91-4b2d-88c7-4aa5a87322cd",
   "metadata": {},
   "source": [
    "# Categorical Spatial Lags"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e80fbeb-d8f9-4d33-903d-ae9c8c0ca54b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from libpysal.graph import Graph\n",
    "from libpysal.graph._spatial_lag import _lag_spatial\n",
    "from libpysal.weights.util import lat2W\n",
    "\n",
    "graph = Graph.from_W(lat2W(3, 3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "61a1c34b-3ede-4ff9-b4b4-f2f8ea63892e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4.,  6.,  6., 10., 16., 14., 10., 18., 12.])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = np.arange(9)\n",
    "_lag_spatial(graph, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "229bacbc-d286-4cf8-9cd2-0ac370dc60e4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['b', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = np.array([*\"ababcbcbc\"])\n",
    "_lag_spatial(graph, y, categorical=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "e4b99d6e-1cea-450d-8a56-7f113f50e725",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['a', 'b', 'a', 'a', 'c', 'b', 'c', 'b', 'c'], dtype='<U1')"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y[3] = \"a\"\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "efb331be-e162-41ce-a758-13a05000229a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['a', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.seed(12345)\n",
    "_lag_spatial(graph, y, categorical=True, ties=\"random\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "cbd5848f-0b97-4b34-b04b-7e7e946e04ae",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['b', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_lag_spatial(graph, y, categorical=True, ties=\"random\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "42f588b7-ccde-4ed5-8a1b-a876ca59ca4b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['a', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_lag_spatial(graph, y, categorical=True, ties=\"tryself\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "603cb33c-3fdb-4d0e-aa7e-8b99374ec3fc",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "There are 2 ties that must be broken to define the categorical spatial lag for these observations. To address this issue, consider setting `ties='tryself'` or `ties='random'` or consult the documentation about ties and the categorical spatial lag.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[31], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43m_lag_spatial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgraph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcategorical\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/para/1_projects/code-pysal-libpysal/libpysal/libpysal/graph/_spatial_lag.py:58\u001b[0m, in \u001b[0;36m_lag_spatial\u001b[0;34m(graph, y, categorical, ties)\u001b[0m\n\u001b[1;32m     56\u001b[0m n_ties \u001b[38;5;241m=\u001b[39m gb\u001b[38;5;241m.\u001b[39mapply(_check_ties)\u001b[38;5;241m.\u001b[39msum()\n\u001b[1;32m     57\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n_ties \u001b[38;5;129;01mand\u001b[39;00m ties \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mraise\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m---> 58\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m     59\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThere are \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mn_ties\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m ties that must be broken \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     60\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mto define the categorical \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     61\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mspatial lag for these observations. To address this \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     62\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124missue, consider setting `ties=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtryself\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m` \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     63\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mor `ties=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrandom\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m` or consult the documentation \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     64\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mabout ties and the categorical spatial lag.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     65\u001b[0m     )\n\u001b[1;32m     66\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m ties \u001b[38;5;129;01min\u001b[39;00m (\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrandom\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtryself\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mraise\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[1;32m     67\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m gb\u001b[38;5;241m.\u001b[39mapply(_get_categorical_lag)\u001b[38;5;241m.\u001b[39mvalues\n",
      "\u001b[0;31mValueError\u001b[0m: There are 2 ties that must be broken to define the categorical spatial lag for these observations. To address this issue, consider setting `ties='tryself'` or `ties='random'` or consult the documentation about ties and the categorical spatial lag."
     ]
    }
   ],
   "source": [
    "_lag_spatial(graph, y, categorical=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2e60d703-41f3-4dd8-80ad-c968e1e72e86",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "import libpysal\n",
    "\n",
    "np.random.seed(12345)\n",
    "w = libpysal.weights.lat2W(3, 3)\n",
    "y = np.array([*\"ababcbcbc\"])\n",
    "y_l = libpysal.weights.lag_categorical(w, y)\n",
    "np.array_equal(y_l, np.array([\"b\", \"a\", \"b\", \"c\", \"b\", \"c\", \"b\", \"c\", \"b\"]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cd0dcf01-ddfe-49a0-b6c5-da9a6d7d73cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from libpysal import graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "86ffb3f3-4743-4339-acf7-36f42789093f",
   "metadata": {},
   "outputs": [],
   "source": [
    "g = graph.Graph.from_W(w)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "947f99bb-8b2f-44b0-810b-e1699559eaae",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "focal  neighbor\n",
       "0      1           1.0\n",
       "       3           1.0\n",
       "1      0           1.0\n",
       "       2           1.0\n",
       "       4           1.0\n",
       "2      1           1.0\n",
       "       5           1.0\n",
       "3      0           1.0\n",
       "       4           1.0\n",
       "       6           1.0\n",
       "4      1           1.0\n",
       "       3           1.0\n",
       "       5           1.0\n",
       "       7           1.0\n",
       "5      2           1.0\n",
       "       4           1.0\n",
       "       8           1.0\n",
       "6      3           1.0\n",
       "       7           1.0\n",
       "7      4           1.0\n",
       "       6           1.0\n",
       "       8           1.0\n",
       "8      5           1.0\n",
       "       7           1.0\n",
       "Name: weight, dtype: float64"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "g.adjacency"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f58b41ad-a1c1-49a5-ac06-1e50b2b7d66c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from libpysal.graph._spatial_lag import _lag_spatial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "38db58fa-01ba-4243-9137-69cd4893732c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['b', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy\n",
    "\n",
    "_lag_spatial(g, numpy.array(y), categorical=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afe32b9a-000b-4cb5-8e2d-7e708586e7e5",
   "metadata": {},
   "source": [
    "## No Ties"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5d075def-1b9c-4c19-b7fd-4d219fe5e754",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['b', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_lag_spatial(g, numpy.array(y), categorical=True, ties=\"tryself\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b65e6f7e-081b-4d36-9e5a-edbe5fe12896",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.array_equal(y_l, _lag_spatial(g, numpy.array(y), categorical=True, ties=\"random\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d3bb409f-7cd0-4258-b880-270461413340",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.array_equal(y_l, _lag_spatial(g, numpy.array(y), categorical=True, ties=\"raise\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1c9b0f4-018f-44a8-9ed8-a6b7523d8108",
   "metadata": {},
   "source": [
    "## Ties"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "de2f2e7b-a6f6-4e43-bf81-69facd3e0cff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['a', 'b', 'a', 'a', 'c', 'b', 'c', 'b', 'c'], dtype='<U1')"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y[3] = \"a\"\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "91d189f3-330f-45e9-93b9-5c19aeb39f82",
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(12345)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "fa883f5f-4a7e-44e7-bde0-741da732b42e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['a', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_lag_spatial(g, numpy.array(y), categorical=True, ties=\"random\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "a56cffaa-19e6-4733-a5fb-4e6e58fbcc56",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['a', 'a', 'b', 'c', 'b', 'c', 'b', 'c', 'b'], dtype=object)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_lag_spatial(g, numpy.array(y), categorical=True, ties=\"tryself\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d1971624-7e66-46be-9c88-69d8764ba709",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "There are 2 ties that must be broken to define the categorical spatial lag for these observations. To address this issue, consider setting `ties='tryself'` or `ties='random'` or consult the documentation about ties and the categorical spatial lag.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[13], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m y_l \u001b[38;5;241m=\u001b[39m libpysal\u001b[38;5;241m.\u001b[39mweights\u001b[38;5;241m.\u001b[39mlag_categorical(w, y)\n\u001b[0;32m----> 2\u001b[0m np\u001b[38;5;241m.\u001b[39marray_equal(y_l, \u001b[43m_lag_spatial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumpy\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marray\u001b[49m\u001b[43m(\u001b[49m\u001b[43my\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcategorical\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mties\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mraise\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m)\n",
      "File \u001b[0;32m~/para/1_projects/code-pysal-libpysal/libpysal/libpysal/graph/_spatial_lag.py:58\u001b[0m, in \u001b[0;36m_lag_spatial\u001b[0;34m(graph, y, categorical, ties)\u001b[0m\n\u001b[1;32m     56\u001b[0m n_ties \u001b[38;5;241m=\u001b[39m gb\u001b[38;5;241m.\u001b[39mapply(_check_ties)\u001b[38;5;241m.\u001b[39msum()\n\u001b[1;32m     57\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n_ties \u001b[38;5;129;01mand\u001b[39;00m ties \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mraise\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m---> 58\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m     59\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThere are \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mn_ties\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m ties that must be broken \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     60\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mto define the categorical \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     61\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mspatial lag for these observations. To address this \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     62\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124missue, consider setting `ties=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtryself\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m` \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     63\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mor `ties=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrandom\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m` or consult the documentation \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     64\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mabout ties and the categorical spatial lag.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     65\u001b[0m     )\n\u001b[1;32m     66\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m ties \u001b[38;5;129;01min\u001b[39;00m (\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrandom\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtryself\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mraise\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[1;32m     67\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m gb\u001b[38;5;241m.\u001b[39mapply(_get_categorical_lag)\u001b[38;5;241m.\u001b[39mvalues\n",
      "\u001b[0;31mValueError\u001b[0m: There are 2 ties that must be broken to define the categorical spatial lag for these observations. To address this issue, consider setting `ties='tryself'` or `ties='random'` or consult the documentation about ties and the categorical spatial lag."
     ]
    }
   ],
   "source": [
    "y_l = libpysal.weights.lag_categorical(w, y)\n",
    "np.array_equal(y_l, _lag_spatial(g, numpy.array(y), categorical=True, ties=\"raise\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e23094f6-94b7-49c1-a537-206ef4b89d37",
   "metadata": {},
   "outputs": [],
   "source": [
    "y_l"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "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.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
