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

6.6 KiB

Module 5 — Lab 1: Auditing a Malware Feature Dataset

Topics: provenance, schema, imbalance, missing values, leakage

Lab version: Complete every TODO, run the cells in order, and use the self-check before opening the Solution.

Learning objectives

After completing this lab, you will be able to:

  • load a prepared table of static and dynamic malware features safely;
  • distinguish identifiers, provenance fields, features, and the binary label;
  • audit class imbalance, source composition, missing values, infinite values, and duplicates;
  • identify columns that must not enter a predictive model;
  • document dataset limitations before modeling.

Safety boundary: This practical uses numeric feature tables only. Do not download, open, or execute malware samples.

In [ ]:
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

DATA_PATH = Path("data/malware_features.csv")

if not DATA_PATH.exists():
    raise FileNotFoundError(
        "Missing data/malware_features.csv. Follow data/README.md, place the three "
        "official UCI CSV files under data/raw/, and run: python data/prepare_dataset.py"
    )

1. Load the prepared table

The preparation utility adds two non-feature columns: source records provenance and label uses 0 = benign, 1 = malware.

In [ ]:
# TODO 1: load data/malware_features.csv into df.
df = None

# TODO 2: verify that source and label exist, then create feature_cols.
required_columns = {"source", "label"}
feature_cols = None

print("Dataset shape:", None)
print("Number of candidate features:", None)
df.head()

2. Audit labels and provenance

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.

In [ ]:
# TODO 3: calculate class counts and class proportions.
class_counts = None
class_proportions = None

# TODO 4: create a source-by-label cross-tabulation with margins.
source_by_label = None

display(pd.DataFrame({"count": class_counts, "proportion": class_proportions}))
display(source_by_label)

3. Audit data quality and leakage risks

Missing values will be imputed inside a training pipeline in later labs. Imputing before the split would allow test-set statistics to influence training.

In [ ]:
numeric_features = df[feature_cols].apply(pd.to_numeric, errors="coerce")

# TODO 5: count infinite numeric values and exact duplicate rows.
infinite_count = None
duplicate_count = None

# TODO 6: calculate per-feature missing rates and find constant features.
missing_summary = None
constant_features = None

# TODO 7: find feature names containing possible label, source, filename, or hash tokens.
suspicious_tokens = ("label", "class", "target", "source", "filename", "hash", "md5", "sha")
suspicious_columns = None

print("Exact duplicate rows:", duplicate_count)
print("Infinite numeric values:", infinite_count)
print("Constant candidate features:", len(constant_features))
print("Suspicious feature names:", suspicious_columns[:20])
display(missing_summary.head(10).to_frame())

4. Write the dataset risk statement

Record three facts before modeling:

  1. The positive class is the majority class.
  2. Benign and malware rows come from different source collections.
  3. A random split can measure interpolation within this historical table, but not performance on future malware campaigns.

Self-check

In [ ]:
assert {"source", "label"}.issubset(df.columns)
assert len(feature_cols) > 0
assert set(df["label"].dropna().unique()) == {0, 1}
assert set(feature_cols).isdisjoint({"source", "label"})
assert class_counts.loc[1] > class_counts.loc[0]
assert len(missing_summary) == len(feature_cols)
assert infinite_count == 0
print("✓ Lab 1 completed")

Reflection questions

  1. Why must source be excluded from the model?
  2. Why should missing-value imputation happen after the train-test split?
  3. Why is a random split not sufficient evidence for production deployment?