From c119a41dc90adf5d7f1e8f3126e4e924cbe2078b Mon Sep 17 00:00:00 2001 From: Yadunand Prem Date: Mon, 25 Mar 2024 12:58:23 +0800 Subject: [PATCH] feat: 2109s PS5 3.5 SOLVED --- cs2109s/labs/ps5/ps5.ipynb | 236 +++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 128 deletions(-) diff --git a/cs2109s/labs/ps5/ps5.ipynb b/cs2109s/labs/ps5/ps5.ipynb index 726a4b3..c4dc986 100644 --- a/cs2109s/labs/ps5/ps5.ipynb +++ b/cs2109s/labs/ps5/ps5.ipynb @@ -65,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -116,7 +116,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 56, "metadata": {}, "outputs": [ { @@ -314,7 +314,7 @@ "[5 rows x 23 columns]" ] }, - "execution_count": 119, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -332,7 +332,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -343,7 +343,7 @@ "Name: Class, dtype: int64" ] }, - "execution_count": 120, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -376,7 +376,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 58, "metadata": {}, "outputs": [ { @@ -396,7 +396,7 @@ "Name: Class, Length: 284807, dtype: int64" ] }, - "execution_count": 121, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -415,7 +415,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 59, "metadata": {}, "outputs": [ { @@ -532,7 +532,7 @@ "[2 rows x 23 columns]" ] }, - "execution_count": 122, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -551,7 +551,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 60, "metadata": {}, "outputs": [ { @@ -571,7 +571,7 @@ "Name: Class, Length: 284807, dtype: bool" ] }, - "execution_count": 123, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -582,7 +582,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 61, "metadata": {}, "outputs": [ { @@ -955,7 +955,7 @@ "[284315 rows x 23 columns]" ] }, - "execution_count": 124, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -974,7 +974,7 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 62, "metadata": {}, "outputs": [ { @@ -1145,7 +1145,7 @@ "[4 rows x 23 columns]" ] }, - "execution_count": 125, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -1212,7 +1212,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 63, "metadata": {}, "outputs": [], "source": [ @@ -1238,7 +1238,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 64, "metadata": {}, "outputs": [], "source": [ @@ -1284,7 +1284,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 65, "metadata": {}, "outputs": [], "source": [ @@ -1311,7 +1311,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 66, "metadata": {}, "outputs": [], "source": [ @@ -1387,7 +1387,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 67, "metadata": {}, "outputs": [], "source": [ @@ -1429,7 +1429,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -1544,7 +1544,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 69, "metadata": {}, "outputs": [], "source": [ @@ -1568,6 +1568,7 @@ " \n", " # Machine epsilon for numpy `float64` type\n", " eps = np.finfo(np.float64).eps\n", + "\n", " y_predicted = 1/(1+np.exp(-X @ weight_vector)) + eps\n", " first = -y * np.log(y_predicted)\n", " second = (1-y) * np.log(1-y_predicted)\n", @@ -1577,7 +1578,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 70, "metadata": {}, "outputs": [], "source": [ @@ -1601,7 +1602,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 71, "metadata": {}, "outputs": [], "source": [ @@ -1631,7 +1632,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ @@ -1659,7 +1660,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -1686,7 +1687,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -1714,7 +1715,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -1743,7 +1744,6 @@ " The final (n,) weight parameters\n", " '''\n", " weights = np.zeros(X_train.shape[1])\n", - " print(weights)\n", " for _ in range(max_num_epochs):\n", " weights = weight_update(X_train, y_train, alpha, weights)\n", " if cost_function(X_train, y_train, weights) <= threshold:\n", @@ -1753,17 +1753,9 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 76, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0. 0.]\n" - ] - } - ], + "outputs": [], "source": [ "data1 = [[111.1, 10, 0], [111.2, 20, 0], [111.3, 10, 0], [111.4, 10, 0], [111.5, 10, 0], [211.6, 80, 1],[111.4, 10, 0], [111.5, 80, 1], [211.6, 80, 1]]\n", "df1 = pd.DataFrame(data1, columns = ['V1', 'V2', 'Class'])\n", @@ -1788,83 +1780,40 @@ }, { "cell_type": "code", - "execution_count": 182, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "def weight_update_stochastic(X: np.ndarray, y: np.ndarray, alpha: np.float64, weight_vector: np.ndarray) -> np.ndarray:\n", - " '''\n", - " Do the weight update for one step in gradient descent.\n", - "\n", - " Parameters\n", - " ----------\n", - " X: np.ndarray\n", - " (1, n) training dataset (features).\n", - " y: np.ndarray\n", - " one y in training dataset (corresponding targets).\n", - " alpha: np.float64\n", - " logistic regression learning rate.\n", - " weight_vector: np.ndarray\n", - " (n, 1) vector of weight parameters.\n", - "\n", - " Returns\n", - " -------\n", - " New (n,) weight parameters after one round of update.\n", - " '''\n", - "\n", - " index = np.random.choice(X.shape[0], 1)\n", - " X_sel = X[index]\n", - " y_sel = y[index]\n", - " y_predicted = 1/(1+np.exp(-X_sel @ weight_vector))\n", - " return weight_vector - alpha * X_sel.T @ (y_predicted - y_sel)" + " y_predicted = 1/(1+np.exp(-X @ weight_vector))\n", + " grad = (X.T @ (y_predicted - y)) / X.shape[0]\n", + " return weight_vector - alpha * grad" ] }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 78, "metadata": {}, "outputs": [], "source": [ "def logistic_regression_stochastic_gradient_descent(X_train: np.ndarray, y_train: np.ndarray, max_num_iterations: int=250, threshold: np.float64=0.05, alpha: np.float64=1e-5, seed :int=43) -> np.ndarray:\n", - " '''\n", - " Initialize your weight to zeros. Write a terminating condition, and run the weight update for some iterations.\n", - " Get the resulting weight vector.\n", - "\n", - " Parameters\n", - " ----------\n", - " X_train: np.ndarray\n", - " (m, n) training dataset (features).\n", - " y_train: np.ndarray\n", - " (m,) training dataset (corresponding targets).\n", - " max_num_iterations: int\n", - " this should be one of the terminating conditions. \n", - " The gradient descent step should happen at most max_num_iterations times.\n", - " threshold: np.float64\n", - " terminating when error <= threshold value, or if you reach the max number of update rounds first.\n", - " alpha: np.float64\n", - " logistic regression learning rate.\n", - " seed: int\n", - " seed for random number generation.\n", - "\n", - " Returns\n", - " -------\n", - " The final (n,) weight parameters\n", - " '''\n", " np.random.seed(seed)\n", - "\n", - " weights = np.zeros(X_train.shape[1])\n", + " n = X_train.shape[1]\n", + " weights = np.zeros(shape=(n,))\n", " error = cost_function(X_train, y_train, weights)\n", - " for i in range(max_num_iterations):\n", + " for _ in range(max_num_iterations):\n", " if error <= threshold:\n", - " break\n", - " weights = weight_update_stochastic(X_train, y_train, alpha, weights)\n", + " return weights\n", + " index = np.random.choice(X_train.shape[0], 1)\n", + " weights = weight_update_stochastic(X_train[index], y_train[index], alpha, weights)\n", " error = cost_function(X_train, y_train, weights)\n", - " return weights" + " return weights\n", + " " ] }, { "cell_type": "code", - "execution_count": 184, + "execution_count": 79, "metadata": {}, "outputs": [], "source": [ @@ -1909,42 +1858,53 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 81, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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w4YNwyeffII///wT//vf/2Bra4uZM2fCxcUFoaGh6NixI4YNG6boFsj783777bfRr18/jBgxAi9evIAQAj179kR4eDimTJmCVq1a4fLly5g1axYiIiIQERGh1PJw8eJFjB8/HrNnz4azszN++uknjBs3Dunp6fj0009hZWWFoUOHYs2aNVi4cCFsbW0V565cuRJmZmYYOnRovvdEndjYWDRv3hyOjo5Yvnw57O3tsWXLFgwePBgPHz7EZ599BgBYuHAhZs+ejenTp6N169bIyMjA9evXlRK5gQMH4sKFC/jiiy9Qq1YtPH36FBcuXEBycnKBMTx48AD29vb48ssvUaVKFfz777/YuHEjmjZtiqioKPj4+AAAEhISEBQUBEtLS6xatQqOjo7Ytm0bxowZo1Ln7du3ERgYiA8++AC2tra4d+8elixZgpYtW+LKlSswNTUtMKbExES89957+OSTTzBr1izs2bMHU6ZMgaurK95//32N7klRPs9ATpLQvXt3fPTRR5g8eXKRW1WnTp2KFi1a4IcffkBqaio+//xzdOvWDTExMTA2NsbKlSvx4Ycf4vbt29izZ4/G9X7wwQd4++23sX37dkRFRWHq1KnIzMxEbGws3n77bXz44Yc4fPgwvvrqK7i6umLixIkAchKVHj164MSJE/jss8/QvHlz3L9/H7NmzUKbNm1w/vx5mJub53vdQ4cOISMjA23btlU6rsnvZnR0NJo3bw53d3csXrwYzs7OOHjwIMaOHYukpCTMmjULAPDs2TO0bNkS9+7dw+eff46mTZvi+fPn+PPPP5GQkABfX19FnW3atMGUKVPw9OlTVKpUSeP7V6EIMhjr168XAMTp06dFRkaGePbsmfjjjz9ElSpVhLW1tUhMTBRCCHH06FEBQBw9elQIIURWVpZwdXUV9evXF1lZWYr6nj17JhwdHUXz5s0VxxYtWiQAiLt37xYajzb1ymP65ZdfCq136NChwtTUVERHR+dbRl5f69atlY4/efJEmJubi86dOysdj4uLE1KpVLz77rtCCCGSkpIEALFs2bJ8r7Fz504BQFy8eLHQmPNey8TERHz88cdKx589eyacnZ1Fnz59FMcGDRokAIiff/5ZqWznzp2Fj4+P4vnjx48FADFr1iyV682aNUsAEDNnzlQ6HhoaKgCIhQsXKh3fsWOHACDWrFmjOObh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WFtb48KFCzh27BgOHTqEmTNnYvbs2Th37pziWvv27UPVqlWV6pJKpQD+S24K8u+//wIAqlSpUmhZIiocxwARkc7MmjULX3/9NR48eAAg58s6ISFB8XpWVhauXr2qVZ3yJCMrK0t3gf4/f39/XLt2DdWrV4e3t7fSw9LSUu058qn40dHRSsdNTEzQvn17LFy4EJcvX8a9e/dw5MgR1KlTB1KpFHFxcSrXcHNzA5DTcnXixAm1yaHc1atXYWpqirp16+rmzRMZOCZARKQzbdq0Qd26dRULBL755pvYt28f9u3bh+vXr2PUqFF4+vSpVnU6OjrC3NxcMUA5JSVFZ/GOHj0a//77L/r374+zZ8/izp07OHToEIYOHZpvwlWlShX4+/vj5MmTimN//PEHli9fjosXL+L+/fvYtGkTsrOz4ePjA2tra3z66aeYMGECNm7ciNu3byMqKgorVqxQrKM0ZswYpKamol+/fjh//jxu3ryJzZs3IzY2VnGNEydOoFWrVhq1FhFR4ZgAEZFOTZw4EWvXrkV8fDyGDh2KQYMG4f3330dQUBA8PT3Rtm1breozMTHB8uXL8f3338PV1RU9evTQWayurq7466+/kJWVhZCQENSrVw/jxo2Dra0tjIzy/+fxww8/xE8//aR4XqlSJezevRtvvvkmateujdWrV2Pbtm2K1pr//e9/mDlzJhYsWIDatWsjJCQEv//+u2KKv729PY4cOYLnz58jKCgIAQEBWLt2rVL33bZt2zB8+HCdvXciQ8eVoImItPT69Wv4+Phg+/btCAwMLPHr7du3D5MmTcLly5dhYsKhm0S6wBYgIiItyWQybNq0qcAFE3XpxYsXWL9+PZMfIh1iCxAREREZHLYAERERkcFhAkREREQGhwkQERERGRwmQERERGRwmAARERGRwWECRERERAaHCRAREREZHCZAREREZHCYABEREZHB+T+AO0GsD8HrVQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import matplotlib.pyplot as plt\n", "from time import time\n", "\n", - "# X_sample, y_sample = \n", - "# num_interations = \n", - "# batch_times = []\n", - "# batch_costs = []\n", + "X_sample, y_sample = X[:1000], y[:1000].flatten()\n", + "num_interations = 2000\n", + "batch_times = []\n", + "batch_costs = []\n", "\n", - "# for i in range(50, num_interations + 1, 50):\n", - "# start = time()\n", - "# weight_vector = logistic_regression_batch_gradient_descent(X_sample, y_sample, i, 0, 1e-5)\n", - "# stop = time()\n", - "# batch_times.append(stop - start)\n", - "# batch_costs.append(cost_function(X_sample, y_sample, weight_vector))\n", - "# plt.plot(batch_times, batch_costs, 'g^', label=\"Batch Gradient Descent\")\n", + "for i in range(50, num_interations + 1, 50):\n", + " start = time()\n", + " weight_vector = logistic_regression_batch_gradient_descent(X_sample, y_sample, i, 0, 1e-5)\n", + " stop = time()\n", + " batch_times.append(stop - start)\n", + " batch_costs.append(cost_function(X_sample, y_sample, weight_vector))\n", + "plt.plot(batch_times, batch_costs, 'g^', label=\"Batch Gradient Descent\")\n", "\n", - "# stochastic_times = []\n", - "# stochastic_costs = []\n", - "# for i in range(50, num_interations + 1, 50):\n", - "# start = time()\n", - "# weight_vector = logistic_regression_stochastic_gradient_descent(X_sample, y_sample, i, 0, 1e-5)\n", - "# stop = time()\n", - "# stochastic_times.append(stop - start)\n", - "# stochastic_costs.append(cost_function(X_sample, y_sample, weight_vector))\n", - "# plt.plot(stochastic_times, stochastic_costs, 'bs', label=\"Stochastic Gradient Descent\")\n", + "stochastic_times = []\n", + "stochastic_costs = []\n", + "for i in range(50, num_interations + 1, 50):\n", + " start = time()\n", + " weight_vector = logistic_regression_stochastic_gradient_descent(X_sample, y_sample, i, 0, 1e-5)\n", + " stop = time()\n", + " stochastic_times.append(stop - start)\n", + " stochastic_costs.append(cost_function(X_sample, y_sample, weight_vector))\n", + "plt.plot(stochastic_times, stochastic_costs, 'bs', label=\"Stochastic Gradient Descent\")\n", "\n", - "# plt.xlabel('Runtime (sec)')\n", - "# plt.ylabel('Cross Entropy Loss')\n", - "# plt.legend()\n", - "# plt.title('Plot of cross entropy loss against runtime (sec)')\n", + "plt.xlabel('Runtime (sec)')\n", + "plt.ylabel('Cross Entropy Loss')\n", + "plt.legend()\n", + "plt.title('Plot of cross entropy loss against runtime (sec)')\n", "\n", - "# plt.show()" + "plt.show()" ] }, { @@ -1976,7 +1936,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -2006,16 +1966,36 @@ " -------\n", " The final (n,) weight parameters\n", " '''\n", + " X_sel = X_train[(y_train == class_i)]\n", + " Y_sel = y_train[(y_train == class_i)]\n", "\n", - " # TODO: add your solution here and remove `raise NotImplementedError`\n", - " raise NotImplementedError" + " weights = np.zeros(X_sel.shape[1])\n", + " for _ in range(max_num_epochs):\n", + " weights = weight_update(X_sel, Y_sel, alpha, weights)\n", + " if cost_function(X_sel, Y_sel, weights) <= threshold:\n", + " break\n", + " return weights\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 74, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for -: 'float' and 'str'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[74], line 10\u001b[0m\n\u001b[1;32m 8\u001b[0m max_num_epochs1 \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m20\u001b[39m\n\u001b[1;32m 9\u001b[0m expected1 \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mtranspose([\u001b[38;5;241m6.75\u001b[39m, \u001b[38;5;241m0.125\u001b[39m, \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m6.0\u001b[39m])\n\u001b[0;32m---> 10\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m np\u001b[38;5;241m.\u001b[39marray_equal(\u001b[43mmulti_class_logistic_regression_batch_gradient_descent\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_num_epochs1\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0.05\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43msome\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m, expected1)\n", + "Cell \u001b[0;32mIn[73], line 32\u001b[0m, in \u001b[0;36mmulti_class_logistic_regression_batch_gradient_descent\u001b[0;34m(X_train, y_train, max_num_epochs, threshold, alpha, class_i)\u001b[0m\n\u001b[1;32m 30\u001b[0m weights \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mzeros(X_sel\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m1\u001b[39m])\n\u001b[1;32m 31\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _ \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(max_num_epochs):\n\u001b[0;32m---> 32\u001b[0m weights \u001b[38;5;241m=\u001b[39m \u001b[43mweight_update\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX_sel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mY_sel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mweights\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 33\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m cost_function(X_sel, Y_sel, weights) \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m threshold:\n\u001b[1;32m 34\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n", + "Cell \u001b[0;32mIn[18], line 22\u001b[0m, in \u001b[0;36mweight_update\u001b[0;34m(X, y, alpha, weight_vector)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;124;03mDo the weight update for one step in gradient descent\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[38;5;124;03mNew weight vector after one round of update.\u001b[39;00m\n\u001b[1;32m 19\u001b[0m \u001b[38;5;124;03m'''\u001b[39;00m\n\u001b[1;32m 20\u001b[0m y_predicted \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\u001b[38;5;241m/\u001b[39m(\u001b[38;5;241m1\u001b[39m\u001b[38;5;241m+\u001b[39mnp\u001b[38;5;241m.\u001b[39mexp(\u001b[38;5;241m-\u001b[39mX \u001b[38;5;241m@\u001b[39m weight_vector))\n\u001b[0;32m---> 22\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m weight_vector \u001b[38;5;241m-\u001b[39m alpha \u001b[38;5;241m*\u001b[39m X\u001b[38;5;241m.\u001b[39mT \u001b[38;5;241m@\u001b[39m (\u001b[43my_predicted\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m) \u001b[38;5;241m/\u001b[39m \u001b[38;5;28mlen\u001b[39m(y)\n", + "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for -: 'float' and 'str'" + ] + } + ], "source": [ "data1 = [[26, 9, 69, 'full'],\n", " [54, 3, 16, 'some'],\n",