{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":118765,"databundleVersionId":15231210,"isSourceIdPinned":false},{"sourceType":"datasetVersion","sourceId":15076579,"datasetId":9652439,"databundleVersionId":15959302}],"dockerImageVersionId":31287,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# CELL 1: Setup\nimport os, sys, subprocess, time, re, tempfile, warnings\nfrom typing import Optional, List, Dict\nimport pandas as pd\nimport numpy as np\nfrom numpy import ndarray\nwarnings.filterwarnings('ignore')\nos.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'\nos.environ['CUDA_LAUNCH_BLOCKING'] = '0'\n\nDATA        = '/kaggle/input/competitions/stanford-rna-3d-folding-2'\nMODELS_DIR = '/kaggle/input/datasets/asomiddinsaidov/stanford-rn-4/rna-model-weights'\nRHOFOLD_WTS = f'{MODELS_DIR}/RhoFold_pretrained.pt'\nMSA_DIR     = f'{DATA}/MSA'\nPDB_DIR     = f'{DATA}/PDB_RNA'\nPDB_SEQRES  = f'{DATA}/PDB_RNA/pdb_seqres_NA.fasta'\nPDB_DATES   = f'{DATA}/PDB_RNA/pdb_release_dates_NA.csv'\nMETADATA    = f'{DATA}/extra/rna_metadata.csv'\nSAMPLE_SUB  = f'{DATA}/sample_submission.csv'\n\nprint(\"Libraries pre-installed.\")\nsubprocess.run([\n    sys.executable, '-m', 'pip', 'install',\n    f'{MODELS_DIR}/biopython-1.84-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl',\n    f'{MODELS_DIR}/gemmi-0.6.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl',\n    '--quiet', '--no-index'\n], check=True)\nprint(\"Libraries installed from wheels.\")\n\nimport shutil\nUSALIGN_BIN = '/kaggle/working/USalign'\nshutil.copy2(f'{MODELS_DIR}/USalign', USALIGN_BIN)\nos.chmod(USALIGN_BIN, 0o755)\nprint(f\"USalign ready.\")\n\ntest   = pd.read_csv(f'{DATA}/test_sequences.csv')\nsample = pd.read_csv(SAMPLE_SUB)\ntest['seq_len'] = test['sequence'].str.len()\nprint(f\"Targets: {len(test)}, Residues: {int(test['seq_len'].sum())}, Weights: {os.path.exists(RHOFOLD_WTS)}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:32.035116Z","iopub.execute_input":"2026-03-07T10:44:32.035866Z","iopub.status.idle":"2026-03-07T10:44:35.455792Z","shell.execute_reply.started":"2026-03-07T10:44:32.035831Z","shell.execute_reply":"2026-03-07T10:44:35.455112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 2: Routing\nTARGET_ROUTING: Dict[str,str] = {\n    '9IWF':'A','9HRO':'A','9EBP':'A','9LJN':'A','9CFN':'A','9G4J':'A','9KGG':'A','9JGM':'A',\n    '8ZNQ':'B','9E9Q':'B','9QZJ':'B','9OBM':'B','9RVP':'B','9WHV':'B','9OD4':'B',\n    '9J09':'B','9JFS':'B','9E74':'B','9E75':'B',\n    '9G4P':'C','9G4Q':'C','9G4R':'C','9LEC':'C','9LEL':'C','9I9W':'C','9JFO':'C',\n    '9MME':'G','9ZCC':'G',\n}\nprint(\"Routing loaded:\", len(TARGET_ROUTING), \"targets\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:35.457348Z","iopub.execute_input":"2026-03-07T10:44:35.45764Z","iopub.status.idle":"2026-03-07T10:44:35.462649Z","shell.execute_reply.started":"2026-03-07T10:44:35.457617Z","shell.execute_reply":"2026-03-07T10:44:35.461986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 3: Load RhoFold - force CPU to avoid corrupted GPU state\nimport torch\nimport torch.nn as nn\n\n# Check GPU but load on CPU first to test\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(f\"Device: {device}\")\n\nsys.path.insert(0, f'{MODELS_DIR}/rhofold_src')\nfrom rhofold.rhofold import RhoFold\nfrom rhofold.config import rhofold_config\n\nrhofold_model = RhoFold(rhofold_config)\n# Load to CPU first, then move to GPU\nsd = torch.load(RHOFOLD_WTS, map_location='cpu')\nif 'model' in sd: sd = sd['model']\nelif 'state_dict' in sd: sd = sd['state_dict']\nrhofold_model.load_state_dict(sd, strict=False)\nrhofold_model.eval()\n# Now move to GPU\nrhofold_model = rhofold_model.to(device)\nprint(f\"RhoFold+ loaded on {device}.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:35.463715Z","iopub.execute_input":"2026-03-07T10:44:35.463948Z","iopub.status.idle":"2026-03-07T10:44:39.820442Z","shell.execute_reply.started":"2026-03-07T10:44:35.46393Z","shell.execute_reply":"2026-03-07T10:44:39.819718Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 4: BLAST + Templates (offline version)\nfrom Bio.Blast import NCBIXML\nimport gemmi\nfrom Bio import pairwise2\nimport stat\n\n# Copy BLAST binaries to writable location and chmod\nfor binary in ['blastn', 'makeblastdb']:\n    src = f'{MODELS_DIR}/{binary}'\n    dst = f'/kaggle/working/{binary}'\n    if not os.path.exists(dst):\n        import shutil\n        shutil.copy2(src, dst)\n    os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)\n\nBLASTN_BIN  = '/kaggle/working/blastn'\nMAKEBLASTDB = '/kaggle/working/makeblastdb'\nCIF_DIR     = f'{MODELS_DIR}/cif_files'\n\nBLAST_DB = '/kaggle/working/pdb_rna_blastdb'\nif not os.path.exists(BLAST_DB+'.nhr'):\n    subprocess.run([MAKEBLASTDB,'-in',PDB_SEQRES,'-dbtype','nucl','-out',BLAST_DB], check=True)\nprint(\"BLAST DB ready.\")\n\nrelease_df = pd.read_csv(PDB_DATES)\nrelease_df['Release Date'] = pd.to_datetime(release_df['Release Date'])\nrelease_lookup = dict(zip(release_df['Entry ID'].str.upper(), release_df['Release Date']))\n\nmeta_df = pd.read_csv(METADATA)\nhq_pdbs = set(meta_df[(meta_df['resolution']<=3.0)|(meta_df['method'].str.contains('NMR',na=False))]['pdb_id'].str.upper())\nprint(f\"HQ PDBs: {len(hq_pdbs)}, Release dates: {len(release_lookup)}\")\n\ndef extract_c1prime(pdb_id, chain_id=None):\n    pdb_id = pdb_id.upper()\n    for cif in [f'{CIF_DIR}/{pdb_id}.cif', f'{PDB_DIR}/{pdb_id}.cif']:\n        if os.path.exists(cif):\n            try:\n                st = gemmi.read_structure(cif)\n                rows = []\n                for model in st:\n                    for chain in model:\n                        if chain_id and chain.name != chain_id: continue\n                        for res in chain:\n                            for atom in res:\n                                if atom.name==\"C1'\":\n                                    rows.append({'resid':res.seqid.num,'resname':res.name[0],\n                                                 'x':atom.pos.x,'y':atom.pos.y,'z':atom.pos.z})\n                return pd.DataFrame(rows)\n            except: pass\n    return pd.DataFrame()\n\ndef search_templates(sequence, top_n=10):\n    with tempfile.NamedTemporaryFile(mode='w',suffix='.fasta',delete=False) as f:\n        f.write(f'>query\\n{sequence}\\n'); qfile=f.name\n    ofile = qfile.replace('.fasta','_blast.xml')\n    try:\n        subprocess.run([BLASTN_BIN,'-query',qfile,'-db',BLAST_DB,'-outfmt','5','-out',ofile,\n                        '-word_size','7','-evalue','10','-num_alignments',str(top_n)],\n                       check=True, capture_output=True)\n        results = []\n        with open(ofile) as f:\n            for rec in NCBIXML.parse(f):\n                for aln in rec.alignments:\n                    for hsp in aln.hsps:\n                        hd = aln.hit_def.strip()\n                        pdb_id = hd[:4].upper()\n                        chain  = hd.split('_')[1][0] if '_' in hd else 'A'\n                        results.append({'pdb_id':pdb_id,'chain':chain,\n                                        'identity':hsp.identities/hsp.align_length,\n                                        'coverage':hsp.align_length/len(sequence),\n                                        'evalue':hsp.expect})\n        return sorted(results, key=lambda x:-x['identity'])\n    except: return []\n    finally:\n        for fp in [qfile,ofile]:\n            if os.path.exists(fp): os.unlink(fp)\n\ndef filter_templates(hits, cutoff_str, require_hq=True):\n    cutoff = pd.to_datetime(cutoff_str)\n    valid = []\n    for h in hits:\n        pid = h['pdb_id'].upper()\n        rel = release_lookup.get(pid)\n        if rel is None or rel > cutoff: continue\n        if require_hq and pid not in hq_pdbs: continue\n        valid.append(h)\n    return valid\n\ndef template_to_coords(target_seq, template_df):\n    if template_df.empty: return None\n    tmpl_seq = ''.join(str(r)[0] for r in template_df['resname'])\n    try: alns = pairwise2.align.globalms(target_seq, tmpl_seq, 2,-1,-2,-0.5)\n    except: return None\n    if not alns: return None\n    coords = np.zeros((len(target_seq),3), dtype=np.float64)\n    t_pos = q_pos = 0\n    for ta,tt in zip(alns[0][0], alns[0][1]):\n        if ta!='-' and tt!='-':\n            if t_pos < len(template_df):\n                coords[q_pos] = template_df.iloc[t_pos][['x','y','z']].values.astype(np.float64)\n            q_pos+=1; t_pos+=1\n        elif ta!='-': q_pos+=1\n        else: t_pos+=1\n    return coords\n\nprint(\"Template functions ready.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:39.821269Z","iopub.execute_input":"2026-03-07T10:44:39.82167Z","iopub.status.idle":"2026-03-07T10:44:41.034616Z","shell.execute_reply.started":"2026-03-07T10:44:39.821645Z","shell.execute_reply":"2026-03-07T10:44:41.033826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 5: predict_dl\nfrom rhofold.utils.alphabet import get_features\n\ndef predict_dl(sequence, target_id, n_preds=5):\n    n_res = len(sequence)\n    msa_path = f'{MSA_DIR}/{target_id}.MSA.fasta'\n    if not os.path.exists(msa_path):\n        msa_path = tempfile.mktemp(suffix='.fasta')\n        with open(msa_path,'w') as f: f.write(f'>{target_id}\\n{sequence}\\n')\n\n    fas_file = tempfile.mktemp(suffix='.fasta')\n    with open(fas_file,'w') as f: f.write(f'>{target_id}\\n{sequence}\\n')\n\n    preds = []\n    try:\n        def run_depth(depth):\n            ft = get_features(fas_fpath=fas_file, msa_fpath=msa_path, msa_depth=depth)\n            with torch.no_grad():\n                out = rhofold_model(tokens=ft['tokens'].to(device),\n                                    rna_fm_tokens=ft['rna_fm_tokens'].to(device),\n                                    seq=ft['seq'])\n            return out\n\n        o1 = run_depth(128)\n        preds.append(o1[-1][\"cords_c1'\"][-1].squeeze(0).cpu().numpy())\n        o2 = run_depth(10)\n        preds.append(o2[-1][\"cords_c1'\"][-1].squeeze(0).cpu().numpy())\n        o3 = run_depth(32)\n        preds.append(o3[-1][\"cords_c1'\"][-1].squeeze(0).cpu().numpy())\n        n_rec = len(o1)\n        preds.append(o1[n_rec//2][\"cords_c1'\"][-1].squeeze(0).cpu().numpy())\n        preds.append(o1[n_rec//4][\"cords_c1'\"][-1].squeeze(0).cpu().numpy())\n    except Exception as exc:\n        print(f\"  DL error: {exc}\")\n        while len(preds) < n_preds:\n            preds.append(np.zeros((n_res,3), dtype=np.float64))\n    finally:\n        if os.path.exists(fas_file): os.unlink(fas_file)\n    return preds[:n_preds]\n\n# Quick test\nt = predict_dl(str(test['sequence'].iloc[0]), str(test['target_id'].iloc[0]), n_preds=1)\nprint(f\"predict_dl OK: shape={t[0].shape}, non-zero={( t[0].sum(axis=1)!=0).sum()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:41.036304Z","iopub.execute_input":"2026-03-07T10:44:41.036553Z","iopub.status.idle":"2026-03-07T10:44:46.951781Z","shell.execute_reply.started":"2026-03-07T10:44:41.036531Z","shell.execute_reply":"2026-03-07T10:44:46.9511Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 6: predict_target\nSKIP_DL = {'9MME', '9ZCC', '9LEL'}\n\ndef predict_target(row):\n    tid      = str(row['target_id'])\n    sequence = str(row['sequence'])\n    n_res    = len(sequence)\n    cutoff   = str(row['temporal_cutoff'])\n    category = TARGET_ROUTING.get(tid, 'C')\n    print(f\"  Cat={category} | {n_res}nt\")\n\n    if tid in SKIP_DL or category == 'G':\n        print(f\"  -> Skipping DL (too large), using zeros\")\n        return [np.zeros((n_res, 3), dtype=np.float64) for _ in range(5)]\n\n    best_coords = None\n    best_id = 0.0\n\n    if category in ('A', 'B'):\n        hits  = search_templates(sequence)\n        valid = filter_templates(hits, cutoff)\n        if valid:\n            best = valid[0]\n            best_id = float(best['identity'])\n            print(f\"  Template: {best['pdb_id']} id={best_id:.1%}\")\n            if best_id >= 0.40:\n                tmpl = extract_c1prime(best['pdb_id'], best['chain'])\n                if not tmpl.empty:\n                    best_coords = template_to_coords(sequence, tmpl)\n        else:\n            print(\"  No templates, DL only\")\n\n    preds = [None] * 5\n\n    if best_coords is not None and best_id >= 0.80:\n        preds[0] = best_coords.copy()\n        preds[2] = best_coords.copy()\n        try:\n            dl = predict_dl(sequence, tid, n_preds=3)\n            preds[1], preds[3], preds[4] = dl[0], dl[1], dl[2]\n        except:\n            preds[1] = preds[3] = preds[4] = best_coords.copy()\n    elif best_coords is not None:\n        preds[2] = best_coords.copy()\n        try:\n            dl = predict_dl(sequence, tid, n_preds=4)\n            preds[0], preds[1], preds[3], preds[4] = dl[0], dl[1], dl[2], dl[3]\n        except:\n            for i in [0, 1, 3, 4]: preds[i] = best_coords.copy()\n    else:\n        try:\n            dl = predict_dl(sequence, tid, n_preds=5)\n            for i in range(5): preds[i] = dl[i]\n        except:\n            for i in range(5): preds[i] = np.zeros((n_res, 3), dtype=np.float64)\n\n    return [p if p is not None else np.zeros((n_res, 3), dtype=np.float64) for p in preds]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:46.952714Z","iopub.execute_input":"2026-03-07T10:44:46.953139Z","iopub.status.idle":"2026-03-07T10:44:46.963633Z","shell.execute_reply.started":"2026-03-07T10:44:46.953113Z","shell.execute_reply":"2026-03-07T10:44:46.962908Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 7: Main loop\nall_rows = []\nstart_time = time.time()\nfailed = []\n\nfor i, row in test.sort_values('seq_len').reset_index(drop=True).iterrows():\n    tid   = str(row['target_id'])\n    seq   = str(row['sequence'])\n    n_res = len(seq)\n    elapsed = (time.time()-start_time)/60\n    print(f\"\\n[{i+1}/28] {tid} | {n_res}nt | {elapsed:.1f}min\")\n\n    try:\n        preds = predict_target(row)\n        for res_i in range(n_res):\n            r = {'ID':f'{tid}_{res_i+1}','resname':seq[res_i],'resid':res_i+1}\n            for p in range(5):\n                r[f'x_{p+1}']=float(preds[p][res_i,0])\n                r[f'y_{p+1}']=float(preds[p][res_i,1])\n                r[f'z_{p+1}']=float(preds[p][res_i,2])\n            all_rows.append(r)\n    except Exception as exc:\n        print(f\"  FAILED: {exc}\"); failed.append(tid)\n        for res_i in range(n_res):\n            r = {'ID':f'{tid}_{res_i+1}','resname':seq[res_i],'resid':res_i+1}\n            for p in range(5): r[f'x_{p+1}']=r[f'y_{p+1}']=r[f'z_{p+1}']=0.0\n            all_rows.append(r)\n\nprint(f\"\\nDone in {(time.time()-start_time)/60:.1f}min | Failed: {failed} | Rows: {len(all_rows)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T10:44:46.964496Z","iopub.execute_input":"2026-03-07T10:44:46.964741Z","iopub.status.idle":"2026-03-07T11:32:05.196074Z","shell.execute_reply.started":"2026-03-07T10:44:46.964708Z","shell.execute_reply":"2026-03-07T11:32:05.195246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 8: Validate and save\nimport os\nimport numpy as np\nsub = pd.DataFrame(all_rows)\ncoord_cols = [c for c in sub.columns if c.startswith(('x_','y_','z_'))]\nsub[coord_cols] = sub[coord_cols].clip(-999.999, 9999.999)\n\nsample_sub = pd.read_csv(SAMPLE_SUB)\nerrors = []\nif len(sub) != len(sample_sub): errors.append(f\"Row count: {len(sub)} vs {len(sample_sub)}\")\nif set(sample_sub['ID']) - set(sub['ID']): errors.append(\"Missing IDs\")\nif len(coord_cols) != 15: errors.append(f\"Wrong coord cols: {len(coord_cols)}\")\nif sub[coord_cols].isnull().any().any(): errors.append(\"NaN values\")\n\nif errors:\n    for e in errors: print(f\"ERROR: {e}\")\nelse:\n    # Save to both locations to be safe\n    sub.to_csv('/kaggle/working/submission.csv', index=False)\n    sub.to_csv('submission.csv', index=False)\n    print(f\"submission.csv saved. Shape: {sub.shape}\")\n    print(f\"File exists check: {os.path.exists('/kaggle/working/submission.csv')}\")\n    print(f\"File size: {os.path.getsize('/kaggle/working/submission.csv')/1e6:.1f} MB\")\n    non_zero = ((sub['x_1'] != 0) | (sub['y_1'] != 0) | (sub['z_1'] != 0)).sum()\n    print(f\"Non-zero rows in slot 1: {non_zero}/{len(sub)}\")\ndef fill_zero_residues(coords):\n    \"\"\"Fill zero rows by interpolating from nearest non-zero neighbors.\"\"\"\n    n = len(coords)\n    filled = coords.copy()\n    \n    for i in range(n):\n        if filled[i].sum() == 0:\n            # Find nearest non-zero neighbors\n            prev_idx = next_idx = None\n            for j in range(i-1, -1, -1):\n                if filled[j].sum() != 0:\n                    prev_idx = j; break\n            for j in range(i+1, n):\n                if filled[j].sum() != 0:\n                    next_idx = j; break\n            \n            if prev_idx is not None and next_idx is not None:\n                # Interpolate\n                t = (i - prev_idx) / (next_idx - prev_idx)\n                filled[i] = filled[prev_idx] * (1-t) + filled[next_idx] * t\n            elif prev_idx is not None:\n                filled[i] = filled[prev_idx]\n            elif next_idx is not None:\n                filled[i] = filled[next_idx]\n    return filled\n\n# Apply to all slots and all targets\nsub = pd.DataFrame(all_rows)\ncoord_cols = [c for c in sub.columns if c.startswith(('x_','y_','z_'))]\n\nfor tid in sub['ID'].str.split('_').str[0].unique():\n    mask = sub['ID'].str.startswith(tid+'_')\n    for p in range(1, 6):\n        coords = sub.loc[mask, [f'x_{p}',f'y_{p}',f'z_{p}']].values.astype(np.float64)\n        filled = fill_zero_residues(coords)\n        sub.loc[mask, f'x_{p}'] = filled[:,0]\n        sub.loc[mask, f'y_{p}'] = filled[:,1]\n        sub.loc[mask, f'z_{p}'] = filled[:,2]\n\nsub[coord_cols] = sub[coord_cols].clip(-999.999, 9999.999)\n\n# Verify\nnon_zero = ((sub['x_1']!=0)|(sub['y_1']!=0)|(sub['z_1']!=0)).sum()\nprint(f\"Non-zero after filling: {non_zero}/{len(sub)}\")\n\nsub.to_csv('/kaggle/working/submission.csv', index=False)\nsub.to_csv('submission.csv', index=False)\nprint(f\"Saved. Shape: {sub.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T11:32:05.197162Z","iopub.execute_input":"2026-03-07T11:32:05.197476Z","iopub.status.idle":"2026-03-07T11:35:18.242179Z","shell.execute_reply.started":"2026-03-07T11:32:05.197451Z","shell.execute_reply":"2026-03-07T11:35:18.24155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 9: Submit\nprint(\"Ready to submit!\")\nprint(f\"Total rows: {len(sub)}\")\nprint(f\"Targets with templates: {sum(1 for t in TARGET_ROUTING if TARGET_ROUTING[t] in ('A','B'))}\")\nprint(f\"submission.csv exists: {os.path.exists('submission.csv')}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T11:35:18.243199Z","iopub.execute_input":"2026-03-07T11:35:18.243561Z","iopub.status.idle":"2026-03-07T11:35:18.248345Z","shell.execute_reply.started":"2026-03-07T11:35:18.243537Z","shell.execute_reply":"2026-03-07T11:35:18.247634Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nsub = pd.read_csv('/kaggle/working/submission.csv')\n\nprint(\"Non-zero residues per target:\")\nfor tid in sub['ID'].str.split('_').str[0].unique():\n    tdf = sub[sub['ID'].str.startswith(tid+'_')]\n    nz = ((tdf['x_1']!=0)|(tdf['y_1']!=0)|(tdf['z_1']!=0)).sum()\n    total = len(tdf)\n    status = \"✅\" if nz==total else (\"❌ ALL ZERO\" if nz==0 else f\"⚠️ {nz}/{total}\")\n    print(f\"  {tid}: {status}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-07T11:35:18.249275Z","iopub.execute_input":"2026-03-07T11:35:18.24954Z","iopub.status.idle":"2026-03-07T11:35:18.37229Z","shell.execute_reply.started":"2026-03-07T11:35:18.249515Z","shell.execute_reply":"2026-03-07T11:35:18.371513Z"}},"outputs":[],"execution_count":null}]}