Files
labs/Lab-5/Lab-5-Practical/01_Malware_Dataset_Audit_Lab.ipynb
T

223 lines
6.6 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "cell-001",
"metadata": {},
"source": [
"# Module 5 — Lab 1: Auditing a Malware Feature Dataset\n",
"\n",
"**Topics:** provenance, schema, imbalance, missing values, leakage\n",
"\n",
"> **Lab version:** Complete every `TODO`, run the cells in order, and use the self-check before opening the Solution."
]
},
{
"cell_type": "markdown",
"id": "cell-002",
"metadata": {},
"source": [
"## Learning objectives\n",
"\n",
"After completing this lab, you will be able to:\n",
"\n",
"- load a prepared table of static and dynamic malware features safely;\n",
"- distinguish identifiers, provenance fields, features, and the binary label;\n",
"- audit class imbalance, source composition, missing values, infinite values, and duplicates;\n",
"- identify columns that must not enter a predictive model;\n",
"- document dataset limitations before modeling.\n",
"\n",
"> **Safety boundary:** This practical uses numeric feature tables only. Do not download, open, or execute malware samples."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-003",
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"\n",
"DATA_PATH = Path(\"data/malware_features.csv\")\n",
"\n",
"if not DATA_PATH.exists():\n",
" raise FileNotFoundError(\n",
" \"Missing data/malware_features.csv. Follow data/README.md, place the three \"\n",
" \"official UCI CSV files under data/raw/, and run: python data/prepare_dataset.py\"\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "cell-004",
"metadata": {},
"source": [
"## 1. Load the prepared table\n",
"\n",
"The preparation utility adds two non-feature columns: `source` records provenance and `label` uses `0 = benign`, `1 = malware`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-005",
"metadata": {},
"outputs": [],
"source": [
"# TODO 1: load data/malware_features.csv into df.\n",
"df = None\n",
"\n",
"# TODO 2: verify that source and label exist, then create feature_cols.\n",
"required_columns = {\"source\", \"label\"}\n",
"feature_cols = None\n",
"\n",
"print(\"Dataset shape:\", None)\n",
"print(\"Number of candidate features:\", None)\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"id": "cell-006",
"metadata": {},
"source": [
"## 2. Audit labels and provenance\n",
"\n",
"`source` is useful for detecting collection bias, but it must never be used as a predictor. In this dataset, collection source and class are closely related."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-007",
"metadata": {},
"outputs": [],
"source": [
"# TODO 3: calculate class counts and class proportions.\n",
"class_counts = None\n",
"class_proportions = None\n",
"\n",
"# TODO 4: create a source-by-label cross-tabulation with margins.\n",
"source_by_label = None\n",
"\n",
"display(pd.DataFrame({\"count\": class_counts, \"proportion\": class_proportions}))\n",
"display(source_by_label)"
]
},
{
"cell_type": "markdown",
"id": "cell-008",
"metadata": {},
"source": [
"## 3. Audit data quality and leakage risks\n",
"\n",
"Missing values will be imputed inside a training pipeline in later labs. Imputing before the split would allow test-set statistics to influence training."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-009",
"metadata": {},
"outputs": [],
"source": [
"numeric_features = df[feature_cols].apply(pd.to_numeric, errors=\"coerce\")\n",
"\n",
"# TODO 5: count infinite numeric values and exact duplicate rows.\n",
"infinite_count = None\n",
"duplicate_count = None\n",
"\n",
"# TODO 6: calculate per-feature missing rates and find constant features.\n",
"missing_summary = None\n",
"constant_features = None\n",
"\n",
"# TODO 7: find feature names containing possible label, source, filename, or hash tokens.\n",
"suspicious_tokens = (\"label\", \"class\", \"target\", \"source\", \"filename\", \"hash\", \"md5\", \"sha\")\n",
"suspicious_columns = None\n",
"\n",
"print(\"Exact duplicate rows:\", duplicate_count)\n",
"print(\"Infinite numeric values:\", infinite_count)\n",
"print(\"Constant candidate features:\", len(constant_features))\n",
"print(\"Suspicious feature names:\", suspicious_columns[:20])\n",
"display(missing_summary.head(10).to_frame())"
]
},
{
"cell_type": "markdown",
"id": "cell-010",
"metadata": {},
"source": [
"## 4. Write the dataset risk statement\n",
"\n",
"Record three facts before modeling:\n",
"\n",
"1. The positive class is the majority class.\n",
"2. Benign and malware rows come from different source collections.\n",
"3. A random split can measure interpolation within this historical table, but not performance on future malware campaigns."
]
},
{
"cell_type": "markdown",
"id": "cell-011",
"metadata": {},
"source": [
"## Self-check"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cell-012",
"metadata": {},
"outputs": [],
"source": [
"assert {\"source\", \"label\"}.issubset(df.columns)\n",
"assert len(feature_cols) > 0\n",
"assert set(df[\"label\"].dropna().unique()) == {0, 1}\n",
"assert set(feature_cols).isdisjoint({\"source\", \"label\"})\n",
"assert class_counts.loc[1] > class_counts.loc[0]\n",
"assert len(missing_summary) == len(feature_cols)\n",
"assert infinite_count == 0\n",
"print(\"✓ Lab 1 completed\")"
]
},
{
"cell_type": "markdown",
"id": "cell-013",
"metadata": {},
"source": [
"## Reflection questions\n",
"\n",
"1. Why must `source` be excluded from the model?\n",
"2. Why should missing-value imputation happen after the train-test split?\n",
"3. Why is a random split not sufficient evidence for production deployment?"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}