{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9e9acafc",
   "metadata": {},
   "source": [
    "# Assignment 1: worked solution\n",
    "\n",
    "This notebook explains the Day 1 assignment one task at a time.\n",
    "\n",
    "Your code may look different and still be correct. Read each solution and make sure you\n",
    "understand what its input is, what it returns, and why it works.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "dfdb1b7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "MEASURES = [\"bill_length_mm\", \"bill_depth_mm\", \"flipper_length_mm\", \"body_mass_g\"]\n",
    "\n",
    "CSV = \"../data/penguins.csv\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5517b74",
   "metadata": {},
   "source": [
    "## Task 1: `load_and_clean`\n",
    "\n",
    "Read the CSV file. Remove rows that are missing any of the four measurement columns.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "46378e5d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "344 rows in the file\n",
      "342 rows after dropping the unmeasured birds\n"
     ]
    }
   ],
   "source": [
    "def load_and_clean(csv_path):\n",
    "    df = pd.read_csv(csv_path)\n",
    "    return df.dropna(subset=MEASURES)\n",
    "\n",
    "\n",
    "penguins = load_and_clean(CSV)\n",
    "print(len(pd.read_csv(CSV)), \"rows in the file\")\n",
    "print(len(penguins), \"rows after dropping the unmeasured birds\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d2a9785",
   "metadata": {},
   "source": [
    "The original table has 344 rows, and 342 remain.\n",
    "\n",
    "`dropna(subset=MEASURES)` checks only the four measurement columns. It does not remove a row\n",
    "just because another column, such as `sex`, is missing.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "e9107745-dc82-4379-b246-9bbfef3c8855",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>species</th>\n",
       "      <th>island</th>\n",
       "      <th>bill_length_mm</th>\n",
       "      <th>bill_depth_mm</th>\n",
       "      <th>flipper_length_mm</th>\n",
       "      <th>body_mass_g</th>\n",
       "      <th>sex</th>\n",
       "      <th>year</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Adelie</td>\n",
       "      <td>Torgersen</td>\n",
       "      <td>39.1</td>\n",
       "      <td>18.7</td>\n",
       "      <td>181.0</td>\n",
       "      <td>3750.0</td>\n",
       "      <td>male</td>\n",
       "      <td>2007</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Adelie</td>\n",
       "      <td>Torgersen</td>\n",
       "      <td>39.5</td>\n",
       "      <td>17.4</td>\n",
       "      <td>186.0</td>\n",
       "      <td>3800.0</td>\n",
       "      <td>female</td>\n",
       "      <td>2007</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Adelie</td>\n",
       "      <td>Torgersen</td>\n",
       "      <td>40.3</td>\n",
       "      <td>18.0</td>\n",
       "      <td>195.0</td>\n",
       "      <td>3250.0</td>\n",
       "      <td>female</td>\n",
       "      <td>2007</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Adelie</td>\n",
       "      <td>Torgersen</td>\n",
       "      <td>36.7</td>\n",
       "      <td>19.3</td>\n",
       "      <td>193.0</td>\n",
       "      <td>3450.0</td>\n",
       "      <td>female</td>\n",
       "      <td>2007</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Adelie</td>\n",
       "      <td>Torgersen</td>\n",
       "      <td>39.3</td>\n",
       "      <td>20.6</td>\n",
       "      <td>190.0</td>\n",
       "      <td>3650.0</td>\n",
       "      <td>male</td>\n",
       "      <td>2007</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  species     island  bill_length_mm  bill_depth_mm  flipper_length_mm  \\\n",
       "0  Adelie  Torgersen            39.1           18.7              181.0   \n",
       "1  Adelie  Torgersen            39.5           17.4              186.0   \n",
       "2  Adelie  Torgersen            40.3           18.0              195.0   \n",
       "4  Adelie  Torgersen            36.7           19.3              193.0   \n",
       "5  Adelie  Torgersen            39.3           20.6              190.0   \n",
       "\n",
       "   body_mass_g     sex  year  \n",
       "0       3750.0    male  2007  \n",
       "1       3800.0  female  2007  \n",
       "2       3250.0  female  2007  \n",
       "4       3450.0  female  2007  \n",
       "5       3650.0    male  2007  "
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "penguins.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b47430d",
   "metadata": {},
   "source": [
    "## Task 2: `species_summary`\n",
    "\n",
    "Create one row for each species. Include the number of penguins, mean body mass, and mean\n",
    "flipper length.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "ed2af302",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>n</th>\n",
       "      <th>mean_mass</th>\n",
       "      <th>mean_flipper</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>species</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Adelie</th>\n",
       "      <td>151</td>\n",
       "      <td>3700.7</td>\n",
       "      <td>190.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chinstrap</th>\n",
       "      <td>68</td>\n",
       "      <td>3733.1</td>\n",
       "      <td>195.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Gentoo</th>\n",
       "      <td>123</td>\n",
       "      <td>5076.0</td>\n",
       "      <td>217.2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             n  mean_mass  mean_flipper\n",
       "species                                \n",
       "Adelie     151     3700.7         190.0\n",
       "Chinstrap   68     3733.1         195.8\n",
       "Gentoo     123     5076.0         217.2"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def species_summary(df):\n",
    "    return df.groupby(\"species\").agg(\n",
    "        n=(\"species\", \"size\"),\n",
    "        mean_mass=(\"body_mass_g\", \"mean\"),\n",
    "        mean_flipper=(\"flipper_length_mm\", \"mean\"),\n",
    "    )\n",
    "\n",
    "\n",
    "display(species_summary(penguins).round(1))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9213f13",
   "metadata": {},
   "source": [
    "`groupby(\"species\")` makes one group for each species. `.agg()` calculates the requested\n",
    "summaries for every group.\n",
    "\n",
    "For example, `n=(\"species\", \"size\")` creates a column called `n` and fills it with the size of\n",
    "each species group.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85bb6b6d",
   "metadata": {},
   "source": [
    "## Task 3: `heaviest_species`\n",
    "\n",
    "Find the species with the highest average body mass.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "ac801bf9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "species\n",
      "Adelie       3700.7\n",
      "Chinstrap    3733.1\n",
      "Gentoo       5076.0\n",
      "\n",
      "heaviest: Gentoo\n"
     ]
    }
   ],
   "source": [
    "def heaviest_species(df):\n",
    "    mean_mass = df.groupby(\"species\")[\"body_mass_g\"].mean()\n",
    "    return mean_mass.idxmax()\n",
    "\n",
    "\n",
    "print(penguins.groupby(\"species\")[\"body_mass_g\"].mean().round(1).to_string())\n",
    "print()\n",
    "print(\"heaviest:\", heaviest_species(penguins))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "506f803d",
   "metadata": {},
   "source": [
    "`idxmax()` returns the label beside the largest value. Here, that label is the species name.\n",
    "\n",
    "By contrast, `max()` would return the largest mean mass but not its species.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc2b391a",
   "metadata": {},
   "source": [
    "## Check the solution\n",
    "\n",
    "Run this cell. All three checks should print `OK`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "e6f818b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OK  load_and_clean -> 342 rows\n",
      "OK  species_summary ->\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>n</th>\n",
       "      <th>mean_mass</th>\n",
       "      <th>mean_flipper</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>species</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Adelie</th>\n",
       "      <td>151</td>\n",
       "      <td>3700.7</td>\n",
       "      <td>190.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chinstrap</th>\n",
       "      <td>68</td>\n",
       "      <td>3733.1</td>\n",
       "      <td>195.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Gentoo</th>\n",
       "      <td>123</td>\n",
       "      <td>5076.0</td>\n",
       "      <td>217.2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             n  mean_mass  mean_flipper\n",
       "species                                \n",
       "Adelie     151     3700.7         190.0\n",
       "Chinstrap   68     3733.1         195.8\n",
       "Gentoo     123     5076.0         217.2"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OK  heaviest_species -> Gentoo\n"
     ]
    }
   ],
   "source": [
    "penguins = load_and_clean(\"../data/penguins.csv\")\n",
    "\n",
    "assert penguins is not None, \"load_and_clean still returns None\"\n",
    "assert len(penguins) == 342, f\"expected 342 clean rows, got {len(penguins)}\"\n",
    "print(\"OK  load_and_clean ->\", len(penguins), \"rows\")\n",
    "\n",
    "summary = species_summary(penguins)\n",
    "assert summary is not None, \"species_summary still returns None\"\n",
    "assert list(summary.columns) == [\"n\", \"mean_mass\", \"mean_flipper\"], list(summary.columns)\n",
    "assert len(summary) == 3, f\"expected 3 species, got {len(summary)}\"\n",
    "print(\"OK  species_summary ->\")\n",
    "display(summary.round(1))\n",
    "\n",
    "answer = heaviest_species(penguins)\n",
    "assert answer == \"Gentoo\", f\"expected 'Gentoo', got {answer!r}\"\n",
    "print(\"OK  heaviest_species ->\", answer)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a851ced2",
   "metadata": {},
   "source": [
    "## Optional task: `count_by_island`\n",
    "\n",
    "Count each penguin species on each island.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "103a06a2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th>island</th>\n",
       "      <th>Biscoe</th>\n",
       "      <th>Dream</th>\n",
       "      <th>Torgersen</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>species</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Adelie</th>\n",
       "      <td>44</td>\n",
       "      <td>56</td>\n",
       "      <td>51</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chinstrap</th>\n",
       "      <td>0</td>\n",
       "      <td>68</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Gentoo</th>\n",
       "      <td>123</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "island     Biscoe  Dream  Torgersen\n",
       "species                            \n",
       "Adelie         44     56         51\n",
       "Chinstrap       0     68          0\n",
       "Gentoo        123      0          0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def count_by_island(df):\n",
    "    #note of the difference between count and size in aggregation:\n",
    "    #count: Counts only non-null rows per column.\n",
    "    #size: Counts all rows including nulls and duplicates.\n",
    "    return df.pivot_table(index=\"species\", columns=\"island\",\n",
    "                          values=\"body_mass_g\", aggfunc=\"count\") \n",
    "\n",
    "\n",
    "display(count_by_island(penguins).fillna(0).astype(int))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1289cdf",
   "metadata": {},
   "source": [
    "The table shows that Gentoo penguins occur only on Biscoe and Chinstrap penguins only on Dream.\n",
    "Adelie penguins occur on all three islands.\n",
    "\n",
    "Because island strongly suggests species in this dataset, we should be careful about using\n",
    "`island` to predict `species`.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c58f332a",
   "metadata": {},
   "source": [
    "## Main ideas\n",
    "\n",
    "- `dropna(subset=...)` checks only the columns you name.\n",
    "- `groupby().agg()` creates summaries for each group.\n",
    "- `idxmax()` returns the label of the largest value.\n",
    "- `pivot_table()` displays a two-column grouping as a grid.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34b380ab-6597-4f71-8299-cc70947092d4",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ML Workshop (Python 3.11)",
   "language": "python",
   "name": "mlworkshop"
  },
  "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.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
