from pathlib import Path
import json
import subprocess
import sys
from urllib.parse import quote
IS_COLAB = 'google.colab' in sys.modules
RAW_BASE = 'https://raw.githubusercontent.com/FrancisBurnet/francisburnet/main'
CAPSTONE_ROOT = Path('Incremental Capstones/Machine Learning Using Python/Capstone Session 8')
MOVIES_FILENAME = 'movies.csv'
RATINGS_FILENAME = 'ratings.csv'
def build_raw_github_url(relative_path: Path) -> str:
return RAW_BASE + '/' + quote(relative_path.as_posix(), safe='/')
def resolve_capstone_dir() -> Path | None:
current = Path.cwd().resolve()
capstone_parts = CAPSTONE_ROOT.parts
for candidate in [current, *current.parents]:
if len(candidate.parts) >= len(capstone_parts) and candidate.parts[-len(capstone_parts):] == capstone_parts:
return candidate
nested_candidate = candidate / CAPSTONE_ROOT
if nested_candidate.exists():
return nested_candidate
return None
def inprocess_stack_ok() -> bool:
try:
import numpy # noqa: F401
import pandas # noqa: F401
import scipy # noqa: F401
import surprise # noqa: F401
return True
except Exception:
return False
def request_runtime_restart() -> bool:
# Colab API has changed over time; try multiple restart paths.
try:
from google.colab import runtime as colab_runtime
colab_runtime.restart_runtime()
return True
except Exception:
pass
try:
from IPython import get_ipython
ip = get_ipython()
if ip is not None and getattr(ip, 'kernel', None) is not None:
ip.kernel.do_shutdown(restart=True)
return True
except Exception:
pass
return False
def ensure_colab_reco_stack() -> None:
if not IS_COLAB:
return
if inprocess_stack_ok():
return
print('Repairing Colab package stack for Session 8 (numpy/pandas/scipy/surprise)...')
subprocess.run(['apt-get', 'update', '-qq'], check=True)
subprocess.run(['apt-get', 'install', '-y', '-qq', 'build-essential'], check=True)
# Remove stale/broken surprise package before rebuilding.
subprocess.run([sys.executable, '-m', 'pip', 'uninstall', '-y', 'scikit-surprise', 'surprise'], check=False)
# Pin a coherent scientific stack, then compile surprise against that ABI.
subprocess.run(
[
sys.executable,
'-m',
'pip',
'install',
'-q',
'--no-cache-dir',
'--upgrade',
'--force-reinstall',
'numpy<2',
'pandas<2.3',
'scipy<1.14',
],
check=True,
)
subprocess.run(
[
sys.executable,
'-m',
'pip',
'install',
'-q',
'--no-cache-dir',
'--force-reinstall',
'--no-binary',
'scikit-surprise',
'--no-deps',
'scikit-surprise',
],
check=True,
)
print('Package stack repaired. Restarting runtime; then Run all.')
if request_runtime_restart():
raise SystemExit('Runtime restarting. Re-run all cells.')
raise SystemExit('Please restart runtime manually (Runtime > Restart session), then Run all cells.')
CAPSTONE_DIR = resolve_capstone_dir()
MOVIES_URL = build_raw_github_url(CAPSTONE_ROOT / MOVIES_FILENAME)
RATINGS_URL = build_raw_github_url(CAPSTONE_ROOT / RATINGS_FILENAME)
if CAPSTONE_DIR is not None:
OUTPUT_ROOT = CAPSTONE_DIR
OUTPUT_MODE = 'permanent capstone outputs'
OUTPUT_DISPLAY = (CAPSTONE_ROOT / 'outputs').as_posix()
else:
runtime_root = Path('/content/capstone-session-8-runtime') if IS_COLAB else Path.cwd().resolve() / 'capstone-session-8-runtime'
OUTPUT_ROOT = runtime_root
OUTPUT_MODE = 'runtime scratch outputs; export final artifacts back into the capstone outputs folder'
OUTPUT_DISPLAY = 'capstone-session-8-runtime/outputs'
OUTPUTS_DIR = (OUTPUT_ROOT / 'outputs').resolve()
PLOTS_DIR = OUTPUTS_DIR / 'plots'
OUTPUTS_DIR.mkdir(parents=True, exist_ok=True)
PLOTS_DIR.mkdir(parents=True, exist_ok=True)
ensure_colab_reco_stack()
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from IPython.display import display
from surprise import Dataset, KNNBasic, NMF as SurpriseNMF, Reader, SVD
from surprise.model_selection import KFold as SurpriseKFold, cross_validate
sns.set_theme(style='whitegrid')
pd.set_option('display.max_columns', 100)
print('Runtime:', 'Google Colab' if IS_COLAB else 'Notebook runtime')
print('Capstone artifact path:', CAPSTONE_ROOT.as_posix())
print('Movies source:', MOVIES_URL)
print('Ratings source:', RATINGS_URL)
print('Output mode:', OUTPUT_MODE)
print('Output target:', OUTPUT_DISPLAY)
print('NumPy version:', np.__version__)
print('scikit-surprise import ready')