{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  },
  "colab": {
   "provenance": []
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-0000",
   "metadata": {},
   "source": [
    "# Conditionals & Booleans\n",
    "\n",
    "Teach your code to make decisions — comparison operators, and/or/not, if/elif/else chains, truthiness, and the pitfalls that bite beginners.\n",
    "\n",
    "*Part of the free [Python for Data Science](https://ramadnsyh.dev/courses/python-for-data-science) course by [Muhammad Ramadiansyah](https://ramadnsyh.dev). This notebook is generated from the interactive lesson — [read it online](https://ramadnsyh.dev/courses/python-for-data-science/conditionals).*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0001",
   "metadata": {},
   "source": [
    "Real programs make decisions: flag an outlier, grade a score, route a\n",
    "customer. Everything hinges on questions that evaluate to `True` or `False`.\n",
    "In this lesson you'll master comparisons and boolean logic, then use\n",
    "`if`/`elif`/`else` to branch your code — plus the handful of pitfalls that\n",
    "account for most beginner bugs."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0002",
   "metadata": {},
   "source": [
    "## Comparison operators produce booleans\n",
    "\n",
    "You met these briefly in the data-types lesson. Every comparison evaluates\n",
    "to a `bool`, and you can store that result in a well-named variable:"
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0003",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "score = 70\n",
    "\n",
    "print(score >= 85)    # greater than or equal\n",
    "print(score < 85)     # less than\n",
    "print(score == 70)    # equal - note the DOUBLE equals\n",
    "print(score != 70)    # not equal\n",
    "\n",
    "good_score = score >= 85\n",
    "print(good_score, type(good_score))\n",
    "\n",
    "# \"in\" checks membership in a collection\n",
    "animals = [\"cow\", \"fish\", \"cat\"]\n",
    "print(\"cow\" in animals)\n",
    "print(\"dog\" in animals)\n",
    "print(\"dog\" not in animals)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0004",
   "metadata": {},
   "source": [
    "> **= assigns, == compares**\n",
    "> \n",
    "> The single biggest beginner mistake: score = 85 STORES 85 into score, while\n",
    "> score == 85 ASKS whether score equals 85. Python will refuse to run an\n",
    "> assignment where a condition belongs (a SyntaxError inside an if), but typing\n",
    "> == where you meant = fails silently - the comparison result is just thrown\n",
    "> away. When a variable mysteriously never changes, check for this."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0005",
   "metadata": {},
   "source": [
    "## Combining conditions: and, or, not\n",
    "\n",
    "Real rules usually involve several conditions at once. Python combines\n",
    "booleans with plain English words:\n",
    "\n",
    "- `a and b` — `True` only if **both** are true\n",
    "- `a or b` — `True` if **at least one** is true\n",
    "- `not a` — flips the value"
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0006",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "score = 70\n",
    "\n",
    "average_score = score >= 60 and score < 85\n",
    "print(average_score)\n",
    "\n",
    "failed_or_flagged = score < 50 or score > 100\n",
    "print(failed_or_flagged)\n",
    "\n",
    "print(not average_score)\n",
    "\n",
    "# Python bonus: comparisons can be CHAINED\n",
    "print(60 <= score < 85)    # same as: score >= 60 and score < 85"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0007",
   "metadata": {},
   "source": [
    "That last line is a lovely Python idiom — `60 <= score < 85` reads exactly\n",
    "like the math notation and replaces an explicit `and`. Use it whenever\n",
    "you're checking that a value falls inside a range."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0008",
   "metadata": {},
   "source": [
    "## if / else: your first branch\n",
    "\n",
    "An `if` statement runs its indented block only when the condition is\n",
    "`True`; the optional `else` block runs otherwise. Indentation isn't\n",
    "decoration in Python — it *is* the block structure:"
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0009",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "score = 70\n",
    "\n",
    "if score >= 85:\n",
    "    print(\"Good score\")\n",
    "else:\n",
    "    print(\"Not a good score\")\n",
    "\n",
    "# The condition can be any expression that evaluates to a bool\n",
    "threshold = 60\n",
    "if score >= threshold:\n",
    "    print(f\"{score} passes the threshold of {threshold}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0010",
   "metadata": {},
   "source": [
    "## elif: many branches, first match wins\n",
    "\n",
    "When there are more than two outcomes, chain conditions with `elif` (\"else\n",
    "if\"). Python checks each condition **top to bottom** and runs only the\n",
    "*first* block whose condition is true — the rest are skipped:"
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0011",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "def grade(score):\n",
    "    if score >= 85:\n",
    "        return \"Good score\"\n",
    "    elif score >= 60:\n",
    "        return \"Average score\"\n",
    "    elif score >= 50:\n",
    "        return \"Bad score\"\n",
    "    else:\n",
    "        return \"Not passed\"\n",
    "\n",
    "for s in [95, 70, 55, 30]:\n",
    "    print(s, \"->\", grade(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0012",
   "metadata": {},
   "source": [
    "Notice the second branch is just `score >= 60`, not\n",
    "`score >= 60 and score < 85`. It doesn't need the upper bound: if the score\n",
    "were 85 or more, the *first* branch would already have caught it. Ordering\n",
    "your conditions from strictest to loosest keeps each one simple — and\n",
    "getting that order wrong is a classic logic bug (put `score >= 50` first and\n",
    "everything above 50 lands there)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0013",
   "metadata": {},
   "source": [
    "## Nested conditions\n",
    "\n",
    "An `if` can live inside another `if` — just indent one level deeper. Use\n",
    "nesting when a second decision only makes sense after the first:"
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0014",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "def ticket_price(age, is_member):\n",
    "    if age < 17:\n",
    "        return 25000        # kids pay one flat price\n",
    "    else:\n",
    "        if is_member:       # adults: membership matters\n",
    "            return 40000\n",
    "        else:\n",
    "            return 50000\n",
    "\n",
    "print(ticket_price(12, False))\n",
    "print(ticket_price(30, True))\n",
    "print(ticket_price(30, False))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0015",
   "metadata": {},
   "source": [
    "Nesting more than two levels deep gets hard to read. Often you can flatten\n",
    "it with `and` — `if age >= 17 and is_member:` — or by returning early. Prefer\n",
    "whichever version you can read aloud without stumbling."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0016",
   "metadata": {},
   "source": [
    "## Truthiness: non-booleans in conditions\n",
    "\n",
    "Python lets *any* value stand in a condition. Empty things — `0`, `\"\"`,\n",
    "`[]`, `{}`, `None` — count as `False`; everything else counts as `True`.\n",
    "This is called **truthiness**, and it makes \"is there anything here?\" checks\n",
    "very concise:"
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0017",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "print(bool(0), bool(42))\n",
    "print(bool(\"\"), bool(\"hi\"))\n",
    "print(bool([]), bool([1, 2]))\n",
    "print(bool(None))\n",
    "\n",
    "results = []\n",
    "if results:\n",
    "    print(\"Found\", len(results), \"results\")\n",
    "else:\n",
    "    print(\"No results - nothing to analyze\")\n",
    "\n",
    "name = \"Rama\"\n",
    "if name:                       # idiomatic \"is name non-empty?\"\n",
    "    print(f\"Hello, {name}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0018",
   "metadata": {},
   "source": [
    "One caution: truthiness can't distinguish \"missing\" from \"legitimately\n",
    "zero\". If `0` is a valid value in your data, test explicitly with\n",
    "`value is None` instead of `if value:`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0019",
   "metadata": {},
   "source": [
    "## Ternary expressions: if in one line\n",
    "\n",
    "When each branch just picks a value, Python's **conditional expression**\n",
    "squeezes the whole decision into one line:\n",
    "`value_if_true if condition else value_if_false`."
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0020",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "score = 70\n",
    "\n",
    "status = \"pass\" if score >= 60 else \"fail\"\n",
    "print(status)\n",
    "\n",
    "# Great for labeling values inside f-strings and list operations\n",
    "ages = [15, 22, 17, 34]\n",
    "labels = [\"adult\" if age >= 18 else \"minor\" for age in ages]\n",
    "print(labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0021",
   "metadata": {},
   "source": [
    "Keep ternaries for genuinely simple picks. The moment you're tempted to nest\n",
    "one inside another, switch back to a full `if`/`elif` block."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0022",
   "metadata": {},
   "source": [
    "### 🏋️ Exercise — A shipping-cost calculator\n",
    "\n",
    "1.\",\n",
    "    \"Apply the member discount after picking the base cost: cost = cost * 0.9 if is_member else cost.\",\n",
    "  ]}\n",
    "  solution={`\n",
    "def shipping_cost(weight, is_member=False):\n",
    "    if weight <= 0:\n",
    "        return None                # invalid input\n",
    "    elif weight <= 1:\n",
    "        cost = 10000\n",
    "    elif weight <= 5:\n",
    "        cost = 25000\n",
    "    else:\n",
    "        cost = 25000 + (weight - 5) * 4000\n",
    "\n",
    "    return cost * 0.9 if is_member else cost\n",
    "\n",
    "print(shipping_cost(0.5))          # 10000\n",
    "print(shipping_cost(3))            # 25000\n",
    "print(shipping_cost(8))            # 37000.0 area: 25000 + 3*4000\n",
    "print(shipping_cost(8, True))      # member discount applied\n",
    "print(shipping_cost(-2))           # None\n",
    "`}\n",
    ">\n",
    "Write `shipping_cost(weight, is_member=False)` for a delivery service.\n",
    "Rules: a weight of 0 or less is invalid — return `None`; up to 1 kg costs\n",
    "10000; up to 5 kg costs 25000; above 5 kg costs 25000 plus 4000 for every kg\n",
    "over 5. Members always get 10% off the final price (use a ternary for the\n",
    "discount). Test it with weights 0.5, 3, 8, 8 with membership, and -2."
   ]
  },
  {
   "cell_type": "code",
   "id": "cell-0023",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Your solution here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0024",
   "metadata": {},
   "source": [
    "<details><summary>✅ Show solution</summary>\n",
    "\n",
    "```python\n",
    "def shipping_cost(weight, is_member=False):\n",
    "    if weight <= 0:\n",
    "        return None                # invalid input\n",
    "    elif weight <= 1:\n",
    "        cost = 10000\n",
    "    elif weight <= 5:\n",
    "        cost = 25000\n",
    "    else:\n",
    "        cost = 25000 + (weight - 5) * 4000\n",
    "\n",
    "    return cost * 0.9 if is_member else cost\n",
    "\n",
    "print(shipping_cost(0.5))          # 10000\n",
    "print(shipping_cost(3))            # 25000\n",
    "print(shipping_cost(8))            # 37000.0 area: 25000 + 3*4000\n",
    "print(shipping_cost(8, True))      # member discount applied\n",
    "print(shipping_cost(-2))           # None\n",
    "```\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-0025",
   "metadata": {},
   "source": [
    "Next up: loops — running the same logic over every item in your data,\n",
    "automatically."
   ]
  }
 ]
}