{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 大环内酯分子过滤分析\n",
    "\n",
    "对12-20元环大环内酯分子进行进一步过滤，去除不符合要求的分子：\n",
    "1. **过滤有桥环的分子**（RDKit检测到多个环的情况）\n",
    "2. **过滤环上有多酯键的分子**（环上有多个内酯键）\n",
    "3. **过滤环上有酰胺键的分子**（环上含有酰胺键）\n",
    "\n",
    "## 数据来源\n",
    "- 输入目录：`macrolactone_ring_results_complete/`\n",
    "- 输出目录：`macrolactone_ring_results_filtered/`\n",
    "- 处理文件：12-20元环的完整结果CSV文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ 所有模块导入成功!\n",
      "项目根目录: /home/zly/project/search_macro\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "# 添加项目根目录到 Python 路径\n",
    "notebook_dir = Path().resolve()\n",
    "project_root = notebook_dir.parent\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "# 导入RDKit和相关库\n",
    "from rdkit import Chem\n",
    "from rdkit.Chem import Descriptors, AllChem, Draw\n",
    "from rdkit.Chem.Draw import rdMolDraw2D\n",
    "from IPython.display import SVG, display\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from typing import List, Dict, Tuple, Optional, Set\n",
    "import json\n",
    "from collections import defaultdict\n",
    "import time\n",
    "import multiprocessing\n",
    "from tqdm import tqdm\n",
    "from joblib import Parallel, delayed\n",
    "import gc\n",
    "\n",
    "# 导入项目模块\n",
    "from src.ring_visualization import get_ring_atoms_by_size\n",
    "from src.macro_lactone_analyzer import MacroLactoneAnalyzer\n",
    "\n",
    "print(\"✅ 所有模块导入成功!\")\n",
    "print(f\"项目根目录: {project_root}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 设置路径和文件列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "输入目录: /home/zly/project/search_macro/macrolactone_ring_results_complete\n",
      "输出目录: /home/zly/project/search_macro/macrolactone_ring_results_filtered\n",
      "处理环大小: [12, 13, 14, 15, 16, 17, 18, 19, 20]\n",
      "  12元环: macrolactone_ring12_complete.csv (0.13 MB)\n",
      "  13元环: macrolactone_ring13_complete.csv (0.17 MB)\n",
      "  14元环: macrolactone_ring14_complete.csv (0.86 MB)\n",
      "  15元环: macrolactone_ring15_complete.csv (0.05 MB)\n",
      "  16元环: macrolactone_ring16_complete.csv (0.57 MB)\n",
      "  17元环: macrolactone_ring17_complete.csv (0.01 MB)\n",
      "  18元环: macrolactone_ring18_complete.csv (0.06 MB)\n",
      "  19元环: macrolactone_ring19_complete.csv (0.03 MB)\n",
      "  20元环: macrolactone_ring20_complete.csv (0.00 MB)\n",
      "\n",
      "找到 9 个输入文件\n"
     ]
    }
   ],
   "source": [
    "# 设置路径\n",
    "input_dir = project_root / 'macrolactone_ring_results_complete'\n",
    "output_dir = project_root / 'macrolactone_ring_results_filtered'\n",
    "\n",
    "# 创建输出目录\n",
    "output_dir.mkdir(exist_ok=True)\n",
    "\n",
    "print(f\"输入目录: {input_dir}\")\n",
    "print(f\"输出目录: {output_dir}\")\n",
    "\n",
    "# 定义要处理的环大小\n",
    "ring_sizes = list(range(12, 21))  # 12-20元环\n",
    "print(f\"处理环大小: {ring_sizes}\")\n",
    "\n",
    "# 获取所有输入CSV文件\n",
    "input_files = {}\n",
    "for ring_size in ring_sizes:\n",
    "    csv_file = input_dir / f'macrolactone_ring{ring_size}_complete.csv'\n",
    "    if csv_file.exists():\n",
    "        input_files[ring_size] = csv_file\n",
    "        size_mb = csv_file.stat().st_size / (1024**2)\n",
    "        print(f\"  {ring_size}元环: {csv_file.name} ({size_mb:.2f} MB)\")\n",
    "    else:\n",
    "        print(f\"  {ring_size}元环: 文件不存在\")\n",
    "\n",
    "print(f\"\\n找到 {len(input_files)} 个输入文件\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 定义过滤函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "过滤函数定义完成!\n"
     ]
    }
   ],
   "source": [
    "def has_bridged_ring(mol: Chem.Mol, target_ring_size: int) -> bool:\n",
    "    \"\"\"\n",
    "    检测分子是否包含桥环结构\n",
    "    桥环：原子属于多个环，且至少有一个是目标大小的环\n",
    "    \n",
    "    Args:\n",
    "        mol: RDKit分子对象\n",
    "        target_ring_size: 目标环大小\n",
    "    \n",
    "    Returns:\n",
    "        是否有桥环结构\n",
    "    \"\"\"\n",
    "    if mol is None:\n",
    "        return False\n",
    "    \n",
    "    # 获取所有环信息\n",
    "    ring_info = mol.GetRingInfo()\n",
    "    \n",
    "    # 检查是否有原子属于多个环（可能表示桥环）\n",
    "    for atom in mol.GetAtoms():\n",
    "        if atom.IsInRing():\n",
    "            # 获取该原子所属的环数量\n",
    "            ring_count = ring_info.NumAtomRings(atom.GetIdx())\n",
    "            if ring_count > 1:\n",
    "                # 检查是否至少有一个是目标大小的环\n",
    "                atom_rings = ring_info.AtomRings()\n",
    "                for ring in atom_rings:\n",
    "                    if atom.GetIdx() in ring and len(ring) == target_ring_size:\n",
    "                        return True\n",
    "    \n",
    "    return False\n",
    "\n",
    "\n",
    "def has_multiple_lactones_on_ring(mol: Chem.Mol, target_ring_size: int) -> bool:\n",
    "    \"\"\"\n",
    "    检测环上是否有多个内酯键（酯键）\n",
    "    适用于任意大小的环\n",
    "    \n",
    "    Args:\n",
    "        mol: RDKit分子对象\n",
    "        target_ring_size: 目标环大小\n",
    "    \n",
    "    Returns:\n",
    "        环上是否有多个内酯键\n",
    "    \"\"\"\n",
    "    if mol is None:\n",
    "        return False\n",
    "    \n",
    "    # 获取目标大小环上的所有原子\n",
    "    ring_atoms = get_ring_atoms_by_size(mol, target_ring_size)\n",
    "    \n",
    "    if len(ring_atoms) == 0:\n",
    "        return False\n",
    "    \n",
    "    # 使用SMARTS模式：环上的氧连接到环上的羰基碳\n",
    "    # [r{size};O] 环上的氧，连接到 [r{size};#6] 环上的碳，该碳有双键氧 (=O)\n",
    "    lactone_pattern = Chem.MolFromSmarts(f\"[r{target_ring_size};#8][r{target_ring_size};#6](=[#8])\")\n",
    "    \n",
    "    if lactone_pattern is None:\n",
    "        return False\n",
    "    \n",
    "    matches = mol.GetSubstructMatches(lactone_pattern)\n",
    "    \n",
    "    # 提取环上的内酯碳索引（match[1]是羰基碳）\n",
    "    lactone_carbons_on_ring = []\n",
    "    for match in matches:\n",
    "        if len(match) >= 2:\n",
    "            carbonyl_carbon = match[1]  # 羰基碳\n",
    "            # 确保这个碳确实在目标大小的环上\n",
    "            if carbonyl_carbon in ring_atoms:\n",
    "                lactone_carbons_on_ring.append(carbonyl_carbon)\n",
    "    \n",
    "    # 去重\n",
    "    lactone_carbons_on_ring = list(set(lactone_carbons_on_ring))\n",
    "    \n",
    "    # 只有当环上有多个内酯键时才返回True\n",
    "    return len(lactone_carbons_on_ring) > 1\n",
    "\n",
    "\n",
    "def has_amide_on_ring(mol: Chem.Mol, target_ring_size: int) -> bool:\n",
    "    \"\"\"\n",
    "    检测环上是否有酰胺键\n",
    "    酰胺键模式：NC(=O) 或 C(=O)N，且N和C都在环上\n",
    "    \n",
    "    Args:\n",
    "        mol: RDKit分子对象\n",
    "        target_ring_size: 目标环大小\n",
    "    \n",
    "    Returns:\n",
    "        环上是否有酰胺键\n",
    "    \"\"\"\n",
    "    if mol is None:\n",
    "        return False\n",
    "    \n",
    "    # 获取目标大小环上的所有原子\n",
    "    ring_atoms = get_ring_atoms_by_size(mol, target_ring_size)\n",
    "    \n",
    "    if len(ring_atoms) == 0:\n",
    "        return False\n",
    "    \n",
    "    # 模式1: 环上的N连接到环上的C(=O)\n",
    "    # [r{size};N][r{size};C](=[#8])\n",
    "    amide_pattern1 = Chem.MolFromSmarts(f\"[r{target_ring_size};#7][r{target_ring_size};#6](=[#8])\")\n",
    "    \n",
    "    # 模式2: 环上的C(=O)连接到环上的N\n",
    "    # [r{size};C](=[#8])[r{size};N]\n",
    "    amide_pattern2 = Chem.MolFromSmarts(f\"[r{target_ring_size};#6](=[#8])[r{target_ring_size};#7]\")\n",
    "    \n",
    "    if amide_pattern1 is None or amide_pattern2 is None:\n",
    "        return False\n",
    "    \n",
    "    matches1 = mol.GetSubstructMatches(amide_pattern1)\n",
    "    matches2 = mol.GetSubstructMatches(amide_pattern2)\n",
    "    \n",
    "    # 检查匹配是否在环上\n",
    "    for match in matches1:\n",
    "        if len(match) >= 2:\n",
    "            n_atom = match[0]  # N原子\n",
    "            c_atom = match[1]  # C原子\n",
    "            if n_atom in ring_atoms and c_atom in ring_atoms:\n",
    "                return True\n",
    "    \n",
    "    for match in matches2:\n",
    "        if len(match) >= 2:\n",
    "            c_atom = match[0]  # C原子\n",
    "            n_atom = match[1]  # N原子\n",
    "            if c_atom in ring_atoms and n_atom in ring_atoms:\n",
    "                return True\n",
    "    \n",
    "    return False\n",
    "\n",
    "\n",
    "def filter_molecule(smiles: str, target_ring_size: int) -> Dict[str, bool]:\n",
    "    \"\"\"\n",
    "    对单个分子进行所有过滤检查\n",
    "    \n",
    "    Args:\n",
    "        smiles: SMILES字符串\n",
    "        target_ring_size: 目标环大小\n",
    "    \n",
    "    Returns:\n",
    "        包含各种过滤结果的字典\n",
    "    \"\"\"\n",
    "    # 解析SMILES\n",
    "    mol = Chem.MolFromSmiles(smiles)\n",
    "    \n",
    "    if mol is None:\n",
    "        return {\n",
    "            'parse_success': False,\n",
    "            'has_bridged_ring': False,\n",
    "            'has_multiple_lactones': False,\n",
    "            'has_amide': False,\n",
    "            'should_keep': False\n",
    "        }\n",
    "    \n",
    "    # 执行各种过滤检查\n",
    "    bridged = has_bridged_ring(mol, target_ring_size)\n",
    "    multiple_lactones = has_multiple_lactones_on_ring(mol, target_ring_size)\n",
    "    amide = has_amide_on_ring(mol, target_ring_size)\n",
    "    \n",
    "    # 只有当所有检查都通过时才保留分子\n",
    "    should_keep = not (bridged or multiple_lactones or amide)\n",
    "    \n",
    "    return {\n",
    "        'parse_success': True,\n",
    "        'has_bridged_ring': bridged,\n",
    "        'has_multiple_lactones': multiple_lactones,\n",
    "        'has_amide': amide,\n",
    "        'should_keep': should_keep\n",
    "    }\n",
    "\n",
    "\n",
    "print(\"过滤函数定义完成!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 测试过滤函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 测试过滤函数 ===\n",
      "加载测试数据: 16元环, 1148 个分子\n",
      "\n",
      "测试结果:\n",
      "分子 1:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: True\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 2:\n",
      "  解析成功: True\n",
      "  有桥环: False\n",
      "  多酯键: False\n",
      "  有酰胺: False\n",
      "  保留: True\n",
      "\n",
      "分子 3:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: True\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 4:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: True\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 5:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: True\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 6:\n",
      "  解析成功: True\n",
      "  有桥环: False\n",
      "  多酯键: False\n",
      "  有酰胺: False\n",
      "  保留: True\n",
      "\n",
      "分子 7:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: False\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 8:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: False\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 9:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: False\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "分子 10:\n",
      "  解析成功: True\n",
      "  有桥环: True\n",
      "  多酯键: False\n",
      "  有酰胺: False\n",
      "  保留: False\n",
      "\n",
      "\n",
      "测试统计 (前50个分子):\n",
      "总分子数: 50\n",
      "SMILES解析成功: 50 (100.0%)\n",
      "含桥环: 48 (96.0%)\n",
      "环上多酯键: 4 (8.0%)\n",
      "环上有酰胺: 0 (0.0%)\n",
      "保留分子: 2 (4.0%)\n"
     ]
    }
   ],
   "source": [
    "# 测试过滤函数\n",
    "print(\"=== 测试过滤函数 ===\")\n",
    "\n",
    "# 选择一个有数据的文件进行测试\n",
    "test_ring_size = 16  # 测试16元环\n",
    "if test_ring_size in input_files:\n",
    "    test_df = pd.read_csv(input_files[test_ring_size])\n",
    "    print(f\"加载测试数据: {test_ring_size}元环, {len(test_df)} 个分子\")\n",
    "    \n",
    "    # 测试前10个分子\n",
    "    test_molecules = test_df.head(10)\n",
    "    \n",
    "    print(\"\\n测试结果:\")\n",
    "    for idx, row in test_molecules.iterrows():\n",
    "        smiles = row['smiles']\n",
    "        result = filter_molecule(smiles, test_ring_size)\n",
    "        \n",
    "        print(f\"分子 {idx+1}:\")\n",
    "        print(f\"  解析成功: {result['parse_success']}\")\n",
    "        print(f\"  有桥环: {result['has_bridged_ring']}\")\n",
    "        print(f\"  多酯键: {result['has_multiple_lactones']}\")\n",
    "        print(f\"  有酰胺: {result['has_amide']}\")\n",
    "        print(f\"  保留: {result['should_keep']}\")\n",
    "        print()\n",
    "    \n",
    "    # 统计测试结果\n",
    "    test_results = []\n",
    "    for smiles in test_df['smiles'].head(50):  # 测试50个分子\n",
    "        result = filter_molecule(smiles, test_ring_size)\n",
    "        test_results.append(result)\n",
    "    \n",
    "    total_test = len(test_results)\n",
    "    parse_success = sum(1 for r in test_results if r['parse_success'])\n",
    "    bridged_count = sum(1 for r in test_results if r['has_bridged_ring'])\n",
    "    multiple_lactones_count = sum(1 for r in test_results if r['has_multiple_lactones'])\n",
    "    amide_count = sum(1 for r in test_results if r['has_amide'])\n",
    "    kept_count = sum(1 for r in test_results if r['should_keep'])\n",
    "    \n",
    "    print(f\"\\n测试统计 (前50个分子):\")\n",
    "    print(f\"总分子数: {total_test}\")\n",
    "    print(f\"SMILES解析成功: {parse_success} ({parse_success/total_test*100:.1f}%)\")\n",
    "    print(f\"含桥环: {bridged_count} ({bridged_count/total_test*100:.1f}%)\")\n",
    "    print(f\"环上多酯键: {multiple_lactones_count} ({multiple_lactones_count/total_test*100:.1f}%)\")\n",
    "    print(f\"环上有酰胺: {amide_count} ({amide_count/total_test*100:.1f}%)\")\n",
    "    print(f\"保留分子: {kept_count} ({kept_count/total_test*100:.1f}%)\")\n",
    "    \n",
    "else:\n",
    "    print(f\"没有找到 {test_ring_size}元环的测试文件\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 批量过滤处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 批量过滤处理 ===\n",
      "并行进程数: 32\n",
      "批处理大小: 1000\n",
      "处理环大小: [12, 13, 14, 15, 16, 17, 18, 19, 20]\n",
      "\n",
      "处理 12元环数据...\n",
      "  原始分子数: 501\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 501\n",
      "    解析成功: 501 (100.0%)\n",
      "    桥环分子: 479 (95.6% of parsed)\n",
      "    多酯键分子: 245 (48.9% of parsed)\n",
      "    酰胺键分子: 6 (1.2% of parsed)\n",
      "    保留分子: 11 (2.2% of total)\n",
      "    过滤分子: 490 (97.8% of total)\n",
      "    保存文件: macrolactone_ring12_filtered.csv\n",
      "\n",
      "处理 13元环数据...\n",
      "  原始分子数: 600\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 600\n",
      "    解析成功: 600 (100.0%)\n",
      "    桥环分子: 583 (97.2% of parsed)\n",
      "    多酯键分子: 13 (2.2% of parsed)\n",
      "    酰胺键分子: 7 (1.2% of parsed)\n",
      "    保留分子: 6 (1.0% of total)\n",
      "    过滤分子: 594 (99.0% of total)\n",
      "    保存文件: macrolactone_ring13_filtered.csv\n",
      "\n",
      "处理 14元环数据...\n",
      "  原始分子数: 2486\n",
      "  分为 3 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 2486\n",
      "    解析成功: 2486 (100.0%)\n",
      "    桥环分子: 2270 (91.3% of parsed)\n",
      "    多酯键分子: 54 (2.2% of parsed)\n",
      "    酰胺键分子: 5 (0.2% of parsed)\n",
      "    保留分子: 202 (8.1% of total)\n",
      "    过滤分子: 2284 (91.9% of total)\n",
      "    保存文件: macrolactone_ring14_filtered.csv\n",
      "\n",
      "处理 15元环数据...\n",
      "  原始分子数: 135\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 135\n",
      "    解析成功: 135 (100.0%)\n",
      "    桥环分子: 40 (29.6% of parsed)\n",
      "    多酯键分子: 43 (31.9% of parsed)\n",
      "    酰胺键分子: 8 (5.9% of parsed)\n",
      "    保留分子: 79 (58.5% of total)\n",
      "    过滤分子: 56 (41.5% of total)\n",
      "    保存文件: macrolactone_ring15_filtered.csv\n",
      "\n",
      "处理 16元环数据...\n",
      "  原始分子数: 1148\n",
      "  分为 2 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 1148\n",
      "    解析成功: 1148 (100.0%)\n",
      "    桥环分子: 893 (77.8% of parsed)\n",
      "    多酯键分子: 57 (5.0% of parsed)\n",
      "    酰胺键分子: 30 (2.6% of parsed)\n",
      "    保留分子: 223 (19.4% of total)\n",
      "    过滤分子: 925 (80.6% of total)\n",
      "    保存文件: macrolactone_ring16_filtered.csv\n",
      "\n",
      "处理 17元环数据...\n",
      "  原始分子数: 52\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 52\n",
      "    解析成功: 52 (100.0%)\n",
      "    桥环分子: 27 (51.9% of parsed)\n",
      "    多酯键分子: 29 (55.8% of parsed)\n",
      "    酰胺键分子: 6 (11.5% of parsed)\n",
      "    保留分子: 13 (25.0% of total)\n",
      "    过滤分子: 39 (75.0% of total)\n",
      "    保存文件: macrolactone_ring17_filtered.csv\n",
      "\n",
      "处理 18元环数据...\n",
      "  原始分子数: 152\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 152\n",
      "    解析成功: 152 (100.0%)\n",
      "    桥环分子: 89 (58.6% of parsed)\n",
      "    多酯键分子: 66 (43.4% of parsed)\n",
      "    酰胺键分子: 60 (39.5% of parsed)\n",
      "    保留分子: 28 (18.4% of total)\n",
      "    过滤分子: 124 (81.6% of total)\n",
      "    保存文件: macrolactone_ring18_filtered.csv\n",
      "\n",
      "处理 19元环数据...\n",
      "  原始分子数: 77\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 77\n",
      "    解析成功: 77 (100.0%)\n",
      "    桥环分子: 71 (92.2% of parsed)\n",
      "    多酯键分子: 11 (14.3% of parsed)\n",
      "    酰胺键分子: 55 (71.4% of parsed)\n",
      "    保留分子: 0 (0.0% of total)\n",
      "    过滤分子: 77 (100.0% of total)\n",
      "    保存文件: macrolactone_ring19_filtered.csv\n",
      "\n",
      "处理 20元环数据...\n",
      "  原始分子数: 9\n",
      "  分为 1 个批次\n",
      "  过滤完成:\n",
      "    原始分子: 9\n",
      "    解析成功: 9 (100.0%)\n",
      "    桥环分子: 4 (44.4% of parsed)\n",
      "    多酯键分子: 5 (55.6% of parsed)\n",
      "    酰胺键分子: 3 (33.3% of parsed)\n",
      "    保留分子: 2 (22.2% of total)\n",
      "    过滤分子: 7 (77.8% of total)\n",
      "    保存文件: macrolactone_ring20_filtered.csv\n",
      "\n",
      "=== 所有过滤完成 ===\n",
      "总过滤时间: 3.50 秒\n",
      "处理环大小: [12, 13, 14, 15, 16, 17, 18, 19, 20]\n"
     ]
    }
   ],
   "source": [
    "# 设置并行处理参数\n",
    "N_JOBS = min(32, multiprocessing.cpu_count() - 2)  # 保留2个核心给系统\n",
    "BATCH_SIZE = 1000  # 每批处理1000个分子\n",
    "\n",
    "print(f\"=== 批量过滤处理 ===\")\n",
    "print(f\"并行进程数: {N_JOBS}\")\n",
    "print(f\"批处理大小: {BATCH_SIZE}\")\n",
    "print(f\"处理环大小: {list(input_files.keys())}\")\n",
    "\n",
    "# 存储过滤结果\n",
    "filtering_results = {}\n",
    "filtering_statistics = {}\n",
    "\n",
    "total_start_time = time.time()\n",
    "\n",
    "for ring_size, csv_file in input_files.items():\n",
    "    print(f\"\\n处理 {ring_size}元环数据...\")\n",
    "    \n",
    "    # 读取数据\n",
    "    df = pd.read_csv(csv_file)\n",
    "    print(f\"  原始分子数: {len(df)}\")\n",
    "    \n",
    "    if len(df) == 0:\n",
    "        print(f\"  {ring_size}元环没有数据，跳过\")\n",
    "        continue\n",
    "    \n",
    "    # 分批处理\n",
    "    smiles_list = df['smiles'].tolist()\n",
    "    molecule_batches = [smiles_list[i:i + BATCH_SIZE] for i in range(0, len(smiles_list), BATCH_SIZE)]\n",
    "    \n",
    "    print(f\"  分为 {len(molecule_batches)} 个批次\")\n",
    "    \n",
    "    # 并行过滤\n",
    "    batch_results = []\n",
    "    for batch_idx, batch in enumerate(molecule_batches):\n",
    "        if (batch_idx + 1) % 10 == 0:\n",
    "            print(f\"    处理批次 {batch_idx + 1}/{len(molecule_batches)}\")\n",
    "        \n",
    "        # 并行处理当前批次\n",
    "        results = Parallel(\n",
    "            n_jobs=N_JOBS,\n",
    "            backend='loky',\n",
    "            verbose=0\n",
    "        )(\n",
    "            delayed(filter_molecule)(smiles, ring_size)\n",
    "            for smiles in batch\n",
    "        )\n",
    "        batch_results.extend(results)\n",
    "    \n",
    "    # 统计过滤结果\n",
    "    total_molecules = len(batch_results)\n",
    "    parse_success = sum(1 for r in batch_results if r['parse_success'])\n",
    "    bridged_count = sum(1 for r in batch_results if r['has_bridged_ring'])\n",
    "    multiple_lactones_count = sum(1 for r in batch_results if r['has_multiple_lactones'])\n",
    "    amide_count = sum(1 for r in batch_results if r['has_amide'])\n",
    "    kept_count = sum(1 for r in batch_results if r['should_keep'])\n",
    "    \n",
    "    # 创建过滤后的DataFrame\n",
    "    filtered_indices = [i for i, r in enumerate(batch_results) if r['should_keep']]\n",
    "    filtered_df = df.iloc[filtered_indices].copy()\n",
    "    \n",
    "    # 添加过滤信息到DataFrame\n",
    "    df['filter_parse_success'] = [r['parse_success'] for r in batch_results]\n",
    "    df['filter_has_bridged_ring'] = [r['has_bridged_ring'] for r in batch_results]\n",
    "    df['filter_has_multiple_lactones'] = [r['has_multiple_lactones'] for r in batch_results]\n",
    "    df['filter_has_amide'] = [r['has_amide'] for r in batch_results]\n",
    "    df['filter_should_keep'] = [r['should_keep'] for r in batch_results]\n",
    "    \n",
    "    # 保存过滤后的数据\n",
    "    filtered_csv_file = output_dir / f'macrolactone_ring{ring_size}_filtered.csv'\n",
    "    filtered_df.to_csv(filtered_csv_file, index=False, encoding='utf-8')\n",
    "    \n",
    "    # 保存完整的过滤信息\n",
    "    detailed_csv_file = output_dir / f'macrolactone_ring{ring_size}_filtering_details.csv'\n",
    "    df.to_csv(detailed_csv_file, index=False, encoding='utf-8')\n",
    "    \n",
    "    # 记录统计信息\n",
    "    stats = {\n",
    "        'ring_size': ring_size,\n",
    "        'original_count': total_molecules,\n",
    "        'parse_success': parse_success,\n",
    "        'parse_failed': total_molecules - parse_success,\n",
    "        'bridged_count': bridged_count,\n",
    "        'multiple_lactones_count': multiple_lactones_count,\n",
    "        'amide_count': amide_count,\n",
    "        'kept_count': kept_count,\n",
    "        'filtered_count': total_molecules - kept_count,\n",
    "        'keep_rate': kept_count / total_molecules * 100 if total_molecules > 0 else 0,\n",
    "        'filter_rate': (total_molecules - kept_count) / total_molecules * 100 if total_molecules > 0 else 0\n",
    "    }\n",
    "    \n",
    "    filtering_statistics[ring_size] = stats\n",
    "    filtering_results[ring_size] = {\n",
    "        'filtered_df': filtered_df,\n",
    "        'detailed_df': df,\n",
    "        'stats': stats\n",
    "    }\n",
    "    \n",
    "    print(f\"  过滤完成:\")\n",
    "    print(f\"    原始分子: {total_molecules}\")\n",
    "    print(f\"    解析成功: {parse_success} ({parse_success/total_molecules*100:.1f}%)\")\n",
    "    print(f\"    桥环分子: {bridged_count} ({bridged_count/parse_success*100:.1f}% of parsed)\")\n",
    "    print(f\"    多酯键分子: {multiple_lactones_count} ({multiple_lactones_count/parse_success*100:.1f}% of parsed)\")\n",
    "    print(f\"    酰胺键分子: {amide_count} ({amide_count/parse_success*100:.1f}% of parsed)\")\n",
    "    print(f\"    保留分子: {kept_count} ({kept_count/total_molecules*100:.1f}% of total)\")\n",
    "    print(f\"    过滤分子: {total_molecules - kept_count} ({(total_molecules - kept_count)/total_molecules*100:.1f}% of total)\")\n",
    "    print(f\"    保存文件: {filtered_csv_file.name}\")\n",
    "    \n",
    "    # 清理内存\n",
    "    del df, filtered_df\n",
    "    gc.collect()\n",
    "\n",
    "total_filtering_time = time.time() - total_start_time\n",
    "\n",
    "print(f\"\\n=== 所有过滤完成 ===\")\n",
    "print(f\"总过滤时间: {total_filtering_time:.2f} 秒\")\n",
    "print(f\"处理环大小: {list(filtering_statistics.keys())}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 过滤统计汇总"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 过滤统计汇总 ===\n",
      "\n",
      "总体统计:\n",
      "原始分子总数: 5,160\n",
      "保留分子总数: 564\n",
      "过滤分子总数: 4,596\n",
      "总体保留率: 10.93%\n",
      "总体过滤率: 89.07%\n",
      "\n",
      "过滤原因统计:\n",
      "桥环分子: 4,456 (86.36%)\n",
      "多酯键分子: 523 (10.14%)\n",
      "酰胺键分子: 180 (3.49%)\n",
      "\n",
      "按环大小详细统计:\n",
      " ring_size  original_count  kept_count  filtered_count  keep_rate  bridged_count  multiple_lactones_count  amide_count\n",
      "        12             501          11             490   2.195609            479                      245            6\n",
      "        13             600           6             594   1.000000            583                       13            7\n",
      "        14            2486         202            2284   8.125503           2270                       54            5\n",
      "        15             135          79              56  58.518519             40                       43            8\n",
      "        16            1148         223             925  19.425087            893                       57           30\n",
      "        17              52          13              39  25.000000             27                       29            6\n",
      "        18             152          28             124  18.421053             89                       66           60\n",
      "        19              77           0              77   0.000000             71                       11           55\n",
      "        20               9           2               7  22.222222              4                        5            3\n",
      "\n",
      "统计结果已保存: /home/zly/project/search_macro/macrolactone_ring_results_filtered/filtering_statistics.csv\n",
      "总体统计已保存: /home/zly/project/search_macro/macrolactone_ring_results_filtered/overall_filtering_summary.csv\n"
     ]
    }
   ],
   "source": [
    "# 创建统计汇总\n",
    "print(\"=== 过滤统计汇总 ===\")\n",
    "\n",
    "# 计算总体统计\n",
    "total_original = sum(stats['original_count'] for stats in filtering_statistics.values())\n",
    "total_kept = sum(stats['kept_count'] for stats in filtering_statistics.values())\n",
    "total_filtered = sum(stats['filtered_count'] for stats in filtering_statistics.values())\n",
    "total_bridged = sum(stats['bridged_count'] for stats in filtering_statistics.values())\n",
    "total_multiple_lactones = sum(stats['multiple_lactones_count'] for stats in filtering_statistics.values())\n",
    "total_amide = sum(stats['amide_count'] for stats in filtering_statistics.values())\n",
    "\n",
    "print(f\"\\n总体统计:\")\n",
    "print(f\"原始分子总数: {total_original:,}\")\n",
    "print(f\"保留分子总数: {total_kept:,}\")\n",
    "print(f\"过滤分子总数: {total_filtered:,}\")\n",
    "print(f\"总体保留率: {total_kept/total_original*100:.2f}%\")\n",
    "print(f\"总体过滤率: {total_filtered/total_original*100:.2f}%\")\n",
    "\n",
    "print(f\"\\n过滤原因统计:\")\n",
    "print(f\"桥环分子: {total_bridged:,} ({total_bridged/total_original*100:.2f}%)\")\n",
    "print(f\"多酯键分子: {total_multiple_lactones:,} ({total_multiple_lactones/total_original*100:.2f}%)\")\n",
    "print(f\"酰胺键分子: {total_amide:,} ({total_amide/total_original*100:.2f}%)\")\n",
    "\n",
    "# 创建详细统计表\n",
    "stats_df = pd.DataFrame([stats for stats in filtering_statistics.values()])\n",
    "stats_df = stats_df.sort_values('ring_size')\n",
    "\n",
    "print(f\"\\n按环大小详细统计:\")\n",
    "print(stats_df[['ring_size', 'original_count', 'kept_count', 'filtered_count', \n",
    "               'keep_rate', 'bridged_count', 'multiple_lactones_count', 'amide_count']].to_string(index=False))\n",
    "\n",
    "# 保存统计结果\n",
    "stats_file = output_dir / 'filtering_statistics.csv'\n",
    "stats_df.to_csv(stats_file, index=False)\n",
    "print(f\"\\n统计结果已保存: {stats_file}\")\n",
    "\n",
    "# 保存总体统计\n",
    "overall_stats = {\n",
    "    'total_original_molecules': total_original,\n",
    "    'total_kept_molecules': total_kept,\n",
    "    'total_filtered_molecules': total_filtered,\n",
    "    'overall_keep_rate': total_kept/total_original*100,\n",
    "    'overall_filter_rate': total_filtered/total_original*100,\n",
    "    'total_bridged_molecules': total_bridged,\n",
    "    'total_multiple_lactones_molecules': total_multiple_lactones,\n",
    "    'total_amide_molecules': total_amide,\n",
    "    'total_filtering_time_seconds': total_filtering_time,\n",
    "    'parallel_jobs_used': N_JOBS,\n",
    "    'batch_size': BATCH_SIZE,\n",
    "    'processed_ring_sizes': list(filtering_statistics.keys())\n",
    "}\n",
    "\n",
    "overall_df = pd.DataFrame([overall_stats])\n",
    "overall_file = output_dir / 'overall_filtering_summary.csv'\n",
    "overall_df.to_csv(overall_file, index=False)\n",
    "print(f\"总体统计已保存: {overall_file}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 生成README文档"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 生成详细的README文档 - 修复版本\n",
    "readme_content = f\"\"\"\n",
    "# 大环内酯分子过滤结果\n",
    "\n",
    "## 🎯 过滤目标\n",
    "\n",
    "对12-20元环大环内酯分子进行精确过滤，去除不符合药物开发要求的分子结构：\n",
    "\n",
    "### 过滤标准\n",
    "1. **桥环分子过滤**：去除含有桥环结构的分子\n",
    "   - 检测：原子属于多个环，且至少有一个是目标大小的环\n",
    "   - 原因：桥环结构可能影响药物代谢和稳定性\n",
    "\n",
    "2. **多酯键分子过滤**：去除环上有多个内酯键的分子\n",
    "   - 检测：环上有多个 [r{{ring_size}};O][r{{ring_size}};C](=[#8]) 结构\n",
    "   - 原因：多个酯键可能增加水解不稳定性\n",
    "\n",
    "3. **酰胺键分子过滤**：去除环上含有酰胺键的分子\n",
    "   - 检测：环上有 [r{{ring_size}};N][r{{ring_size}};C](=[#8]) 或 [r{{ring_size}};C](=[#8])[r{{ring_size}};N] 结构\n",
    "   - 原因：酰胺键可能改变分子的药理性质\n",
    "\n",
    "## 📊 过滤结果统计\n",
    "\n",
    "### 总体统计\n",
    "- **原始分子总数**: {total_original:,}\n",
    "- **保留分子总数**: {total_kept:,}\n",
    "- **过滤分子总数**: {total_filtered:,}\n",
    "- **总体保留率**: {total_kept/total_original*100:.2f}%\n",
    "- **总体过滤率**: {total_filtered/total_original*100:.2f}%\n",
    "- **处理时间**: {total_filtering_time:.2f} 秒\n",
    "\n",
    "### 过滤原因分布\n",
    "| 过滤原因 | 分子数量 | 占比 |\n",
    "|----------|----------|------|\n",
    "| 桥环结构 | {total_bridged:,} | {total_bridged/total_original*100:.2f}% |\n",
    "| 多酯键 | {total_multiple_lactones:,} | {total_multiple_lactones/total_original*100:.2f}% |\n",
    "| 酰胺键 | {total_amide:,} | {total_amide/total_original*100:.2f}% |\n",
    "\n",
    "### 按环大小详细统计\n",
    "\n",
    "| 环大小 | 原始数量 | 保留数量 | 过滤数量 | 保留率 | 桥环数 | 多酯键数 | 酰胺键数 |\n",
    "|--------|----------|----------|----------|--------|--------|----------|----------|\n",
    "\"\"\"\n",
    "\n",
    "# 添加按环大小的统计\n",
    "for ring_size in sorted(filtering_statistics.keys()):\n",
    "    stats = filtering_statistics[ring_size]\n",
    "    readme_content += f\"| {stats['ring_size']}元环 | {stats['original_count']:,} | {stats['kept_count']:,} | {stats['filtered_count']:,} | {stats['keep_rate']:.1f}% | {stats['bridged_count']:,} | {stats['multiple_lactones_count']:,} | {stats['amide_count']:,} |\\n\"\n",
    "\n",
    "readme_content += f\"\"\"\n",
    "\n",
    "## 🔬 技术实现\n",
    "\n",
    "### 过滤算法\n",
    "#### 1. 桥环检测算法\n",
    "```python\n",
    "def has_bridged_ring(mol, target_ring_size):\n",
    "    ring_info = mol.GetRingInfo()\n",
    "    for atom in mol.GetAtoms():\n",
    "        if atom.IsInRing():\n",
    "            ring_count = ring_info.NumAtomRings(atom.GetIdx())\n",
    "            if ring_count > 1:\n",
    "                # 检查是否至少有一个是目标大小的环\n",
    "                atom_rings = ring_info.AtomRings()\n",
    "                for ring in atom_rings:\n",
    "                    if atom.GetIdx() in ring and len(ring) == target_ring_size:\n",
    "                        return True\n",
    "    return False\n",
    "```\n",
    "\n",
    "#### 2. 多酯键检测算法\n",
    "```python\n",
    "def has_multiple_lactones_on_ring(mol, target_ring_size):\n",
    "    ring_atoms = get_ring_atoms_by_size(mol, target_ring_size)\n",
    "    lactone_pattern = Chem.MolFromSmarts(f\"[r{target_ring_size};#8][r{target_ring_size};#6](=[#8])\")\n",
    "    matches = mol.GetSubstructMatches(lactone_pattern)\n",
    "    \n",
    "    lactone_carbons_on_ring = []\n",
    "    for match in matches:\n",
    "        carbonyl_carbon = match[1]  # 羰基碳\n",
    "        if carbonyl_carbon in ring_atoms:\n",
    "            lactone_carbons_on_ring.append(carbonyl_carbon)\n",
    "    \n",
    "    return len(set(lactone_carbons_on_ring)) > 1\n",
    "```\n",
    "\n",
    "#### 3. 酰胺键检测算法\n",
    "```python\n",
    "def has_amide_on_ring(mol, target_ring_size):\n",
    "    ring_atoms = get_ring_atoms_by_size(mol, target_ring_size)\n",
    "    amide_pattern1 = Chem.MolFromSmarts(f\"[r{target_ring_size};#7][r{target_ring_size};#6](=[#8])\")\n",
    "    amide_pattern2 = Chem.MolFromSmarts(f\"[r{target_ring_size};#6](=[#8])[r{target_ring_size};#7]\")\n",
    "    \n",
    "    matches1 = mol.GetSubstructMatches(amide_pattern1)\n",
    "    matches2 = mol.GetSubstructMatches(amide_pattern2)\n",
    "    \n",
    "    # 检查匹配是否在环上\n",
    "    for match in matches1 + matches2:\n",
    "        if match[0] in ring_atoms and match[1] in ring_atoms:\n",
    "            return True\n",
    "    return False\n",
    "```\n",
    "\n",
    "### 性能优化\n",
    "- **并行处理**: 使用Joblib并行，{N_JOBS} 个进程\n",
    "- **批处理**: 每批 {BATCH_SIZE} 个分子，避免内存溢出\n",
    "- **SMARTS优化**: 使用高效的SMARTS模式匹配\n",
    "- **内存管理**: 及时清理中间结果，避免内存泄漏\n",
    "\n",
    "## 📁 文件结构\n",
    "\n",
    "```\n",
    "macrolactone_ring_results_filtered/          # 过滤结果目录\n",
    "├── README.md                                 # 本文档\n",
    "├── filtering_statistics.csv                 # 详细统计表\n",
    "├── overall_filtering_summary.csv             # 总体统计摘要\n",
    "├── macrolactone_ring12_filtered.csv          # 12元环过滤结果\n",
    "├── macrolactone_ring12_filtering_details.csv # 12元环详细过滤信息\n",
    "├── macrolactone_ring13_filtered.csv          # 13元环过滤结果\n",
    "├── macrolactone_ring13_filtering_details.csv # 13元环详细过滤信息\n",
    "├── macrolactone_ring14_filtered.csv          # 14元环过滤结果\n",
    "├── macrolactone_ring14_filtering_details.csv # 14元环详细过滤信息\n",
    "├── macrolactone_ring15_filtered.csv          # 15元环过滤结果\n",
    "├── macrolactone_ring15_filtering_details.csv # 15元环详细过滤信息\n",
    "├── macrolactone_ring16_filtered.csv          # 16元环过滤结果\n",
    "├── macrolactone_ring16_filtering_details.csv # 16元环详细过滤信息\n",
    "├── macrolactone_ring17_filtered.csv          # 17元环过滤结果\n",
    "├── macrolactone_ring17_filtering_details.csv # 17元环详细过滤信息\n",
    "├── macrolactone_ring18_filtered.csv          # 18元环过滤结果\n",
    "├── macrolactone_ring18_filtering_details.csv # 18元环详细过滤信息\n",
    "├── macrolactone_ring19_filtered.csv          # 19元环过滤结果\n",
    "├── macrolactone_ring19_filtering_details.csv # 19元环详细过滤信息\n",
    "├── macrolactone_ring20_filtered.csv          # 20元环过滤结果\n",
    "├── macrolactone_ring20_filtering_details.csv # 20元环详细过滤信息\n",
    "└── generated_files_list.csv                  # 生成文件清单\n",
    "```\n",
    "\n",
    "## 💡 数据说明\n",
    "\n",
    "### 文件类型说明\n",
    "- **`*_filtered.csv`**: 过滤后的高质量分子数据\n",
    "  - 只包含通过所有过滤检查的分子\n",
    "  - 保留所有原始SDF属性\n",
    "  - 可直接用于下游药物研发\n",
    "\n",
    "- **`*_filtering_details.csv`**: 完整的过滤详细信息\n",
    "  - 包含所有原始分子\n",
    "  - 添加了过滤结果列：\n",
    "    - `filter_parse_success`: SMILES解析是否成功\n",
    "    - `filter_has_bridged_ring`: 是否有桥环结构\n",
    "    - `filter_has_multiple_lactones`: 是否有多个酯键\n",
    "    - `filter_has_amide`: 是否有酰胺键\n",
    "    - `filter_should_keep`: 是否应该保留\n",
    "\n",
    "### 数据质量保证\n",
    "- ✅ **SMILES验证**: 所有分子SMILES格式正确\n",
    "- ✅ **环大小验证**: 确保分子含有指定大小的环\n",
    "- ✅ **大环内酯验证**: 确保环上有酯键结构\n",
    "- ✅ **结构纯度**: 去除桥环、多酯键、酰胺键等复杂结构\n",
    "- ✅ **属性完整性**: 保留所有原始SDF属性信息\n",
    "\n",
    "## 🏢 应用价值\n",
    "\n",
    "### 药物研发优势\n",
    "1. **结构简化**: 去除复杂结构，提高药物可开发性\n",
    "2. **稳定性提升**: 单酯键结构更稳定，减少水解风险\n",
    "3. **合成友好**: 简单环结构更容易合成和修饰\n",
    "4. **代谢优化**: 避免桥环和酰胺键的代谢复杂性\n",
    "\n",
    "### 数据可用性\n",
    "- **商业价值**: 所有分子均来自上海陶术公司，可采购\n",
    "- **研究价值**: 适用于大环内酯类药物研究\n",
    " - **开发价值**: 结构优化，适合药物开发项目\n",
    "\n",
    "## 🚀 使用指南\n",
    "\n",
    "### 快速开始\n",
    "```python\n",
    "import pandas as pd\n",
    "from rdkit import Chem\n",
    "\n",
    "# 加载过滤后的16元环数据\n",
    "filtered_16 = pd.read_csv('macrolactone_ring16_filtered.csv')\n",
    "print(f\"过滤后16元环分子数: {{len(filtered_16)}}\")\n",
    "\n",
    "# 查看分子属性\n",
    "print(f\"列名: {{filtered_16.columns.tolist()}}\")\n",
    "\n",
    "# 重建分子对象\n",
    "smiles = filtered_16.iloc[0]['smiles']\n",
    "mol = Chem.MolFromSmiles(smiles)\n",
    "print(f\"分子原子数: {{mol.GetNumAtoms()}}\")\n",
    "```\n",
    "\n",
    "### 过滤质量分析\n",
    "```python\n",
    "# 加载详细过滤信息\n",
    "details = pd.read_csv('macrolactone_ring16_filtering_details.csv')\n",
    "\n",
    "# 分析过滤原因\n",
    "print(\"过滤原因统计:\")\n",
    "print(f\"解析失败: {{details['filter_parse_success'].value_counts().get(False, 0)}}\")\n",
    "print(f\"桥环分子: {{details['filter_has_bridged_ring'].sum()}}\")\n",
    "print(f\"多酯键分子: {{details['filter_has_multiple_lactones'].sum()}}\")\n",
    "print(f\"酰胺键分子: {{details['filter_has_amide'].sum()}}\")\n",
    "print(f\"保留分子: {{details['filter_should_keep'].sum()}}\")\n",
    "```\n",
    "\n",
    "## 📞 技术支持\n",
    "\n",
    "- **数据提供商**: 上海陶术公司\n",
    "- **官网**: https://www.targetmol.com/\n",
    "- **技术文档**: 参考项目根目录下的其他分析文档\n",
    "\n",
    "---\n",
    "\n",
    "*生成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}*\n",
    "*过滤版本: v1.0 - 桥环/多酯键/酰胺键过滤*\n",
    "*处理工具: RDKit + Joblib + Python 3.14*\n",
    "*数据来源: macrolactone_ring_results_complete/*\n",
    "*算法: SMARTS模式匹配 + 环分析*\n",
    "\"\"\"\n",
    "\n",
    "# 保存README文件\n",
    "readme_file = output_dir / 'README.md'\n",
    "with open(readme_file, 'w', encoding='utf-8') as f:\n",
    "    f.write(readme_content)\n",
    "\n",
    "# 生成文件清单\n",
    "files_list = []\n",
    "for file in output_dir.iterdir():\n",
    "    if file.is_file():\n",
    "        files_list.append({\n",
    "            'filename': file.name,\n",
    "            'file_size_kb': file.stat().st_size / 1024,\n",
    "            'file_type': file.suffix,\n",
    "            'description': file.name.replace('_', ' ').replace('.csv', '').title()\n",
    "        })\n",
    "\n",
    "files_df = pd.DataFrame(files_list)\n",
    "files_file = output_dir / 'generated_files_list.csv'\n",
    "files_df.to_csv(files_file, index=False)\n",
    "\n",
    "print(f\"✅ README文档生成完成: {readme_file}\")\n",
    "print(f\"✅ 文件清单生成完成: {files_file}\")\n",
    "print(f\"\\n🎯 过滤处理总结:\")\n",
    "print(f\"  处理环大小: {list(filtering_statistics.keys())}\")\n",
    "print(f\"  原始分子: {total_original:,}\")\n",
    "print(f\"  保留分子: {total_kept:,}\")\n",
    "print(f\"  过滤分子: {total_filtered:,}\")\n",
    "print(f\"  保留率: {total_kept/total_original*100:.2f}%\")\n",
    "print(f\"  处理时间: {total_filtering_time:.2f} 秒\")\n",
    "print(f\"\\n✅ 过滤处理完成! 结果保存在: {output_dir}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "default",
   "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.14.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
