{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Instructor Solutions Notebook\n",
        "This notebook contains solution functions and tests for 20 questions covering Python data structures and pandas manipulation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9fddf698",
      "metadata": {},
      "outputs": [],
      "source": [
        "name = \"Perfection\"\n",
        "roll_number = \"007\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Sum of Unique Elements\n",
        "\n",
        "Return the sum of unique elements in a list (each element counted once)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_one(nums: list[int]) -> int:\n",
        "    return sum(set(nums))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_one([1,2,2,3]) == 6\n",
        "assert question_one([]) == 0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Reverse List\n",
        "\n",
        "Return a new list that is the reverse of input list."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_two(xs: list) -> list:\n",
        "    return xs[::-1]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_two([1,2,3]) == [3,2,1]\n",
        "assert question_two([]) == []"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Count Occurrences\n",
        "\n",
        "Return a dict mapping element -> count."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_three(xs: list) -> dict:\n",
        "    d = {}\n",
        "    for x in xs:\n",
        "        d[x] = d.get(x, 0) + 1\n",
        "    return d"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_three([1,2,2,3]) == {1:1,2:2,3:1}\n",
        "assert question_three([]) == {}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Flatten Nested List (one level)\n",
        "\n",
        "Flatten a list containing lists by one level."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_four(nested: list[list]) -> list:\n",
        "    return [item for sub in nested for item in sub]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_four([[1,2],[3,4]]) == [1,2,3,4]\n",
        "assert question_four([]) == []"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Invert Dictionary (values -> list of keys)\n",
        "\n",
        "Invert a dict mapping k->v into v->list_of_k."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_five(d: dict) -> dict:\n",
        "    inv = {}\n",
        "    for k, v in d.items():\n",
        "        inv.setdefault(v, []).append(k)\n",
        "    return inv"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_five({'a':1,'b':2,'c':1}) == {1:['a','c'],2:['b']}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Merge Dicts Summing Values\n",
        "\n",
        "Given two dicts with numeric values, return merged dict summing values for same keys."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_six(a: dict, b: dict) -> dict:\n",
        "    res = a.copy()\n",
        "    for k, v in b.items():\n",
        "        res[k] = res.get(k, 0) + v\n",
        "    return res"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_six({'x':1,'y':2},{'y':3,'z':4}) == {'x':1,'y':5,'z':4}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Top-K Frequent Elements (small k)\n",
        "\n",
        "Return top k frequent elements (order not important)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_seven(nums: list[int], k: int) -> list[int]:\n",
        "    from collections import Counter\n",
        "    c = Counter(nums)\n",
        "    return [x for x,_ in c.most_common(k)]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "out = question_seven([1,1,2,2,2,3],2); assert set(out) == {1,2}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Split Words from Sentence\n",
        "\n",
        "Split a sentence into words (split on whitespace)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_eight(s: str) -> list[str]:\n",
        "    return s.split()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_eight(\"hello world\") == [\"hello\",\"world\"]\n",
        "assert question_eight(\"\") == []"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Join Words with Separator\n",
        "\n",
        "Join a list of words with given separator."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_nine(words: list[str], sep: str = \" \") -> str:\n",
        "    return sep.join(words)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_nine([\"a\",\"b\"],\"-\") == \"a-b\"\n",
        "assert question_nine([]) == "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Longest Word Length\n",
        "\n",
        "Return length of the longest word in sentence."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_ten(sentence: str) -> int:\n",
        "    words = sentence.split()\n",
        "    return max((len(w) for w in words), default=0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_ten(\"I love python\") == 6\n",
        "assert question_ten(\"\") == 0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Set Intersection\n",
        "\n",
        "Return intersection of two lists as a set."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_eleven(a: list, b: list) -> set:\n",
        "    return set(a).intersection(b)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_eleven([1,2,3],[2,3,4]) == {2,3}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Deduplicate Preserve Order\n",
        "\n",
        "Remove duplicates preserving the first occurrence order."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_twelve(xs: list) -> list:\n",
        "    seen = set(); out = []\n",
        "    for x in xs:\n",
        "        if x not in seen:\n",
        "            seen.add(x); out.append(x)\n",
        "    return out"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_twelve([1,2,2,3,1]) == [1,2,3]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Square Each Element (map)\n",
        "\n",
        "Return list of squares."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_thirteen(nums: list[int]) -> list[int]:\n",
        "    return [x*x for x in nums]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_thirteen([1,2,3]) == [1,4,9]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Filter Even Numbers\n",
        "\n",
        "Return only even numbers in original order."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_fourteen(nums: list[int]) -> list[int]:\n",
        "    return [x for x in nums if x%2==0]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "assert question_fourteen([1,2,3,4]) == [2,4]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pandas Series value_counts\n",
        "\n",
        "Given a list, return a pandas Series of value_counts sorted by index."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_fifteen(values: list) -> 'pd.Series':\n",
        "    import pandas as pd\n",
        "    s = pd.Series(values)\n",
        "    return s.value_counts().sort_index()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "s = question_fifteen(['a','b','a'])\n",
        "assert isinstance(s, pd.Series)\n",
        "assert s.loc['a'] == 2 and s.loc['b'] == 1"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pandas: Split 'name' column into first/last\n",
        "\n",
        "Given DataFrame with column 'name' = 'First Last', split into 'first' and 'last' columns."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_sixteen(df: 'pd.DataFrame') -> 'pd.DataFrame':\n",
        "    import pandas as pd\n",
        "    parts = df['name'].str.split(n=1, expand=True)\n",
        "    df = df.copy()\n",
        "    df['first'] = parts[0]\n",
        "    df['last'] = parts[1]\n",
        "    return df"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "df = pd.DataFrame({'name':['John Doe','Alice Smith']})\n",
        "out = question_sixteen(df)\n",
        "assert list(out['first']) == ['John','Alice']\n",
        "assert list(out['last']) == ['Doe','Smith']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pandas: Merge two DataFrames on key 'id'\n",
        "\n",
        "Perform inner merge on 'id' and return merged DataFrame."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_seventeen(a: 'pd.DataFrame', b: 'pd.DataFrame') -> 'pd.DataFrame':\n",
        "    import pandas as pd\n",
        "    return a.merge(b, on='id', how='inner')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "a = pd.DataFrame({'id':[1,2],'x':[10,20]})\n",
        "b = pd.DataFrame({'id':[2,3],'y':[200,300]})\n",
        "out = question_seventeen(a,b)\n",
        "assert list(out['id']) == [2]\n",
        "assert out.iloc[0]['x'] == 20 and out.iloc[0]['y'] == 200"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pandas: Concatenate DataFrames vertically\n",
        "\n",
        "Return concatenated DataFrame with reset index."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_eighteen(dfs: list) -> 'pd.DataFrame':\n",
        "    import pandas as pd\n",
        "    return pd.concat(dfs, ignore_index=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "a = pd.DataFrame({'x':[1]})\n",
        "b = pd.DataFrame({'x':[2]})\n",
        "out = question_eighteen([a,b])\n",
        "assert len(out)==2 and list(out['x'])==[1,2]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pandas: GroupBy Sum\n",
        "\n",
        "Group by 'key' and sum numeric column 'val'. Return DataFrame sorted by key."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_nineteen(df: 'pd.DataFrame') -> 'pd.DataFrame':\n",
        "    import pandas as pd\n",
        "    out = df.groupby('key', as_index=False)['val'].sum()\n",
        "    return out.sort_values('key').reset_index(drop=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "df = pd.DataFrame({'key':['a','b','a'],'val':[1,2,3]})\n",
        "out = question_nineteen(df)\n",
        "assert list(out['val'])==[4,2] and list(out['key'])==['a','b']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pandas: Fill missing values\n",
        "\n",
        "Fill NA in numeric column 'num' with given fill_value and return DataFrame."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def question_twenty(df: 'pd.DataFrame', fill_value=0) -> 'pd.DataFrame':\n",
        "    import pandas as pd\n",
        "    out = df.copy()\n",
        "    out['num'] = out['num'].fillna(fill_value)\n",
        "    return out"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "df = pd.DataFrame({'num':[1,None,3]})\n",
        "out = question_twenty(df, fill_value=9)\n",
        "assert list(out['num'])==[1,9,3]"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
