{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Plots from the \"Chain of Trust\" paper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ast import literal_eval\n",
    "from pathlib import Path\n",
    "\n",
    "import matplotlib\n",
    "import matplotlib.dates as mdates\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "from matplotlib import lines\n",
    "from notebooks.fixed_sankey_plot import sankey\n",
    "from sklearn import metrics\n",
    "\n",
    "from sec_certs.dataset import CCDataset\n",
    "\n",
    "# LaTeX plotting\n",
    "matplotlib.use(\"pgf\")\n",
    "sns.set_palette(\"Set2\")\n",
    "sns.set_context(\"paper\")\n",
    "\n",
    "plt.rcParams[\"pgf.texsystem\"] = \"pdflatex\"\n",
    "plt.rcParams[\"font.family\"] = \"serif\"\n",
    "plt.rcParams[\"text.usetex\"] = True\n",
    "plt.rcParams[\"pgf.rcfonts\"] = False\n",
    "\n",
    "plt.rcParams[\"axes.linewidth\"] = 0.5\n",
    "plt.rcParams[\"axes.labelsize\"] = 14\n",
    "\n",
    "plt.rcParams[\"xtick.labelsize\"] = 12\n",
    "plt.rcParams[\"xtick.bottom\"] = True\n",
    "plt.rcParams[\"xtick.major.size\"] = 5\n",
    "plt.rcParams[\"xtick.major.width\"] = 0.5\n",
    "plt.rcParams[\"xtick.major.pad\"] = 0.1\n",
    "\n",
    "plt.rcParams[\"ytick.labelsize\"] = 12\n",
    "plt.rcParams[\"ytick.left\"] = True\n",
    "plt.rcParams[\"ytick.major.size\"] = 5\n",
    "plt.rcParams[\"ytick.major.width\"] = 0.5\n",
    "plt.rcParams[\"ytick.major.pad\"] = 0.1\n",
    "\n",
    "plt.rcParams[\"legend.title_fontsize\"] = 10\n",
    "plt.rcParams[\"legend.fontsize\"] = 10\n",
    "plt.rcParams[\"legend.handletextpad\"] = 0.3\n",
    "plt.rcParams[\"lines.markersize\"] = 0.5\n",
    "plt.rcParams[\"savefig.pad_inches\"] = 0.01\n",
    "\n",
    "INPUT_DIR = Path(\"./paper_artifacts/chain_of_trust/data/plots/\")\n",
    "OUTPUT_DIR = Path(\"./results/figures/\")\n",
    "DATASET_PATH = Path(\"./dataset/cc_november_23/dataset.json\")\n",
    "INPUT_DIR.mkdir(exist_ok=True, parents=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ecosystem insights plots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "figure_width = 3.5\n",
    "figure_height = 2.5\n",
    "\n",
    "dset = CCDataset.from_web()\n",
    "df = dset.to_pandas()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Validity boxplot\n",
    "df_validity = pd.read_csv(INPUT_DIR / \"df_validity.csv\")\n",
    "box = sns.boxplot(data=df_validity, x=\"year_from\", y=\"validity_period\", linewidth=0.75, flierprops={\"marker\": \"x\"})\n",
    "box.set(xlabel=\"\", ylabel=\"\")\n",
    "plt.xticks([3 * i for i in range(10)], [3 * i + 1997 for i in range(10)])\n",
    "# rotate ticks by 45 degrees\n",
    "plt.xticks(rotation=45)\n",
    "\n",
    "fig = matplotlib.pyplot.gcf()\n",
    "fig.set_size_inches(figure_width, figure_height)\n",
    "fig.tight_layout(pad=0.1)\n",
    "fig.savefig(OUTPUT_DIR / \"boxplot_validity.pdf\", bbox_inches=\"tight\", dpi=300)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Average EAL level\n",
    "\n",
    "avg_levels = pd.read_csv(INPUT_DIR / \"avg_eal.csv\").loc[lambda _df: _df.year_from < 2025]\n",
    "eal_to_num_mapping = {eal: index for index, eal in enumerate(df[\"eal\"].cat.categories)}\n",
    "avg_levels[\"smartcard_category\"] = avg_levels.category.map(\n",
    "    lambda x: x if x == \"ICs, Smartcards\" else \"Other categories\"\n",
    ")\n",
    "line = sns.lineplot(\n",
    "    data=avg_levels,\n",
    "    x=\"year_from\",\n",
    "    y=\"eal_number\",\n",
    "    hue=\"smartcard_category\",\n",
    "    errorbar=None,\n",
    "    style=\"smartcard_category\",\n",
    "    markers=True,\n",
    "    linewidth=2,\n",
    ")\n",
    "line.set(xlabel=None, ylabel=None, title=None, xlim=(1999.6, 2023))\n",
    "ymin = 1\n",
    "ymax = 9\n",
    "ylabels = [\n",
    "    x if \"+\" in x else x + r\"\\phantom{+}\" for x in list(eal_to_num_mapping.keys())[ymin : ymax + 1]\n",
    "]  # this also aligns the labels by adding phantom spaces\n",
    "plt.yticks(range(ymin, ymax + 1), ylabels)\n",
    "plt.xticks([3 * i + 1997 for i in range(9)] + [2023])\n",
    "plt.xticks(rotation=45)\n",
    "line.legend(title=None, labels=avg_levels.smartcard_category.unique())\n",
    "plt.legend(frameon=False)\n",
    "\n",
    "fig = matplotlib.pyplot.gcf()\n",
    "fig.set_size_inches(figure_width, figure_height)\n",
    "fig.tight_layout(pad=0.1)\n",
    "fig.savefig(OUTPUT_DIR / \"temporal_trends_categories.pdf\", dpi=300)\n",
    "plt.show()\n",
    "plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interesting schemes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "interesting_schemes = pd.read_csv(INPUT_DIR / \"interesting_schemes.csv\")\n",
    "\n",
    "line = sns.lineplot(\n",
    "    data=interesting_schemes,\n",
    "    x=\"year_from\",\n",
    "    y=\"size\",\n",
    "    hue=\"scheme\",\n",
    "    style=\"scheme\",\n",
    "    markers=True,\n",
    "    dashes=True,\n",
    "    linewidth=2,\n",
    ")\n",
    "line.set(xlabel=None, ylabel=None, title=None, xlim=(1999.6, 2023), ylim=(0, 100))\n",
    "plt.xticks([3 * i + 1997 for i in range(9)] + [2023])\n",
    "plt.xticks(rotation=45)\n",
    "line.legend(title=None)\n",
    "fig = matplotlib.pyplot.gcf()\n",
    "fig.set_size_inches(figure_width, figure_height)\n",
    "fig.tight_layout(pad=0.1)\n",
    "fig.savefig(OUTPUT_DIR / \"temporal_trends_schemes.pdf\", dpi=300)\n",
    "plt.show()\n",
    "plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Popular categories"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_certs = pd.read_csv(INPUT_DIR / \"popular_categories.csv\").astype({\"year_from\": \"category\"})\n",
    "dct = {\n",
    "    \"ICs, Smart Cards and Smart Card-Related Devices and Systems\": \"ICs and Smart Cards\",\n",
    "    \"Network and Network-Related Devices and Systems\": \"Network-Related Devices\",\n",
    "    \"Other Devices and Systems\": \"Other Devices\",\n",
    "    \"One of 11 other categories\": \"11 Other Categories\",\n",
    "}\n",
    "n_certs.popular_categories = n_certs.popular_categories.map(lambda x: dct.get(x, x))\n",
    "cats = n_certs.popular_categories.unique()\n",
    "years = n_certs.year_from.cat.categories[:-1]\n",
    "data = [n_certs.loc[n_certs.popular_categories == c, \"size\"].tolist()[:-1] for c in cats]\n",
    "\n",
    "plt.stackplot(\n",
    "    years,\n",
    "    data,\n",
    "    labels=cats,\n",
    ")\n",
    "plt.legend(frameon=False, loc=\"upper left\", bbox_to_anchor=(0, 1))\n",
    "plt.xticks([3 * i + 1997 for i in range(9)] + [2023])\n",
    "plt.xticks(rotation=45)\n",
    "plt.xlim(1997, 2023)\n",
    "\n",
    "fig = matplotlib.pyplot.gcf()\n",
    "fig.set_size_inches(figure_width, figure_height)\n",
    "fig.tight_layout(pad=0.1)\n",
    "fig.savefig(OUTPUT_DIR / \"temporal_trends_stackplot.pdf\")\n",
    "plt.show()\n",
    "plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Average number of transitive references over time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_to_plot = pd.read_csv(INPUT_DIR / \"avg_refs_over_time.csv\", parse_dates=[\"date\"])\n",
    "df_to_plot[\"category\"] = df_to_plot[\"category\"].map(lambda x: \"others\" if x == \"others categories\" else x)\n",
    "\n",
    "plt.figure()\n",
    "g = sns.lineplot(data=df_to_plot, x=\"date\", y=\"n_references\", hue=\"category\", errorbar=None)\n",
    "plt.legend(frameon=True, handlelength=2, title=\"Product category\")\n",
    "g.set_xlabel(\"\")\n",
    "g.set_ylabel(\"Avg. \\# transitive refs.\")\n",
    "\n",
    "dtFmt = mdates.DateFormatter(\"%Y\")\n",
    "g.xaxis.set_major_formatter(dtFmt)\n",
    "g.set_xticks(\n",
    "    [\n",
    "        pd.to_datetime(\"1998-01-01\"),\n",
    "        pd.to_datetime(\"2003-01-01\"),\n",
    "        pd.to_datetime(\"2008-01-01\"),\n",
    "        pd.to_datetime(\"2013-01-01\"),\n",
    "        pd.to_datetime(\"2018-01-01\"),\n",
    "        pd.to_datetime(\"2023-01-01\"),\n",
    "    ]\n",
    ")\n",
    "g.figure.set_size_inches(3.9, 3)\n",
    "plt.tight_layout(pad=0.1)\n",
    "g.figure.savefig(OUTPUT_DIR / \"lineplot_avg_refs.pdf\")\n",
    "g.figure.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Average reach over time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_to_plot = pd.read_csv(INPUT_DIR / \"avg_reach_over_time.csv\", parse_dates=[\"date\"])\n",
    "\n",
    "plt.figure()\n",
    "g = sns.lineplot(data=df_to_plot, x=\"date\", y=\"n_references\", hue=\"category\", errorbar=None)\n",
    "plt.legend(frameon=True, handlelength=2, title=\"Product category\")\n",
    "g.set_xlabel(\"\")\n",
    "g.set_ylabel(\"Average certificate reach\")\n",
    "dtFmt = mdates.DateFormatter(\"%Y\")\n",
    "g.xaxis.set_major_formatter(dtFmt)\n",
    "g.set_xticks(\n",
    "    [\n",
    "        pd.to_datetime(\"1998-01-01\"),\n",
    "        pd.to_datetime(\"2003-01-01\"),\n",
    "        pd.to_datetime(\"2008-01-01\"),\n",
    "        pd.to_datetime(\"2013-01-01\"),\n",
    "        pd.to_datetime(\"2018-01-01\"),\n",
    "        pd.to_datetime(\"2023-01-01\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "g.figure.set_size_inches(3.9, 3)\n",
    "plt.tight_layout(pad=0.1)\n",
    "g.figure.savefig(OUTPUT_DIR / \"lineplot_avg_reach.pdf\")\n",
    "g.figure.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Area under curve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(2.8, 1.8))\n",
    "sns.set_palette(\"Set2\")\n",
    "colors = plt.cm.Dark2(np.linspace(0, 1, 8))\n",
    "\n",
    "df_sent = pd.read_csv(INPUT_DIR / \"df_pred_sentence_transformers.csv\")\n",
    "df_tf_idf = pd.read_csv(INPUT_DIR / \"df_pred_tf_idf.csv\")\n",
    "df_baseline = pd.read_csv(INPUT_DIR / \"df_pred_baseline.csv\")\n",
    "\n",
    "fpr, tpr, thresholds = metrics.roc_curve(df_sent.y_true, df_sent.y_pred)\n",
    "auc = metrics.roc_auc_score(df_sent.y_true, df_sent.y_pred)\n",
    "plt.plot(fpr, tpr, label=f\"Sent. trans. (AUC={auc:.2f})\", color=colors[0])\n",
    "\n",
    "fpr, tpr, thresholds = metrics.roc_curve(df_tf_idf.y_true, df_tf_idf.y_pred)\n",
    "auc = metrics.roc_auc_score(df_tf_idf.y_true, df_tf_idf.y_pred)\n",
    "plt.plot(fpr, tpr, label=f\"TF-IDF (AUC={auc:.2f})\", color=colors[1])\n",
    "\n",
    "fpr, tpr, thresholds = metrics.roc_curve(df_baseline.y_true, df_baseline.y_pred)\n",
    "auc = metrics.roc_auc_score(df_baseline.y_true, df_baseline.y_pred)\n",
    "with plt.rc_context({\"legend.fontsize\": 8}):\n",
    "    plt.plot(fpr, tpr, label=f\"Random guess (AUC={auc:.2f})\", color=colors[2])\n",
    "\n",
    "    plt.legend(loc=\"lower right\")\n",
    "    plt.savefig(OUTPUT_DIR / \"roc_auc.pdf\")\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stack-bar plot of annotations in categories"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv(INPUT_DIR / \"ref_categories_stackplot.csv\")\n",
    "\n",
    "ax = df.plot.barh(stacked=True, rot=0, width=0.95)\n",
    "ax.set_ylim(-0.6, 2.6)\n",
    "ax.set_xlabel(\"\\# references\", fontsize=12)\n",
    "ax.set_yticklabels([\"Others\", \"Smartcard-related\", \"Smartcards\"])\n",
    "ax.legend(title=\"Reference context\", loc=\"lower right\", frameon=True)\n",
    "\n",
    "plt.text(0.4, 0.8, df.iloc[2][\"Component reuse\"], transform=ax.transAxes, color=\"white\", fontsize=14)\n",
    "plt.text(0.81, 0.8, df.iloc[2][\"Predecessor\"], transform=ax.transAxes, color=\"white\", fontsize=14)\n",
    "\n",
    "plt.axhline(y=1.21, xmin=0.05, xmax=0.18, color=\"black\", linewidth=0.75)\n",
    "plt.axhline(y=0.9, xmin=0.12, xmax=0.18, color=\"black\", linewidth=0.75)\n",
    "plt.text(0.2, 0.55, df.iloc[1][\"Component reuse\"], transform=ax.transAxes, color=\"black\", fontsize=14)\n",
    "plt.text(0.2, 0.45, df.iloc[1][\"Predecessor\"], transform=ax.transAxes, color=\"black\", fontsize=14)\n",
    "\n",
    "plt.axhline(y=0.17, xmin=0.02, xmax=0.1, color=\"black\", linewidth=0.75)\n",
    "plt.axhline(y=-0.1, xmin=0.05, xmax=0.1, color=\"black\", linewidth=0.75)\n",
    "plt.text(0.12, 0.22, df.iloc[0][\"Component reuse\"], transform=ax.transAxes, color=\"black\", fontsize=14)\n",
    "plt.text(0.12, 0.13, df.iloc[0][\"Predecessor\"], transform=ax.transAxes, color=\"black\", fontsize=14)\n",
    "\n",
    "ax.figure.set_size_inches(4, 3)\n",
    "plt.tight_layout(pad=0.1)\n",
    "plt.savefig(OUTPUT_DIR / \"stacked_barplot.pdf\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Archived certificate half-life"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "\n",
    "df = pd.read_csv(INPUT_DIR / \"archived_half_life.csv\")\n",
    "\n",
    "with plt.rc_context({\"legend.fontsize\": 10, \"legend.title_fontsize\": 10}):\n",
    "    g = sns.ecdfplot(data=df.n_days, complementary=True)\n",
    "\n",
    "    plt.axvline(x=365, color=\"r\", linestyle=\"--\", linewidth=0.75)\n",
    "    vertical_line = lines.Line2D(\n",
    "        [], [], color=\"r\", marker=\"\", linestyle=\"--\", markersize=10, markeredgewidth=1.5, label=\"One year\"\n",
    "    )\n",
    "    plt.legend(handles=[vertical_line])\n",
    "\n",
    "    g.figure.set_size_inches(3, 2)\n",
    "    g.set_xlim(0, 2000)\n",
    "\n",
    "    g.set_xlabel(\"Number of days\")\n",
    "    g.set_ylabel(\"Proportion\")\n",
    "\n",
    "    g.yaxis.set_major_formatter(matplotlib.ticker.PercentFormatter(xmax=1))\n",
    "    g.set_yticks([0, 0.25, 0.5, 0.75, 1])\n",
    "\n",
    "    plt.tight_layout(pad=0.05)\n",
    "    g.figure.savefig(OUTPUT_DIR / \"cdf_half_life.pdf\")\n",
    "    g.figure.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Age of referenced certificate in composite-evaluation products"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "\n",
    "df = pd.read_csv(INPUT_DIR / \"ecdf_archival_data.csv\")\n",
    "df = df.loc[df.scheme.isin({\"FR\", \"DE\", \"NL\"})]\n",
    "\n",
    "with plt.rc_context({\"legend.fontsize\": 10, \"legend.title_fontsize\": 10}):\n",
    "    g = sns.ecdfplot(data=df, x=\"date_diff\", hue=\"scheme\", legend=True)\n",
    "    plt.axvline(x=540, color=\"r\", linestyle=\"--\", linewidth=0.75)\n",
    "\n",
    "    vertical_line = lines.Line2D([], [], color=\"r\", linestyle=\"--\", markersize=10, label=\"18 months\")\n",
    "    unique_hues = df[\"scheme\"].unique()\n",
    "    handles = [\n",
    "        plt.Line2D([], [], color=g.lines[color_idx].get_color(), label=label)\n",
    "        for color_idx, label in enumerate(unique_hues)\n",
    "    ]\n",
    "\n",
    "    handles.append(vertical_line)\n",
    "    labels = list(unique_hues) + [\"18 months\"]\n",
    "\n",
    "    g.legend(handles=handles, labels=labels)\n",
    "\n",
    "    g.figure.set_size_inches(3, 2)\n",
    "    g.yaxis.set_major_formatter(matplotlib.ticker.PercentFormatter(xmax=1))\n",
    "    g.set_yticks([0, 0.25, 0.5, 0.75, 1])\n",
    "    g.set_xlim(0, 2000)\n",
    "    g.set_xlabel(\"Number of days\")\n",
    "    g.set_ylabel(\"Proportion\")\n",
    "    plt.tight_layout(pad=0.05)\n",
    "    g.figure.savefig(OUTPUT_DIR / \"ref_comp_age.pdf\")\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sankey diagram"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv(INPUT_DIR / \"sankey_scheme_data.csv\").assign(refs=lambda df_: df_.refs.map(literal_eval))\n",
    "\n",
    "cert_id_to_scheme_mapping = dict(zip(df.cert_id, df.scheme))\n",
    "exploded = (\n",
    "    df.copy()\n",
    "    .loc[lambda df_: df_.refs.notnull(), [\"scheme\", \"refs\"]]\n",
    "    .explode(\"refs\")\n",
    "    .assign(ref_scheme=lambda df_: df_.refs.map(cert_id_to_scheme_mapping))\n",
    "    .loc[lambda df_: df_.ref_scheme.notnull()]\n",
    ")\n",
    "\n",
    "all_schemes = set(exploded.scheme.unique()) | set(exploded.ref_scheme.unique())\n",
    "colors = list(sns.color_palette(\"hls\", len(all_schemes), as_cmap=False).as_hex())\n",
    "color_dict = dict(zip(all_schemes, colors))\n",
    "\n",
    "plt.figure()\n",
    "figure, axes = plt.subplots(1, 1)\n",
    "figure.set_size_inches(4, 4)\n",
    "figure.set_tight_layout(True)\n",
    "\n",
    "sankey(\n",
    "    exploded.scheme,\n",
    "    exploded.ref_scheme,\n",
    "    colorDict=color_dict,\n",
    "    leftLabels=list(exploded.scheme.unique()),\n",
    "    rightLabels=list(exploded.ref_scheme.unique()),\n",
    "    fontsize=7,\n",
    "    ax=axes,\n",
    ")\n",
    "\n",
    "figure.savefig(str(OUTPUT_DIR / \"scheme_references.pdf\"), bbox_inches=\"tight\")\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "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.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
