{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bf0778f4",
   "metadata": {},
   "source": [
    "# 02 · Data Formats and pandas\n",
    "\n",
    "Data arrives as CSV, JSON, spreadsheets, databases or columnar files, and the format you store it in\n",
    "decides how big the files are, how fast they load and what can go wrong. This module covers pandas,\n",
    "the standard tool for tables in Python, and then measures the common formats on the same data.\n",
    "\n",
    "**You will learn**\n",
    "1. The pandas `DataFrame`: create, inspect, select, filter, group\n",
    "2. Reading and writing CSV, JSON, Excel, Parquet, Feather, SQLite, YAML and pickle\n",
    "3. Which format to choose, measured: file size, write and read time, types preserved\n",
    "4. Pitfalls: encodings, dates, types lost in CSV, and why you must never unpickle untrusted data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4ecc48e",
   "metadata": {},
   "source": [
    "## 1. The pandas DataFrame\n",
    "A two-dimensional, labelled table: columns have names and types, rows have an index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7a247380",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:36.748535Z",
     "iopub.status.busy": "2026-10-17T23:21:36.747856Z",
     "iopub.status.idle": "2026-10-17T23:21:37.385195Z",
     "shell.execute_reply": "2026-10-17T23:21:37.383266Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(4, 4)\n",
      "name      object\n",
      "age        int64\n",
      "city      object\n",
      "salary     int64\n",
      "dtype: object\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>name</th>\n",
       "      <th>age</th>\n",
       "      <th>city</th>\n",
       "      <th>salary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Alice</td>\n",
       "      <td>30</td>\n",
       "      <td>Berlin</td>\n",
       "      <td>62000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Bob</td>\n",
       "      <td>25</td>\n",
       "      <td>Paris</td>\n",
       "      <td>48000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Charlie</td>\n",
       "      <td>35</td>\n",
       "      <td>Berlin</td>\n",
       "      <td>75000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      name  age    city  salary\n",
       "0    Alice   30  Berlin   62000\n",
       "1      Bob   25   Paris   48000\n",
       "2  Charlie   35  Berlin   75000"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "\n",
    "df = pd.DataFrame({\n",
    "    \"name\": [\"Alice\", \"Bob\", \"Charlie\", \"Diana\"],\n",
    "    \"age\": [30, 25, 35, 28],\n",
    "    \"city\": [\"Berlin\", \"Paris\", \"Berlin\", \"Madrid\"],\n",
    "    \"salary\": [62_000, 48_000, 75_000, 51_000],\n",
    "})\n",
    "print(df.shape)\n",
    "print(df.dtypes)\n",
    "df.head(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d2a9a3d0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:37.389429Z",
     "iopub.status.busy": "2026-10-17T23:21:37.388949Z",
     "iopub.status.idle": "2026-10-17T23:21:37.412071Z",
     "shell.execute_reply": "2026-10-17T23:21:37.410385Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Alice', 'Bob', 'Charlie', 'Diana']\n",
      "Berlin\n",
      "{'name': 'Bob', 'age': 25, 'city': 'Paris', 'salary': 48000}\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>name</th>\n",
       "      <th>age</th>\n",
       "      <th>city</th>\n",
       "      <th>salary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Alice</td>\n",
       "      <td>30</td>\n",
       "      <td>Berlin</td>\n",
       "      <td>62000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Charlie</td>\n",
       "      <td>35</td>\n",
       "      <td>Berlin</td>\n",
       "      <td>75000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      name  age    city  salary\n",
       "0    Alice   30  Berlin   62000\n",
       "2  Charlie   35  Berlin   75000"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(df[\"name\"].tolist())            # one column (a Series)\n",
    "print(df.loc[0, \"city\"])               # by label\n",
    "print(df.iloc[1].to_dict())            # by position\n",
    "df[(df[\"age\"] > 26) & (df[\"city\"] == \"Berlin\")]   # boolean filtering"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f5d84fd9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:37.416699Z",
     "iopub.status.busy": "2026-10-17T23:21:37.416375Z",
     "iopub.status.idle": "2026-10-17T23:21:37.499988Z",
     "shell.execute_reply": "2026-10-17T23:21:37.497809Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "        people  mean_salary\n",
      "city                       \n",
      "Berlin       2      68500.0\n",
      "Madrid       1      51000.0\n",
      "Paris        1      48000.0\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>age</th>\n",
       "      <th>salary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>4.000000</td>\n",
       "      <td>4.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>29.500000</td>\n",
       "      <td>59000.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>4.203173</td>\n",
       "      <td>12247.448714</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>25.000000</td>\n",
       "      <td>48000.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>27.250000</td>\n",
       "      <td>50250.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>29.000000</td>\n",
       "      <td>56500.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>31.250000</td>\n",
       "      <td>65250.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>35.000000</td>\n",
       "      <td>75000.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "             age        salary\n",
       "count   4.000000      4.000000\n",
       "mean   29.500000  59000.000000\n",
       "std     4.203173  12247.448714\n",
       "min    25.000000  48000.000000\n",
       "25%    27.250000  50250.000000\n",
       "50%    29.000000  56500.000000\n",
       "75%    31.250000  65250.000000\n",
       "max    35.000000  75000.000000"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df[\"senior\"] = df[\"age\"] >= 30                     # new column\n",
    "summary = df.groupby(\"city\").agg(people=(\"name\", \"count\"), mean_salary=(\"salary\", \"mean\"))\n",
    "print(summary)\n",
    "df.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42cc57ce",
   "metadata": {},
   "source": [
    "## 2. A realistic dataset to compare formats\n",
    "200,000 rows of synthetic sensor readings with the column types real data has: timestamps, integers,\n",
    "floats, categories and free text."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "56ae7f25",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:37.505066Z",
     "iopub.status.busy": "2026-10-17T23:21:37.504549Z",
     "iopub.status.idle": "2026-10-17T23:21:37.755373Z",
     "shell.execute_reply": "2026-10-17T23:21:37.753424Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "timestamp      datetime64[ns]\n",
      "sensor_id               int64\n",
      "temperature           float64\n",
      "status               category\n",
      "note                   object\n",
      "dtype: object\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "in memory: 16.6 MB\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "rng = np.random.default_rng(42)\n",
    "n = 200_000\n",
    "data = pd.DataFrame({\n",
    "    \"timestamp\": pd.date_range(\"2025-01-01\", periods=n, freq=\"min\"),\n",
    "    \"sensor_id\": rng.integers(1, 500, n),\n",
    "    \"temperature\": rng.normal(21, 4, n).round(2),\n",
    "    \"status\": rng.choice([\"ok\", \"warning\", \"fault\"], n, p=[0.9, 0.08, 0.02]),\n",
    "    \"note\": rng.choice([\"\", \"manual check\", \"recalibrated\", \"battery low\"], n),\n",
    "})\n",
    "data[\"status\"] = data[\"status\"].astype(\"category\")\n",
    "print(data.dtypes)\n",
    "print(f\"in memory: {data.memory_usage(deep=True).sum() / 1e6:.1f} MB\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86bf3f04",
   "metadata": {},
   "source": [
    "## 3. Write, read and compare\n",
    "Each format is written and read back three times, and we check whether the column types survive the\n",
    "round trip."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "1c8c1fbf",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:37.760430Z",
     "iopub.status.busy": "2026-10-17T23:21:37.760085Z",
     "iopub.status.idle": "2026-10-17T23:21:51.895638Z",
     "shell.execute_reply": "2026-10-17T23:21:51.893771Z"
    }
   },
   "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>size_mb</th>\n",
       "      <th>write_s</th>\n",
       "      <th>read_s</th>\n",
       "      <th>types_kept</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>format</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>CSV.gz</th>\n",
       "      <td>1.556</td>\n",
       "      <td>2.780</td>\n",
       "      <td>0.326</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Parquet</th>\n",
       "      <td>2.306</td>\n",
       "      <td>0.184</td>\n",
       "      <td>0.040</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Feather</th>\n",
       "      <td>4.076</td>\n",
       "      <td>0.050</td>\n",
       "      <td>0.013</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Pickle</th>\n",
       "      <td>7.305</td>\n",
       "      <td>0.044</td>\n",
       "      <td>0.031</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>CSV</th>\n",
       "      <td>8.580</td>\n",
       "      <td>1.127</td>\n",
       "      <td>0.291</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQLite</th>\n",
       "      <td>10.482</td>\n",
       "      <td>1.641</td>\n",
       "      <td>0.712</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>JSON</th>\n",
       "      <td>21.780</td>\n",
       "      <td>0.595</td>\n",
       "      <td>0.975</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         size_mb  write_s  read_s  types_kept\n",
       "format                                       \n",
       "CSV.gz     1.556    2.780   0.326       False\n",
       "Parquet    2.306    0.184   0.040        True\n",
       "Feather    4.076    0.050   0.013        True\n",
       "Pickle     7.305    0.044   0.031        True\n",
       "CSV        8.580    1.127   0.291       False\n",
       "SQLite    10.482    1.641   0.712       False\n",
       "JSON      21.780    0.595   0.975       False"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os, time, tempfile, sqlite3, warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "tmp = tempfile.mkdtemp()\n",
    "\n",
    "def timed(fn, repeat=3):\n",
    "    best = float(\"inf\")\n",
    "    for _ in range(repeat):\n",
    "        t0 = time.perf_counter(); out = fn(); best = min(best, time.perf_counter() - t0)\n",
    "    return best, out\n",
    "\n",
    "formats = {\n",
    "    \"CSV\":     (\"data.csv\",     lambda p: data.to_csv(p, index=False),          lambda p: pd.read_csv(p)),\n",
    "    \"CSV.gz\":  (\"data.csv.gz\",  lambda p: data.to_csv(p, index=False),          lambda p: pd.read_csv(p)),\n",
    "    \"JSON\":    (\"data.json\",    lambda p: data.to_json(p, orient=\"records\", date_format=\"iso\"), lambda p: pd.read_json(p, orient=\"records\")),\n",
    "    \"Parquet\": (\"data.parquet\", lambda p: data.to_parquet(p, index=False),      lambda p: pd.read_parquet(p)),\n",
    "    \"Feather\": (\"data.feather\", lambda p: data.to_feather(p),                   lambda p: pd.read_feather(p)),\n",
    "    \"Pickle\":  (\"data.pkl\",     lambda p: data.to_pickle(p),                    lambda p: pd.read_pickle(p)),\n",
    "    \"SQLite\":  (\"data.db\",      lambda p: data.to_sql(\"readings\", sqlite3.connect(p), index=False, if_exists=\"replace\"),\n",
    "                                lambda p: pd.read_sql(\"select * from readings\", sqlite3.connect(p))),\n",
    "}\n",
    "rows = []\n",
    "for name, (fname, write, read) in formats.items():\n",
    "    path = os.path.join(tmp, fname)\n",
    "    t_write, _ = timed(lambda: write(path), repeat=1)\n",
    "    t_read, back = timed(lambda: read(path))\n",
    "    same_types = list(back.dtypes.astype(str)) == list(data.dtypes.astype(str))\n",
    "    rows.append(dict(format=name, size_mb=os.path.getsize(path) / 1e6, write_s=t_write, read_s=t_read, types_kept=same_types))\n",
    "\n",
    "results = pd.DataFrame(rows).set_index(\"format\").round(3).sort_values(\"size_mb\")\n",
    "results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "861fd0ad",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:51.900621Z",
     "iopub.status.busy": "2026-10-17T23:21:51.900062Z",
     "iopub.status.idle": "2026-10-17T23:21:53.074516Z",
     "shell.execute_reply": "2026-10-17T23:21:53.072742Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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cAHNmzeHm5ubtJ68vjKMiIioJGjfvj2OHDmCL774AitXrkT9+vXx8uVLPHnyBP3794empiZkMhmeP3+OmjVrwsrKCubm5ggMDESZMmVyzdU9e/aElZUVunTpIrW/ePEChw4dwqJFiwAA3377LU6fPo1atWqhbt26uHfvHho0aICYmBgAwN27d7F37140btxY4ZGzvL4yjIiIqLAU2/d0Z8vMzIS/vz+eP3+O8uXLo1atWjluC09OTkZQUBASEhJQsWJFpa8uye536tQp1KhRA3Xr1pXaQ0NDcePGDXTr1g3a2toAgPT0dPj4+EBTUxP16tXDhAkTIJPJsG3bNkRHR+PSpUs51q+vr4+OHTt+dJ/4nm4iIlLmc3tPdzYhBAIDAxEeHg5zc3PUqlULhoaK+S0lJQV37txBdHQ0rKysUKdOHaVXnzMzM3H06FFUrFhR4QR5VFQULl++jA4dOsDAwADA29eWXblyBenp6ahXrx5++eUX3L59G6dOnUJSUhLOnDmTY/2ampoKA7Dlhu/pLp34nm4i+piC5OpiX3SrQ0REBKKiotCwYUMAwJ07d9C0aVOsXbsWQ4cO/eT1s+gmIiJlPteiWx2SkpJw48YNuLi4AADCwsLQpEkTTJ8+HdOmTfvk9bPoLp1YdBPRxxQkVxfb28vVqWzZshg/fjyysrKgq6sLX19fjBw5Eu7u7uoOjYiIiPB24LN58+YhNjYWJiYmuH79Orp3744vvvhC3aEREREpYNGthLGxMXx8fBAYGIjo6GjUqlULFStWVHdYRERE9P+VKVMGZ86cwd27dxEREYEaNWqgSpUq6g6LiIgoBxbdudDQ0EC9evXUHQYRERF9QO3atVG7dm11h0FERJQrFt1qtONABT6zR0REVIyFDyzHXE1ERJ+EL60kIiIiIiIiUhEW3UREREREREQqwqKbiIiIiIiISEVYdBMRERERERGpCItuIiIiIiIiIhVh0U1ERERERESkIiy6iYiIiIiIiFSERTcRERERERGRirDoJiIiIiIiIlIRFt1EREREREREKsKim4iIiIiIiEhFWHQTERERERERqQiLbiIiIiIiIiIVYdFNREREREREpCIsuomIiIiIiIhUhEU3ERERERERkYqw6CYiIiIiIiJSERbdRERERERERCqipe4ASrPBfSKhrfVK3WHkcPCklbpDICIiKhasd0dDpp+S7+US3C1UEA0REX2OeKWbiIiIiIiISEVYdBMRERERERGpCItuIiIiIiIiIhVR6zPd69evh7e3NwBAW1sbNjY26Nu3L+rWravOsPJl+/btiIuLw5QpU9QdChERUaHbs2cPjh07BgDQ1NSEtbU1unXrBkdHRzVHlndHjhzBrVu38MMPP6g7FCIiKoXUeqXb29sbV65cQZs2beDo6Ijg4GDUr18fe/fuVWdY+XLlyhWcPXtW3WEQERGphJ+fH06ePIk2bdqgefPmiIqKQvPmzfHXX3+pO7Q8u337tnTigIiIqKipffRyS0tLjBgxAgAwZswYJCYmYv78+WjYsCF+/vlnAICuri5sbW0xcuRImJqaSsv6+Phg586dmDp1KjZs2ICIiAj8+uuvMDc3h7+/P7Zs2QINDQ20adMGqampePToEaZPnw4AWL58OUxNTTF06FBpfXv27MGTJ0+kPgAQExODzZs3Izg4GNbW1hg8eDCqV68OADh48CDOnDmD5ORkaR+++eYbODg4qPKQERERFSm5XC7ludGjR0MIgXnz5mHgwIGYNm0aAKBMmTKoVq0aPDw8YGX1f2/BuHPnDhYvXowlS5ZgzZo1ePr0KWbOnInq1asjODgY69evR1paGpydnWFiYoLz589j/vz5AIAtW7bg1atXmDRpkrS+//77DxcvXpT6AEBSUhI2b96MoKAgWFpaol+/flIuPnfuHPbv34+wsDBpHzw9PdGyZUtVHjIiIiJJsXumu3bt2ggPD4eRkRHatGmDNm3aoH79+vDy8kKdOnUQGxsr9X306BH+/vtvtG/fHlpaWmjVqhV0dXVx9epVODo6IiYmBtbW1pg7dy4mTZqEkydPSsuePXsWV65cUdh29tn8bA8ePEC9evVw+/Zt1KtXD1FRUWjcuDEuXboEAKhSpQoqVqwIc3NzKdZ3TwoQERGVRLVr10ZUVBQ0NTWl/Ne4cWMEBgaidu3aCAkJkfo+e/YMW7ZsQfPmzZGSkoIWLVrAyMgIDx48QOPGjfHw4UNUqVIFf/31F4YNG4aDBw9Ky/r4+ODcuXMK275z545Cn8jISNSvXx/nzp2Dg4MD3rx5g1atWuHQoUMAACsrK1StWlXhe4WlpaVKjw8REdG71H6l+10ZGRk4ffo0HBwcUK5cOemMNABMmDABrVu3xpo1azBr1iypPSUlBRs2bICLi4vUNmPGDAwaNAibN28G8PYKetWqVfMdzxdffAE3NzcsW7ZMajMxMcF3330Hb29vNGjQALVq1UJ4eLhCrO9LTU1FamqqNJ2YmJjvWIiIiIoDIQROnjyJmjVrwsTERCH/jRs3DgMGDMCyZcuwYsUKheXmz58PNzc3aXrKlClwdnbGvn37AAATJ04s0JguM2bMQLNmzbBjxw6prUqVKvjmm2/Qq1cv1KxZEw0bNkRERARzNRERqYXai+579+5hxIgRyMjIwNWrV/HmzRscPXoUwNuz2Xv37kVERARSU1MRFRWFe/fuKSyvq6uL1q1bS9OZmZm4dOkSpk6dKrXp6+ujW7duCA0NzXNcKSkpOHv2LIQQ8PT0hBACQgiEhobi9u3b+drHhQsXYu7cuflahoiIqLiIjIzEiBEjkJmZiVu3buH58+fYs2cPAODx48fYsWMHQkNDkZKSguDgYLx8+TLHOjp16qQwfeHCBYXcqKWlhb59++Lw4cP5iu3YsWOoXbu2Qq6Ojo7Go0eP8OrVKxgYGORpPczVRESkKmovuo2NjdGmTRtoa2tjzJgxaNasGXR1dXHs2DH069cPo0aNgoODAwwMDBAaGoqkpCSF5eVyOWQymTT98uVLpKen57jN29TUNF9Fd0JCAjIyMtCwYUPUrl1bYd67z4HnxcyZMxVOAiQmJsLGxiZf6yAiIlIXfX19tGnTBpqamhgxYgSaNWsGAwMDaTDUQYMGoVGjRjAyMkJCQgIiIyNzrMPY2FhhOiYmRmmuzq+4uDjUqVMHTk5OCu39+vWDpqZmntfDXE1ERKqi9qL73YHU3vX3339j3Lhx+PPPP6W2vIxqbmZmhrJlyyI0NBQtWrSQ2p8+farQT0dHB2lpaQpt8fHx0r9NTU2hp6cHa2vrD96O9m7BnxsdHR3o6Oh8tB8REVFx9O5Aau/atGkTevXqJT3OBbx9M4myovt9NjY2OU6G5zdXA0DFihVhZmbGXE1ERMVWsRtILZtMJlNIrNevX1cY5OxD+vTpg1WrVkmJ+sGDB9It69lsbW3h4+ODzMxMAEB0dLTCLW3a2toYPHgwli5dimfPnkntSUlJOHLkiDRtamqKuLi4/O8gERHRZ04mkyncSv7w4UPs2rUrT8v27dsX69evx6tXrwC8vYX93eeygbe52s/PD2/evAEAvHr1Ksf6PTw88Ndff+Hhw4dSW2pqKvbv3y9NM1cTEZE6Fduie+rUqdi3bx/atm2LHj16oEuXLrC3t8/TsgsWLEBERAQcHBzQp08ftG/fHvXq1VPoM3HiRLx8+RINGzZE//790bx5c1SuXFmhz7Jly1CnTh3Y29ujR48e6NSpExwcHPD8+XOpT48ePXD9+nX06NEDI0aMQEBAwKfvPBER0Wdg8uTJuHbtGpycnNC7d284OzujZs2aeVr2+++/h6amJurWrYu+ffvC0dExx+Ncw4cPh6GhIerXr48BAwagYcOGsLCwUOgzZ84cdOnSBQ0aNECXLl3QrVs32NnZ4cGDB1Kfzp07IyIiAh07dsSIESPg7e396TtPRESUR2q9vdzT01M6w/2+1q1bIzg4GJcvX4ampiY2bdqER48eITk5WerTvHlzhdvPs9nY2CAoKAinT5+GpqYm1qxZgxUrVii8IszKygpBQUHw8vKChoYGHB0d8eTJE4VXkhkZGeH48eMICAhAUFAQzM3N0bRpU8jlcqlPkyZN8ODBA1y/fh2JiYl8ZRgREZUoAwYMQLNmzZTOq1u3Lh4+fAgvLy9kZWXhr7/+QkxMDMLCwqQ+derUwaZNm3Isa2JiAl9fX5w5cwZpaWlYvnw5Dh48iDVr1kh9DA0NcfPmTVy8eBFpaWlYsWIF4uLiFK5qlylTBv/++y8ePnyImzdvwtDQEE2bNoW5ubnUp2rVqggJCYGPjw8SEhL4yjAiIipSMiGEUHcQRWH27Nm4cuUKTp8+re5QkJiYCLlcjq7t7kFby1Dd4eRw8KSVukMgIiqVsvNDQkICjIyM1B1OkVu5ciXWrFmDwMBAdYci/V8YrguGTD//uTrB3eLjnYiI6LNTkFxdbG8vJyIiIiIiIvrcqX308qLSr18/hfd5ExERUfHSoUMHvqaLiIhKnFJTdDds2FDdIRAREdEH1KpVC7Vq1VJ3GERERIWq1BTdxdGOAxVK5TN7REREn4vwgeWYq4mI6JPwmW4iIiIiIiIiFWHRTURERERERKQiLLqJiIiIiIiIVIRFNxEREREREZGKsOgmIiIiIiIiUhEW3UREREREREQqwqKbiIiIiIiISEVYdBMRERERERGpCItuIiIiIiIiIhVh0U1ERERERESkIiy6iYiIiIiIiFSERTcRERERERGRirDoJiIiIiIiIlIRFt1EREREREREKsKim4iIiIiIiEhFWHQTERERERERqQiLbiIiIiIiIiIVYdFNREREREREpCJa6g6gNBvcJxLaWq/UHUaxcvCklbpDICIikljvjoZMP0WaTnC3UGM0RET0OeKVbiIiIiIiIiIVYdFNREREREREpCKlsuj28/PD1atX1R0GERER5SIoKAgXLlxQdxhERESfTK3PdHt5eeHFixc52rt27Qp9ff1C2catW7fw5s0bODs7S22rV6/Gq1evsHPnzkLZBhERUUl1/fp1PH36NEd7u3btYGpqWijbuHfvHiIjI9G2bVupbceOHfD29sb58+cLZRtERETqotaie+7cuXj48CGaNm2q0O7i4lJoRff69esRHh6OgwcPFsr6iIiISpM///wTp06dQqtWrRTa69evX2hF9969e3H06FFcuXKlUNZHRERUnKh99PJWrVph+/btuc6/d+8egoODYW1tDQcHB2hqakrz7t+/j4CAAACAXC5HnTp1YGX1f6Nf37lzB48ePUJsbCz27t0rbS9bSkoK/P39kZSUhCZNmkAul+dr+35+fsjIyICDgwOuXLmCtLQ0dO7cueAHg4iIqBhycHCQ8qgyISEhuHPnDiwtLVG/fn1oa2tL854+fYrr168DAAwMDGBvb49KlSopLBsUFIS4uDhpG++ejE9PT0dAQABiYmLQqFEjmJub52v7QUFBiImJgZOTE65evYq4uDj07t27wMeCiIgov9RedOfm9evXcHd3x7Vr19CgQQM8fPgQJiYmOHToECpUqAAAuHv3rnSLeHx8PK5cuYI5c+bg22+/BQAEBgbi4cOHePPmjdSvRo0aAN4W7I0aNYKVlRVevHiB6OhonDt3DrVr187z9levXg0/Pz+8efMGFStWRM2aNVl0ExFRqZGRkYExY8bg6NGjaNKkCcLCwpCVlYVDhw7B1tYWAPD48WMpBycmJsLHxwfjxo3D0qVLAQCPHj1CQEAAYmNjpX7ZhfWzZ8/g6OgIY2NjvHr1Cg8fPsT//vc/6ZGxvGx/x44d2L17N/T09GBsbIyKFSuy6CYioiKl9qI7NDRU4ex5xYoV4ezsjO+++w7JyckICQmBrq4uMjMzMWjQIHzzzTf4999/AQC9e/dWSJw3b96Es7Mz+vTpA1tbWwwcOBAXL15EeHh4jjP0d+/exbVr11CvXj0IIdCxY0f8+uuv2Lx5MwDkafsA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",
      "text/plain": [
       "<Figure size 1000x340 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "fig, ax = plt.subplots(1, 2, figsize=(10, 3.4))\n",
    "results[\"size_mb\"].plot.barh(ax=ax[0], color=\"#4f46e5\"); ax[0].set_xlabel(\"file size (MB)\")\n",
    "results[\"read_s\"].plot.barh(ax=ax[1], color=\"#0ea5e9\"); ax[1].set_xlabel(\"read time (s, best of 3)\")\n",
    "for a in ax: a.invert_yaxis()\n",
    "plt.tight_layout(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d8006c3",
   "metadata": {},
   "source": [
    "**Reading the result.** Columnar binary formats (Parquet, Feather) are the smallest and fastest *and*\n",
    "they keep the column types: timestamps stay timestamps and categories stay categories. CSV and JSON lose\n",
    "types, so `timestamp` comes back as text and `status` as plain strings unless you tell the reader what\n",
    "to expect:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "1393de44",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:53.078615Z",
     "iopub.status.busy": "2026-10-17T23:21:53.078142Z",
     "iopub.status.idle": "2026-10-17T23:21:53.476101Z",
     "shell.execute_reply": "2026-10-17T23:21:53.474281Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "timestamp      datetime64[ns]\n",
      "sensor_id               int64\n",
      "temperature           float64\n",
      "status               category\n",
      "note                   object\n",
      "dtype: object\n"
     ]
    }
   ],
   "source": [
    "back = pd.read_csv(os.path.join(tmp, \"data.csv\"),\n",
    "                   parse_dates=[\"timestamp\"], dtype={\"status\": \"category\"}, keep_default_na=False)\n",
    "print(back.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ff4b028",
   "metadata": {},
   "source": [
    "## 4. Small configuration and exchange formats\n",
    "Not everything is a table. Configuration lives in YAML, TOML or JSON; APIs exchange JSON."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fd55fa3d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:53.479972Z",
     "iopub.status.busy": "2026-10-17T23:21:53.479597Z",
     "iopub.status.idle": "2026-10-17T23:21:53.527379Z",
     "shell.execute_reply": "2026-10-17T23:21:53.525847Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "model:\n",
      "  name: baseline\n",
      "  layers:\n",
      "  - 64\n",
      "  - 32\n",
      "train:\n",
      "  epochs: 10\n",
      "  lr: 0.001\n",
      "\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "import json, yaml, tomllib\n",
    "\n",
    "config = {\"model\": {\"name\": \"baseline\", \"layers\": [64, 32]}, \"train\": {\"epochs\": 10, \"lr\": 0.001}}\n",
    "as_json = json.dumps(config, indent=2)\n",
    "as_yaml = yaml.safe_dump(config, sort_keys=False)\n",
    "print(as_yaml)\n",
    "assert json.loads(as_json) == yaml.safe_load(as_yaml) == config\n",
    "\n",
    "toml_text = '[model]\\nname = \"baseline\"\\nlayers = [64, 32]\\n\\n[train]\\nepochs = 10\\nlr = 0.001\\n'\n",
    "print(tomllib.loads(toml_text) == config)     # TOML: what pyproject.toml uses (module 07)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "996cde63",
   "metadata": {},
   "source": [
    "## 5. Excel\n",
    "Common in business settings. Slow and type-fragile compared with Parquet, but sometimes the format\n",
    "the reader needs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "0b8a0195",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:53.532695Z",
     "iopub.status.busy": "2026-10-17T23:21:53.532238Z",
     "iopub.status.idle": "2026-10-17T23:21:53.811788Z",
     "shell.execute_reply": "2026-10-17T23:21:53.810090Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    status   count   mean    max\n",
      "0    fault    4045  20.84  37.87\n",
      "1       ok  180055  21.01  40.97\n",
      "2  warning   15900  21.01  35.84\n"
     ]
    }
   ],
   "source": [
    "path = os.path.join(tmp, \"summary.xlsx\")\n",
    "summary_by_status = data.groupby(\"status\", observed=True)[\"temperature\"].agg([\"count\", \"mean\", \"max\"]).round(2)\n",
    "with pd.ExcelWriter(path) as xl:\n",
    "    summary_by_status.to_excel(xl, sheet_name=\"by_status\")\n",
    "    data.head(100).to_excel(xl, sheet_name=\"sample\", index=False)\n",
    "print(pd.read_excel(path, sheet_name=\"by_status\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd347a46",
   "metadata": {},
   "source": [
    "## 6. Pitfalls worth knowing\n",
    "- **Encodings.** Files from other systems may not be UTF-8: `pd.read_csv(path, encoding=\"latin-1\")`.\n",
    "- **Decimal and thousands separators.** European CSVs often use `;` and `,`: `pd.read_csv(path, sep=\";\", decimal=\",\")`.\n",
    "- **Dates.** Ambiguous `01/02/2025` means different days in different countries; prefer ISO 8601 (`2025-02-01`).\n",
    "- **Pickle executes code when loaded.** It is fine for your own cached objects, but never load a pickle\n",
    "  from an untrusted source. The demonstration below runs a command just by being unpickled:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "ea1fb357",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-17T23:21:53.816125Z",
     "iopub.status.busy": "2026-10-17T23:21:53.815699Z",
     "iopub.status.idle": "2026-10-17T23:21:53.823283Z",
     "shell.execute_reply": "2026-10-17T23:21:53.821503Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "this ran during pickle.loads: it could have been any command\n"
     ]
    }
   ],
   "source": [
    "import pickle\n",
    "\n",
    "class Malicious:\n",
    "    def __reduce__(self):                      # tells pickle how to \"rebuild\" the object\n",
    "        return (print, (\"this ran during pickle.loads: it could have been any command\",))\n",
    "\n",
    "payload = pickle.dumps(Malicious())\n",
    "pickle.loads(payload)                           # prints: loading executed code"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f542e7d4",
   "metadata": {},
   "source": [
    "## Which format when?\n",
    "\n",
    "| Need | Use |\n",
    "|---|---|\n",
    "| Analysis data, anything above a few MB | **Parquet** (compressed, typed, columnar, readable by every tool) |\n",
    "| Fast temporary exchange between Python and R | Feather / Arrow IPC |\n",
    "| Human-readable, small, universal | CSV (with explicit types on read) |\n",
    "| Nested data, web APIs | JSON |\n",
    "| Configuration | YAML or TOML |\n",
    "| Many writers, queries, transactions | a database (SQLite locally, PostgreSQL on a server) |\n",
    "| People who live in spreadsheets | Excel |\n",
    "| Caching your own Python objects | pickle (trusted data only) |\n",
    "\n",
    "## Exercises\n",
    "1. Add a `float32` column and check which formats keep `float32` after the round trip.\n",
    "2. Write the readings to Parquet partitioned by `status` (`partition_cols=[\"status\"]`) and read back only the `fault` rows.\n",
    "3. Load the CSV with `usecols=[\"timestamp\", \"temperature\"]` and compare the time with loading everything."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
