[{"data":1,"prerenderedAt":2341},["ShallowReactive",2],{"article-ai\u002Fspatial-transcriptomics":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"tags":11,"body":14,"_type":2335,"_id":2336,"_source":2337,"_file":2338,"_stem":2339,"_extension":2340},"\u002Farticles\u002Fai\u002Fspatial-transcriptomics","ai",false,"","Squidpy Visium 空间转录组入门实战","用 Squidpy 跑通 10x Visium H&E 示例数据，构建空间邻接图，完成邻域富集分析与 Moran's I 空间可变基因检测，并输出结构化质量报告。","2026-07-03",[12,13],"生物信息学","AI制药",{"type":15,"children":16,"toc":2324},"root",[17,26,61,66,72,100,111,125,131,145,170,423,429,442,447,526,754,760,765,770,795,905,911,916,929,934,955,1183,1189,1194,1208,1213,1431,1437,1442,1456,1461,1466,1471,1828,1834,1839,2246,2252,2257,2271,2276,2290,2295,2318],{"type":18,"tag":19,"props":20,"children":22},"element","h1",{"id":21},"_01-squidpy-visium-hello",[23],{"type":24,"value":25},"text","01 - Squidpy Visium Hello",{"type":18,"tag":27,"props":28,"children":29},"blockquote",{},[30,36,41],{"type":18,"tag":31,"props":32,"children":33},"p",{},[34],{"type":24,"value":35},"目标：用 Squidpy 跑通一个 10x Visium H&E 示例数据，把「空间坐标 + 表达矩阵 + 组织图像」连起来。",{"type":18,"tag":31,"props":37,"children":38},{},[39],{"type":24,"value":40},"这不是追求漂亮图，而是练三件事：",{"type":18,"tag":42,"props":43,"children":44},"ol",{},[45,51,56],{"type":18,"tag":46,"props":47,"children":48},"li",{},[49],{"type":24,"value":50},"看懂 AnnData 里空间信息放在哪里。",{"type":18,"tag":46,"props":52,"children":53},{},[54],{"type":24,"value":55},"构建空间邻接图，并做邻域富集分析。",{"type":18,"tag":46,"props":57,"children":58},{},[59],{"type":24,"value":60},"把结果整理成后续 Agent tool 能返回的结构化报告。",{"type":18,"tag":31,"props":62,"children":63},{},[64],{"type":24,"value":65},"第一次运行会下载 Squidpy 官方示例数据，体积约数百 MB。",{"type":18,"tag":67,"props":68,"children":70},"h2",{"id":69},"总结",[71],{"type":24,"value":69},{"type":18,"tag":42,"props":73,"children":74},{},[75,80,85,90,95],{"type":18,"tag":46,"props":76,"children":77},{},[78],{"type":24,"value":79},"空间转录组是在每个空间 spot 上测基因表达。",{"type":18,"tag":46,"props":81,"children":82},{},[83],{"type":24,"value":84},"先根据表达模式把 spot 聚成 cluster，再结合真实空间坐标看这些 cluster 在组织里怎么分布。",{"type":18,"tag":46,"props":86,"children":87},{},[88],{"type":24,"value":89},"空间相邻富集看不同 cluster 是否异常靠近；",{"type":18,"tag":46,"props":91,"children":92},{},[93],{"type":24,"value":94},"空间可变基因看某些基因的表达是否在空间上成片分布。",{"type":18,"tag":46,"props":96,"children":97},{},[98],{"type":24,"value":99},"这些结果都是候选线索，不是机制验证。",{"type":18,"tag":31,"props":101,"children":102},{},[103,109],{"type":18,"tag":104,"props":105,"children":106},"strong",{},[107],{"type":24,"value":108},"自己理解",{"type":24,"value":110},"：数据形状 spot × gene的意思是测了spot个区域，每个区域有gene种基因；之后对所有spot进行分类，分成若干和cluster；空间相邻富集是分析出每两个cluster之间是否比随机概率更容易相邻；空间可变基因分析是判断某些基因的表达是否与空间区域有关。",{"type":18,"tag":112,"props":113,"children":114},"ul",{},[115,120],{"type":18,"tag":46,"props":116,"children":117},{},[118],{"type":24,"value":119},"空间相邻富集对AI制药的作用：判断某些细胞群\u002F组织区域是否在空间上形成了特殊关系。",{"type":18,"tag":46,"props":121,"children":122},{},[123],{"type":24,"value":124},"空间可变基因对AI制药的作用：把“高表达基因”升级成“在关键空间区域高表达的候选靶点”。",{"type":18,"tag":67,"props":126,"children":128},{"id":127},"_0-导入依赖",[129],{"type":24,"value":130},"0. 导入依赖",{"type":18,"tag":31,"props":132,"children":133},{},[134,136,143],{"type":24,"value":135},"这里用 ",{"type":18,"tag":137,"props":138,"children":140},"code",{"className":139},[],[141],{"type":24,"value":142},"scanpy + squidpy",{"type":24,"value":144},"：",{"type":18,"tag":112,"props":146,"children":147},{},[148,159],{"type":18,"tag":46,"props":149,"children":150},{},[151,157],{"type":18,"tag":137,"props":152,"children":154},{"className":153},[],[155],{"type":24,"value":156},"scanpy",{"type":24,"value":158}," 负责单细胞\u002F表达矩阵的常规处理。",{"type":18,"tag":46,"props":160,"children":161},{},[162,168],{"type":18,"tag":137,"props":163,"children":165},{"className":164},[],[166],{"type":24,"value":167},"squidpy",{"type":24,"value":169}," 负责空间坐标、空间邻接图、空间可视化和空间统计。",{"type":18,"tag":171,"props":172,"children":176},"pre",{"className":173,"code":174,"language":175,"meta":7,"style":7},"language-python shiki shiki-themes github-dark","from pathlib import Path\n\n# 【依赖分工】\n# pathlib：只负责路径管理，避免在代码里到处手写字符串路径。\n# matplotlib\u002Fpandas：辅助画图和表格整理，不承担空间组学算法。\n# scanpy：处理 AnnData、表达矩阵、基础单细胞流程。\n# squidpy：在 scanpy 的 AnnData 之上增加空间组学能力，例如空间坐标、邻接图、邻域富集。\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport scanpy as sc\nimport squidpy as sq\n\n# 【工程习惯】\n# notebook 很容易把图片、缓存、临时文件散落到当前目录。\n# 先统一定义输出目录，后面封装成 Agent tool 时也能把所有 artifact 路径集中返回。\nOUTPUT_DIR = Path(\"outputs\")\nOUTPUT_DIR.mkdir(exist_ok=True)\n\n# 【可复现性】\n# 固定图像大小和 dpi，不是为了美观，而是为了让每次运行产出的图尺寸稳定。\n# 将来如果 Agent 把图路径写进报告，稳定尺寸能减少“同一段代码每次输出都不一样”的调试成本。\nsc.settings.set_figure_params(figsize=(6, 5), dpi=120)\n\n# 【环境观察 Observation】\n# 先打印库版本。空间组学库 API 变化较快，版本号是排查问题的第一条证据。\nprint(\"scanpy\", sc.__version__)\nprint(\"squidpy\", sq.__version__)\n\n","python",[177],{"type":18,"tag":137,"props":178,"children":179},{"__ignoreMap":7},[180,191,201,210,219,228,237,246,255,264,273,282,290,299,308,317,326,335,343,352,361,370,379,387,396,405,414],{"type":18,"tag":181,"props":182,"children":185},"span",{"class":183,"line":184},"line",1,[186],{"type":18,"tag":181,"props":187,"children":188},{},[189],{"type":24,"value":190},"from pathlib import Path\n",{"type":18,"tag":181,"props":192,"children":194},{"class":183,"line":193},2,[195],{"type":18,"tag":181,"props":196,"children":198},{"emptyLinePlaceholder":197},true,[199],{"type":24,"value":200},"\n",{"type":18,"tag":181,"props":202,"children":204},{"class":183,"line":203},3,[205],{"type":18,"tag":181,"props":206,"children":207},{},[208],{"type":24,"value":209},"# 【依赖分工】\n",{"type":18,"tag":181,"props":211,"children":213},{"class":183,"line":212},4,[214],{"type":18,"tag":181,"props":215,"children":216},{},[217],{"type":24,"value":218},"# pathlib：只负责路径管理，避免在代码里到处手写字符串路径。\n",{"type":18,"tag":181,"props":220,"children":222},{"class":183,"line":221},5,[223],{"type":18,"tag":181,"props":224,"children":225},{},[226],{"type":24,"value":227},"# matplotlib\u002Fpandas：辅助画图和表格整理，不承担空间组学算法。\n",{"type":18,"tag":181,"props":229,"children":231},{"class":183,"line":230},6,[232],{"type":18,"tag":181,"props":233,"children":234},{},[235],{"type":24,"value":236},"# scanpy：处理 AnnData、表达矩阵、基础单细胞流程。\n",{"type":18,"tag":181,"props":238,"children":240},{"class":183,"line":239},7,[241],{"type":18,"tag":181,"props":242,"children":243},{},[244],{"type":24,"value":245},"# squidpy：在 scanpy 的 AnnData 之上增加空间组学能力，例如空间坐标、邻接图、邻域富集。\n",{"type":18,"tag":181,"props":247,"children":249},{"class":183,"line":248},8,[250],{"type":18,"tag":181,"props":251,"children":252},{},[253],{"type":24,"value":254},"import matplotlib.pyplot as plt\n",{"type":18,"tag":181,"props":256,"children":258},{"class":183,"line":257},9,[259],{"type":18,"tag":181,"props":260,"children":261},{},[262],{"type":24,"value":263},"import pandas as pd\n",{"type":18,"tag":181,"props":265,"children":267},{"class":183,"line":266},10,[268],{"type":18,"tag":181,"props":269,"children":270},{},[271],{"type":24,"value":272},"import scanpy as sc\n",{"type":18,"tag":181,"props":274,"children":276},{"class":183,"line":275},11,[277],{"type":18,"tag":181,"props":278,"children":279},{},[280],{"type":24,"value":281},"import squidpy as sq\n",{"type":18,"tag":181,"props":283,"children":285},{"class":183,"line":284},12,[286],{"type":18,"tag":181,"props":287,"children":288},{"emptyLinePlaceholder":197},[289],{"type":24,"value":200},{"type":18,"tag":181,"props":291,"children":293},{"class":183,"line":292},13,[294],{"type":18,"tag":181,"props":295,"children":296},{},[297],{"type":24,"value":298},"# 【工程习惯】\n",{"type":18,"tag":181,"props":300,"children":302},{"class":183,"line":301},14,[303],{"type":18,"tag":181,"props":304,"children":305},{},[306],{"type":24,"value":307},"# notebook 很容易把图片、缓存、临时文件散落到当前目录。\n",{"type":18,"tag":181,"props":309,"children":311},{"class":183,"line":310},15,[312],{"type":18,"tag":181,"props":313,"children":314},{},[315],{"type":24,"value":316},"# 先统一定义输出目录，后面封装成 Agent tool 时也能把所有 artifact 路径集中返回。\n",{"type":18,"tag":181,"props":318,"children":320},{"class":183,"line":319},16,[321],{"type":18,"tag":181,"props":322,"children":323},{},[324],{"type":24,"value":325},"OUTPUT_DIR = Path(\"outputs\")\n",{"type":18,"tag":181,"props":327,"children":329},{"class":183,"line":328},17,[330],{"type":18,"tag":181,"props":331,"children":332},{},[333],{"type":24,"value":334},"OUTPUT_DIR.mkdir(exist_ok=True)\n",{"type":18,"tag":181,"props":336,"children":338},{"class":183,"line":337},18,[339],{"type":18,"tag":181,"props":340,"children":341},{"emptyLinePlaceholder":197},[342],{"type":24,"value":200},{"type":18,"tag":181,"props":344,"children":346},{"class":183,"line":345},19,[347],{"type":18,"tag":181,"props":348,"children":349},{},[350],{"type":24,"value":351},"# 【可复现性】\n",{"type":18,"tag":181,"props":353,"children":355},{"class":183,"line":354},20,[356],{"type":18,"tag":181,"props":357,"children":358},{},[359],{"type":24,"value":360},"# 固定图像大小和 dpi，不是为了美观，而是为了让每次运行产出的图尺寸稳定。\n",{"type":18,"tag":181,"props":362,"children":364},{"class":183,"line":363},21,[365],{"type":18,"tag":181,"props":366,"children":367},{},[368],{"type":24,"value":369},"# 将来如果 Agent 把图路径写进报告，稳定尺寸能减少“同一段代码每次输出都不一样”的调试成本。\n",{"type":18,"tag":181,"props":371,"children":373},{"class":183,"line":372},22,[374],{"type":18,"tag":181,"props":375,"children":376},{},[377],{"type":24,"value":378},"sc.settings.set_figure_params(figsize=(6, 5), dpi=120)\n",{"type":18,"tag":181,"props":380,"children":382},{"class":183,"line":381},23,[383],{"type":18,"tag":181,"props":384,"children":385},{"emptyLinePlaceholder":197},[386],{"type":24,"value":200},{"type":18,"tag":181,"props":388,"children":390},{"class":183,"line":389},24,[391],{"type":18,"tag":181,"props":392,"children":393},{},[394],{"type":24,"value":395},"# 【环境观察 Observation】\n",{"type":18,"tag":181,"props":397,"children":399},{"class":183,"line":398},25,[400],{"type":18,"tag":181,"props":401,"children":402},{},[403],{"type":24,"value":404},"# 先打印库版本。空间组学库 API 变化较快，版本号是排查问题的第一条证据。\n",{"type":18,"tag":181,"props":406,"children":408},{"class":183,"line":407},26,[409],{"type":18,"tag":181,"props":410,"children":411},{},[412],{"type":24,"value":413},"print(\"scanpy\", sc.__version__)\n",{"type":18,"tag":181,"props":415,"children":417},{"class":183,"line":416},27,[418],{"type":18,"tag":181,"props":419,"children":420},{},[421],{"type":24,"value":422},"print(\"squidpy\", sq.__version__)\n",{"type":18,"tag":67,"props":424,"children":426},{"id":425},"_1-读取-visium-示例数据",[427],{"type":24,"value":428},"1. 读取 Visium 示例数据",{"type":18,"tag":31,"props":430,"children":431},{},[432,434,440],{"type":24,"value":433},"Squidpy 官方教程使用 ",{"type":18,"tag":137,"props":435,"children":437},{"className":436},[],[438],{"type":24,"value":439},"visium_hne_adata()",{"type":24,"value":441},"，这是一个预处理好的 10x Genomics Visium H&E 数据。",{"type":18,"tag":31,"props":443,"children":444},{},[445],{"type":24,"value":446},"重点看三类对象：",{"type":18,"tag":448,"props":449,"children":450},"table",{},[451,470],{"type":18,"tag":452,"props":453,"children":454},"thead",{},[455],{"type":18,"tag":456,"props":457,"children":458},"tr",{},[459,465],{"type":18,"tag":460,"props":461,"children":462},"th",{},[463],{"type":24,"value":464},"位置",{"type":18,"tag":460,"props":466,"children":467},{},[468],{"type":24,"value":469},"含义",{"type":18,"tag":471,"props":472,"children":473},"tbody",{},[474,492,509],{"type":18,"tag":456,"props":475,"children":476},{},[477,487],{"type":18,"tag":478,"props":479,"children":480},"td",{},[481],{"type":18,"tag":137,"props":482,"children":484},{"className":483},[],[485],{"type":24,"value":486},"adata.X",{"type":18,"tag":478,"props":488,"children":489},{},[490],{"type":24,"value":491},"spot × gene 表达矩阵",{"type":18,"tag":456,"props":493,"children":494},{},[495,504],{"type":18,"tag":478,"props":496,"children":497},{},[498],{"type":18,"tag":137,"props":499,"children":501},{"className":500},[],[502],{"type":24,"value":503},"adata.obsm[\"spatial\"]",{"type":18,"tag":478,"props":505,"children":506},{},[507],{"type":24,"value":508},"每个 spot 的组织切片坐标",{"type":18,"tag":456,"props":510,"children":511},{},[512,521],{"type":18,"tag":478,"props":513,"children":514},{},[515],{"type":18,"tag":137,"props":516,"children":518},{"className":517},[],[519],{"type":24,"value":520},"adata.obs[\"cluster\"]",{"type":18,"tag":478,"props":522,"children":523},{},[524],{"type":24,"value":525},"官方预注释的空间\u002F表达聚类标签",{"type":18,"tag":171,"props":527,"children":529},{"className":173,"code":528,"language":175,"meta":7,"style":7},"# 【输入数据】\n# 这里用 Squidpy 官方自带的 10x Visium H&E 示例数据。\n# 它适合入门，因为表达矩阵、空间坐标、组织图像和 cluster 标签都已经整理在 AnnData 里。\n# 第一次运行可能会下载数据；这一步相当于未来 tool 的 data loader。\nadata = sq.datasets.visium_hne_adata()\n\n# 【基因名去重】\n# AnnData 要求 var_names 最好唯一。重复基因名会让后续按基因取表达值时产生歧义。\n# 这一步不是生物学分析，而是数据工程层面的“输入清洗”。\nadata.var_names_make_unique()\n\n# 【分组字段】\n# cluster 是示例数据里已有的区域\u002F簇标签。\n# 后面的邻域富集不是直接比较每个 spot，而是比较“不同 cluster 是否更常相邻”。\ncluster_key = \"cluster\"\n\n# 【类型转换】\n# category 类型会告诉 scanpy\u002Fsquidpy：这是离散分组标签，不是连续数值。\n# 如果不转成 category，某些绘图或统计函数可能无法按“类别”正确处理。\nadata.obs[cluster_key] = adata.obs[cluster_key].astype(\"category\")\n\n# 【先观察输入，不急着分析】\n# Agent tool 设计里，这些打印信息未来应变成结构化返回值：n_spots、n_genes、obs\u002Fobsm keys。\n# 原因是：模型不能只看图片，必须知道自己分析的数据规模和字段是否符合预期。\nprint(\"数据形状 spot × gene:\", adata.shape)\nprint(\"obsm keys:\", list(adata.obsm.keys()))\nprint(\"obs columns 示例:\", list(adata.obs.columns[:10]))\nprint(\"cluster 数量:\", adata.obs[cluster_key].nunique())\n\n",[530],{"type":18,"tag":137,"props":531,"children":532},{"__ignoreMap":7},[533,541,549,557,565,573,580,588,596,604,612,619,627,635,643,651,658,666,674,682,690,697,705,713,721,729,737,745],{"type":18,"tag":181,"props":534,"children":535},{"class":183,"line":184},[536],{"type":18,"tag":181,"props":537,"children":538},{},[539],{"type":24,"value":540},"# 【输入数据】\n",{"type":18,"tag":181,"props":542,"children":543},{"class":183,"line":193},[544],{"type":18,"tag":181,"props":545,"children":546},{},[547],{"type":24,"value":548},"# 这里用 Squidpy 官方自带的 10x Visium H&E 示例数据。\n",{"type":18,"tag":181,"props":550,"children":551},{"class":183,"line":203},[552],{"type":18,"tag":181,"props":553,"children":554},{},[555],{"type":24,"value":556},"# 它适合入门，因为表达矩阵、空间坐标、组织图像和 cluster 标签都已经整理在 AnnData 里。\n",{"type":18,"tag":181,"props":558,"children":559},{"class":183,"line":212},[560],{"type":18,"tag":181,"props":561,"children":562},{},[563],{"type":24,"value":564},"# 第一次运行可能会下载数据；这一步相当于未来 tool 的 data loader。\n",{"type":18,"tag":181,"props":566,"children":567},{"class":183,"line":221},[568],{"type":18,"tag":181,"props":569,"children":570},{},[571],{"type":24,"value":572},"adata = sq.datasets.visium_hne_adata()\n",{"type":18,"tag":181,"props":574,"children":575},{"class":183,"line":230},[576],{"type":18,"tag":181,"props":577,"children":578},{"emptyLinePlaceholder":197},[579],{"type":24,"value":200},{"type":18,"tag":181,"props":581,"children":582},{"class":183,"line":239},[583],{"type":18,"tag":181,"props":584,"children":585},{},[586],{"type":24,"value":587},"# 【基因名去重】\n",{"type":18,"tag":181,"props":589,"children":590},{"class":183,"line":248},[591],{"type":18,"tag":181,"props":592,"children":593},{},[594],{"type":24,"value":595},"# AnnData 要求 var_names 最好唯一。重复基因名会让后续按基因取表达值时产生歧义。\n",{"type":18,"tag":181,"props":597,"children":598},{"class":183,"line":257},[599],{"type":18,"tag":181,"props":600,"children":601},{},[602],{"type":24,"value":603},"# 这一步不是生物学分析，而是数据工程层面的“输入清洗”。\n",{"type":18,"tag":181,"props":605,"children":606},{"class":183,"line":266},[607],{"type":18,"tag":181,"props":608,"children":609},{},[610],{"type":24,"value":611},"adata.var_names_make_unique()\n",{"type":18,"tag":181,"props":613,"children":614},{"class":183,"line":275},[615],{"type":18,"tag":181,"props":616,"children":617},{"emptyLinePlaceholder":197},[618],{"type":24,"value":200},{"type":18,"tag":181,"props":620,"children":621},{"class":183,"line":284},[622],{"type":18,"tag":181,"props":623,"children":624},{},[625],{"type":24,"value":626},"# 【分组字段】\n",{"type":18,"tag":181,"props":628,"children":629},{"class":183,"line":292},[630],{"type":18,"tag":181,"props":631,"children":632},{},[633],{"type":24,"value":634},"# cluster 是示例数据里已有的区域\u002F簇标签。\n",{"type":18,"tag":181,"props":636,"children":637},{"class":183,"line":301},[638],{"type":18,"tag":181,"props":639,"children":640},{},[641],{"type":24,"value":642},"# 后面的邻域富集不是直接比较每个 spot，而是比较“不同 cluster 是否更常相邻”。\n",{"type":18,"tag":181,"props":644,"children":645},{"class":183,"line":310},[646],{"type":18,"tag":181,"props":647,"children":648},{},[649],{"type":24,"value":650},"cluster_key = \"cluster\"\n",{"type":18,"tag":181,"props":652,"children":653},{"class":183,"line":319},[654],{"type":18,"tag":181,"props":655,"children":656},{"emptyLinePlaceholder":197},[657],{"type":24,"value":200},{"type":18,"tag":181,"props":659,"children":660},{"class":183,"line":328},[661],{"type":18,"tag":181,"props":662,"children":663},{},[664],{"type":24,"value":665},"# 【类型转换】\n",{"type":18,"tag":181,"props":667,"children":668},{"class":183,"line":337},[669],{"type":18,"tag":181,"props":670,"children":671},{},[672],{"type":24,"value":673},"# category 类型会告诉 scanpy\u002Fsquidpy：这是离散分组标签，不是连续数值。\n",{"type":18,"tag":181,"props":675,"children":676},{"class":183,"line":345},[677],{"type":18,"tag":181,"props":678,"children":679},{},[680],{"type":24,"value":681},"# 如果不转成 category，某些绘图或统计函数可能无法按“类别”正确处理。\n",{"type":18,"tag":181,"props":683,"children":684},{"class":183,"line":354},[685],{"type":18,"tag":181,"props":686,"children":687},{},[688],{"type":24,"value":689},"adata.obs[cluster_key] = adata.obs[cluster_key].astype(\"category\")\n",{"type":18,"tag":181,"props":691,"children":692},{"class":183,"line":363},[693],{"type":18,"tag":181,"props":694,"children":695},{"emptyLinePlaceholder":197},[696],{"type":24,"value":200},{"type":18,"tag":181,"props":698,"children":699},{"class":183,"line":372},[700],{"type":18,"tag":181,"props":701,"children":702},{},[703],{"type":24,"value":704},"# 【先观察输入，不急着分析】\n",{"type":18,"tag":181,"props":706,"children":707},{"class":183,"line":381},[708],{"type":18,"tag":181,"props":709,"children":710},{},[711],{"type":24,"value":712},"# Agent tool 设计里，这些打印信息未来应变成结构化返回值：n_spots、n_genes、obs\u002Fobsm keys。\n",{"type":18,"tag":181,"props":714,"children":715},{"class":183,"line":389},[716],{"type":18,"tag":181,"props":717,"children":718},{},[719],{"type":24,"value":720},"# 原因是：模型不能只看图片，必须知道自己分析的数据规模和字段是否符合预期。\n",{"type":18,"tag":181,"props":722,"children":723},{"class":183,"line":398},[724],{"type":18,"tag":181,"props":725,"children":726},{},[727],{"type":24,"value":728},"print(\"数据形状 spot × gene:\", adata.shape)\n",{"type":18,"tag":181,"props":730,"children":731},{"class":183,"line":407},[732],{"type":18,"tag":181,"props":733,"children":734},{},[735],{"type":24,"value":736},"print(\"obsm keys:\", list(adata.obsm.keys()))\n",{"type":18,"tag":181,"props":738,"children":739},{"class":183,"line":416},[740],{"type":18,"tag":181,"props":741,"children":742},{},[743],{"type":24,"value":744},"print(\"obs columns 示例:\", list(adata.obs.columns[:10]))\n",{"type":18,"tag":181,"props":746,"children":748},{"class":183,"line":747},28,[749],{"type":18,"tag":181,"props":750,"children":751},{},[752],{"type":24,"value":753},"print(\"cluster 数量:\", adata.obs[cluster_key].nunique())\n",{"type":18,"tag":67,"props":755,"children":757},{"id":756},"_2-空间可视化",[758],{"type":24,"value":759},"2. 空间可视化",{"type":18,"tag":31,"props":761,"children":762},{},[763],{"type":24,"value":764},"这一步不是 UMAP。它直接使用真实组织坐标，把每个 spot 的 cluster 画回切片位置。",{"type":18,"tag":31,"props":766,"children":767},{},[768],{"type":24,"value":769},"要记住：",{"type":18,"tag":171,"props":771,"children":775},{"className":772,"code":773,"language":774,"meta":7,"style":7},"language-plain shiki shiki-themes github-dark","UMAP = 表达相似性的二维压缩\nspatial plot = 真实组织坐标\n","plain",[776],{"type":18,"tag":137,"props":777,"children":778},{"__ignoreMap":7},[779,787],{"type":18,"tag":181,"props":780,"children":781},{"class":183,"line":184},[782],{"type":18,"tag":181,"props":783,"children":784},{},[785],{"type":24,"value":786},"UMAP = 表达相似性的二维压缩\n",{"type":18,"tag":181,"props":788,"children":789},{"class":183,"line":193},[790],{"type":18,"tag":181,"props":791,"children":792},{},[793],{"type":24,"value":794},"spatial plot = 真实组织坐标\n",{"type":18,"tag":171,"props":796,"children":798},{"className":173,"code":797,"language":175,"meta":7,"style":7},"# 【空间图，不是 UMAP】\n# spatial_scatter 会读取 adata.obsm[\"spatial\"] 里的真实组织坐标。\n# 如果 adata.uns[\"spatial\"] 里有组织切片图像，Squidpy 也可以把 spot 叠到图片背景上。\n# 这张图回答的问题是：“不同 cluster 在组织切片的哪里？”\n# 它不回答：“哪些 spot 的表达最相似？”后者才是 UMAP\u002FPCA 这类降维图的问题。\nsq.pl.spatial_scatter(adata, color=cluster_key, title=\"Visium H&E clusters\")\n\n# 【artifact 记录】\n# 现在图片显示在 notebook 里；未来封装成 Agent tool 时，最好把图片保存到文件并返回路径。\n# 这样 LLM 负责解释，Python tool 负责稳定产出证据，职责更清楚。\nfigure_paths = {\n    \"spatial_cluster\": \"notebook_inline_spatial_cluster\",\n}\n\n",[799],{"type":18,"tag":137,"props":800,"children":801},{"__ignoreMap":7},[802,810,818,826,834,842,850,857,865,873,881,889,897],{"type":18,"tag":181,"props":803,"children":804},{"class":183,"line":184},[805],{"type":18,"tag":181,"props":806,"children":807},{},[808],{"type":24,"value":809},"# 【空间图，不是 UMAP】\n",{"type":18,"tag":181,"props":811,"children":812},{"class":183,"line":193},[813],{"type":18,"tag":181,"props":814,"children":815},{},[816],{"type":24,"value":817},"# spatial_scatter 会读取 adata.obsm[\"spatial\"] 里的真实组织坐标。\n",{"type":18,"tag":181,"props":819,"children":820},{"class":183,"line":203},[821],{"type":18,"tag":181,"props":822,"children":823},{},[824],{"type":24,"value":825},"# 如果 adata.uns[\"spatial\"] 里有组织切片图像，Squidpy 也可以把 spot 叠到图片背景上。\n",{"type":18,"tag":181,"props":827,"children":828},{"class":183,"line":212},[829],{"type":18,"tag":181,"props":830,"children":831},{},[832],{"type":24,"value":833},"# 这张图回答的问题是：“不同 cluster 在组织切片的哪里？”\n",{"type":18,"tag":181,"props":835,"children":836},{"class":183,"line":221},[837],{"type":18,"tag":181,"props":838,"children":839},{},[840],{"type":24,"value":841},"# 它不回答：“哪些 spot 的表达最相似？”后者才是 UMAP\u002FPCA 这类降维图的问题。\n",{"type":18,"tag":181,"props":843,"children":844},{"class":183,"line":230},[845],{"type":18,"tag":181,"props":846,"children":847},{},[848],{"type":24,"value":849},"sq.pl.spatial_scatter(adata, color=cluster_key, title=\"Visium H&E clusters\")\n",{"type":18,"tag":181,"props":851,"children":852},{"class":183,"line":239},[853],{"type":18,"tag":181,"props":854,"children":855},{"emptyLinePlaceholder":197},[856],{"type":24,"value":200},{"type":18,"tag":181,"props":858,"children":859},{"class":183,"line":248},[860],{"type":18,"tag":181,"props":861,"children":862},{},[863],{"type":24,"value":864},"# 【artifact 记录】\n",{"type":18,"tag":181,"props":866,"children":867},{"class":183,"line":257},[868],{"type":18,"tag":181,"props":869,"children":870},{},[871],{"type":24,"value":872},"# 现在图片显示在 notebook 里；未来封装成 Agent tool 时，最好把图片保存到文件并返回路径。\n",{"type":18,"tag":181,"props":874,"children":875},{"class":183,"line":266},[876],{"type":18,"tag":181,"props":877,"children":878},{},[879],{"type":24,"value":880},"# 这样 LLM 负责解释，Python tool 负责稳定产出证据，职责更清楚。\n",{"type":18,"tag":181,"props":882,"children":883},{"class":183,"line":275},[884],{"type":18,"tag":181,"props":885,"children":886},{},[887],{"type":24,"value":888},"figure_paths = {\n",{"type":18,"tag":181,"props":890,"children":891},{"class":183,"line":284},[892],{"type":18,"tag":181,"props":893,"children":894},{},[895],{"type":24,"value":896},"    \"spatial_cluster\": \"notebook_inline_spatial_cluster\",\n",{"type":18,"tag":181,"props":898,"children":899},{"class":183,"line":292},[900],{"type":18,"tag":181,"props":901,"children":902},{},[903],{"type":24,"value":904},"}\n",{"type":18,"tag":67,"props":906,"children":908},{"id":907},"_3-构建空间邻接图",[909],{"type":24,"value":910},"3. 构建空间邻接图",{"type":18,"tag":31,"props":912,"children":913},{},[914],{"type":24,"value":915},"空间分析的关键不是“点在图上好看”，而是先定义谁和谁是邻居。",{"type":18,"tag":31,"props":917,"children":918},{},[919,921,927],{"type":24,"value":920},"Squidpy 1.8 以后推荐使用更明确的邻接构建函数。Visium 是规则网格，所以这里用 ",{"type":18,"tag":137,"props":922,"children":924},{"className":923},[],[925],{"type":24,"value":926},"spatial_neighbors_grid()",{"type":24,"value":928},"。",{"type":18,"tag":31,"props":930,"children":931},{},[932],{"type":24,"value":933},"后续分析会用到：",{"type":18,"tag":112,"props":935,"children":936},{},[937,946],{"type":18,"tag":46,"props":938,"children":939},{},[940],{"type":18,"tag":137,"props":941,"children":943},{"className":942},[],[944],{"type":24,"value":945},"adata.obsp[\"spatial_connectivities\"]",{"type":18,"tag":46,"props":947,"children":948},{},[949],{"type":18,"tag":137,"props":950,"children":952},{"className":951},[],[953],{"type":24,"value":954},"adata.obsp[\"spatial_distances\"]",{"type":18,"tag":171,"props":956,"children":958},{"className":173,"code":957,"language":175,"meta":7,"style":7},"# 【构建空间邻接图】\n# 空间分析的核心不是“ spot 有坐标”这么简单，而是要定义谁和谁是邻居。\n# Visium spot 排列近似六边形网格，所以这里使用 spatial_neighbors_grid。\n# 这一步会在 adata.obsp 中写入空间连接矩阵和距离矩阵，供后续统计函数复用。\n\n# n_neighs=6：Visium 六边形网格中，一个 spot 理论上最多有 6 个一圈邻居。\n# n_rings=1：只看第一圈邻居；如果调成 2，就会把更远的第二圈 spot 也纳入空间邻域。\n# key_added=\"spatial\"：让输出字段命名为 spatial_connectivities \u002F spatial_distances，便于后续函数默认读取。\nsq.gr.spatial_neighbors_grid(\n    adata,\n    spatial_key=\"spatial\",\n    n_neighs=6,\n    n_rings=1,\n    key_added=\"spatial\",\n)\n\n# 【邻接矩阵解释】\n# adata.obsp[\"spatial_connectivities\"] 是 spot × spot 的稀疏矩阵。\n# 某个位置非 0，表示两个 spot 被定义为空间邻居。\n# getnnz(axis=1) 统计每个 spot 有多少邻居；平均值可作为邻接图是否异常的粗略 QC 指标。\nconnectivities = adata.obsp[\"spatial_connectivities\"]\navg_neighbors = connectivities.getnnz(axis=1).mean()\n\n# 【Observation】\n# 对 Agent 来说，这些是工具调用后的观察结果。\n# 如果平均邻居数极低或极高，后续邻域富集结论就不可靠，必须进入 quality_flags。\nprint(\"obsp keys:\", list(adata.obsp.keys()))\nprint(\"平均空间邻居数:\", round(float(avg_neighbors), 2))\n\n",[959],{"type":18,"tag":137,"props":960,"children":961},{"__ignoreMap":7},[962,970,978,986,994,1001,1009,1017,1025,1033,1041,1049,1057,1065,1073,1081,1088,1096,1104,1112,1120,1128,1136,1143,1151,1159,1167,1175],{"type":18,"tag":181,"props":963,"children":964},{"class":183,"line":184},[965],{"type":18,"tag":181,"props":966,"children":967},{},[968],{"type":24,"value":969},"# 【构建空间邻接图】\n",{"type":18,"tag":181,"props":971,"children":972},{"class":183,"line":193},[973],{"type":18,"tag":181,"props":974,"children":975},{},[976],{"type":24,"value":977},"# 空间分析的核心不是“ spot 有坐标”这么简单，而是要定义谁和谁是邻居。\n",{"type":18,"tag":181,"props":979,"children":980},{"class":183,"line":203},[981],{"type":18,"tag":181,"props":982,"children":983},{},[984],{"type":24,"value":985},"# Visium spot 排列近似六边形网格，所以这里使用 spatial_neighbors_grid。\n",{"type":18,"tag":181,"props":987,"children":988},{"class":183,"line":212},[989],{"type":18,"tag":181,"props":990,"children":991},{},[992],{"type":24,"value":993},"# 这一步会在 adata.obsp 中写入空间连接矩阵和距离矩阵，供后续统计函数复用。\n",{"type":18,"tag":181,"props":995,"children":996},{"class":183,"line":221},[997],{"type":18,"tag":181,"props":998,"children":999},{"emptyLinePlaceholder":197},[1000],{"type":24,"value":200},{"type":18,"tag":181,"props":1002,"children":1003},{"class":183,"line":230},[1004],{"type":18,"tag":181,"props":1005,"children":1006},{},[1007],{"type":24,"value":1008},"# n_neighs=6：Visium 六边形网格中，一个 spot 理论上最多有 6 个一圈邻居。\n",{"type":18,"tag":181,"props":1010,"children":1011},{"class":183,"line":239},[1012],{"type":18,"tag":181,"props":1013,"children":1014},{},[1015],{"type":24,"value":1016},"# n_rings=1：只看第一圈邻居；如果调成 2，就会把更远的第二圈 spot 也纳入空间邻域。\n",{"type":18,"tag":181,"props":1018,"children":1019},{"class":183,"line":248},[1020],{"type":18,"tag":181,"props":1021,"children":1022},{},[1023],{"type":24,"value":1024},"# key_added=\"spatial\"：让输出字段命名为 spatial_connectivities \u002F spatial_distances，便于后续函数默认读取。\n",{"type":18,"tag":181,"props":1026,"children":1027},{"class":183,"line":257},[1028],{"type":18,"tag":181,"props":1029,"children":1030},{},[1031],{"type":24,"value":1032},"sq.gr.spatial_neighbors_grid(\n",{"type":18,"tag":181,"props":1034,"children":1035},{"class":183,"line":266},[1036],{"type":18,"tag":181,"props":1037,"children":1038},{},[1039],{"type":24,"value":1040},"    adata,\n",{"type":18,"tag":181,"props":1042,"children":1043},{"class":183,"line":275},[1044],{"type":18,"tag":181,"props":1045,"children":1046},{},[1047],{"type":24,"value":1048},"    spatial_key=\"spatial\",\n",{"type":18,"tag":181,"props":1050,"children":1051},{"class":183,"line":284},[1052],{"type":18,"tag":181,"props":1053,"children":1054},{},[1055],{"type":24,"value":1056},"    n_neighs=6,\n",{"type":18,"tag":181,"props":1058,"children":1059},{"class":183,"line":292},[1060],{"type":18,"tag":181,"props":1061,"children":1062},{},[1063],{"type":24,"value":1064},"    n_rings=1,\n",{"type":18,"tag":181,"props":1066,"children":1067},{"class":183,"line":301},[1068],{"type":18,"tag":181,"props":1069,"children":1070},{},[1071],{"type":24,"value":1072},"    key_added=\"spatial\",\n",{"type":18,"tag":181,"props":1074,"children":1075},{"class":183,"line":310},[1076],{"type":18,"tag":181,"props":1077,"children":1078},{},[1079],{"type":24,"value":1080},")\n",{"type":18,"tag":181,"props":1082,"children":1083},{"class":183,"line":319},[1084],{"type":18,"tag":181,"props":1085,"children":1086},{"emptyLinePlaceholder":197},[1087],{"type":24,"value":200},{"type":18,"tag":181,"props":1089,"children":1090},{"class":183,"line":328},[1091],{"type":18,"tag":181,"props":1092,"children":1093},{},[1094],{"type":24,"value":1095},"# 【邻接矩阵解释】\n",{"type":18,"tag":181,"props":1097,"children":1098},{"class":183,"line":337},[1099],{"type":18,"tag":181,"props":1100,"children":1101},{},[1102],{"type":24,"value":1103},"# adata.obsp[\"spatial_connectivities\"] 是 spot × spot 的稀疏矩阵。\n",{"type":18,"tag":181,"props":1105,"children":1106},{"class":183,"line":345},[1107],{"type":18,"tag":181,"props":1108,"children":1109},{},[1110],{"type":24,"value":1111},"# 某个位置非 0，表示两个 spot 被定义为空间邻居。\n",{"type":18,"tag":181,"props":1113,"children":1114},{"class":183,"line":354},[1115],{"type":18,"tag":181,"props":1116,"children":1117},{},[1118],{"type":24,"value":1119},"# getnnz(axis=1) 统计每个 spot 有多少邻居；平均值可作为邻接图是否异常的粗略 QC 指标。\n",{"type":18,"tag":181,"props":1121,"children":1122},{"class":183,"line":363},[1123],{"type":18,"tag":181,"props":1124,"children":1125},{},[1126],{"type":24,"value":1127},"connectivities = adata.obsp[\"spatial_connectivities\"]\n",{"type":18,"tag":181,"props":1129,"children":1130},{"class":183,"line":372},[1131],{"type":18,"tag":181,"props":1132,"children":1133},{},[1134],{"type":24,"value":1135},"avg_neighbors = connectivities.getnnz(axis=1).mean()\n",{"type":18,"tag":181,"props":1137,"children":1138},{"class":183,"line":381},[1139],{"type":18,"tag":181,"props":1140,"children":1141},{"emptyLinePlaceholder":197},[1142],{"type":24,"value":200},{"type":18,"tag":181,"props":1144,"children":1145},{"class":183,"line":389},[1146],{"type":18,"tag":181,"props":1147,"children":1148},{},[1149],{"type":24,"value":1150},"# 【Observation】\n",{"type":18,"tag":181,"props":1152,"children":1153},{"class":183,"line":398},[1154],{"type":18,"tag":181,"props":1155,"children":1156},{},[1157],{"type":24,"value":1158},"# 对 Agent 来说，这些是工具调用后的观察结果。\n",{"type":18,"tag":181,"props":1160,"children":1161},{"class":183,"line":407},[1162],{"type":18,"tag":181,"props":1163,"children":1164},{},[1165],{"type":24,"value":1166},"# 如果平均邻居数极低或极高，后续邻域富集结论就不可靠，必须进入 quality_flags。\n",{"type":18,"tag":181,"props":1168,"children":1169},{"class":183,"line":416},[1170],{"type":18,"tag":181,"props":1171,"children":1172},{},[1173],{"type":24,"value":1174},"print(\"obsp keys:\", list(adata.obsp.keys()))\n",{"type":18,"tag":181,"props":1176,"children":1177},{"class":183,"line":747},[1178],{"type":18,"tag":181,"props":1179,"children":1180},{},[1181],{"type":24,"value":1182},"print(\"平均空间邻居数:\", round(float(avg_neighbors), 2))\n",{"type":18,"tag":67,"props":1184,"children":1186},{"id":1185},"_4-邻域富集分析",[1187],{"type":24,"value":1188},"4. 邻域富集分析",{"type":18,"tag":31,"props":1190,"children":1191},{},[1192],{"type":24,"value":1193},"邻域富集回答的是：",{"type":18,"tag":171,"props":1195,"children":1197},{"className":772,"code":1196,"language":774,"meta":7,"style":7},"某两个 cluster 是否比随机情况更常挨在一起？\n",[1198],{"type":18,"tag":137,"props":1199,"children":1200},{"__ignoreMap":7},[1201],{"type":18,"tag":181,"props":1202,"children":1203},{"class":183,"line":184},[1204],{"type":18,"tag":181,"props":1205,"children":1206},{},[1207],{"type":24,"value":1196},{"type":18,"tag":31,"props":1209,"children":1210},{},[1211],{"type":24,"value":1212},"注意：这只是空间共现\u002F邻近的统计结果，不等于真实生物学互作。",{"type":18,"tag":171,"props":1214,"children":1216},{"className":173,"code":1215,"language":175,"meta":7,"style":7},"# 【邻域富集分析】\n# nhood_enrichment 的问题不是“两个 cluster 是否真的发生细胞通讯”，而是：\n# 在已经定义好的空间邻接图上，cluster A 和 cluster B 是否比随机置换更常相邻。\n# 因此它输出的是空间共现统计线索，不是机制证明。\n\n# n_perms：随机置换次数。次数越大，统计更稳，但运行更慢。\n# 学习阶段用 100 是为了快速看懂流程；正式报告建议提高，并记录 seed 方便复现。\n# seed：固定随机种子，避免每次运行 zscore 有轻微变化。\nsq.gr.nhood_enrichment(\n    adata,\n    cluster_key=cluster_key,\n    n_perms=100,\n    seed=42,\n    show_progress_bar=False,\n)\n\n# 【可视化】\n# 热图颜色通常表示 z-score：越高说明两个 cluster 比随机情况更常相邻。\n# 但“更常相邻”仍然只是空间统计，不要写成“发生了相互作用”。\nsq.pl.nhood_enrichment(adata, cluster_key=cluster_key, title=\"Neighborhood enrichment\")\n\n# 【结果落点】\n# Squidpy 会把结果写进 adata.uns，而不是直接返回一个表。\n# 这体现了 AnnData 的工作方式：分析步骤不断往 adata 里追加结果，adata 就是整个 pipeline 的状态容器。\nnhood_result = adata.uns[f\"{cluster_key}_nhood_enrichment\"]\nprint(\"邻域富集结果字段:\", list(nhood_result.keys()))\nprint(\"zscore 矩阵形状:\", nhood_result[\"zscore\"].shape)\n   \n",[1217],{"type":18,"tag":137,"props":1218,"children":1219},{"__ignoreMap":7},[1220,1228,1236,1244,1252,1259,1267,1275,1283,1291,1298,1306,1314,1322,1330,1337,1344,1352,1360,1368,1376,1383,1391,1399,1407,1415,1423],{"type":18,"tag":181,"props":1221,"children":1222},{"class":183,"line":184},[1223],{"type":18,"tag":181,"props":1224,"children":1225},{},[1226],{"type":24,"value":1227},"# 【邻域富集分析】\n",{"type":18,"tag":181,"props":1229,"children":1230},{"class":183,"line":193},[1231],{"type":18,"tag":181,"props":1232,"children":1233},{},[1234],{"type":24,"value":1235},"# nhood_enrichment 的问题不是“两个 cluster 是否真的发生细胞通讯”，而是：\n",{"type":18,"tag":181,"props":1237,"children":1238},{"class":183,"line":203},[1239],{"type":18,"tag":181,"props":1240,"children":1241},{},[1242],{"type":24,"value":1243},"# 在已经定义好的空间邻接图上，cluster A 和 cluster B 是否比随机置换更常相邻。\n",{"type":18,"tag":181,"props":1245,"children":1246},{"class":183,"line":212},[1247],{"type":18,"tag":181,"props":1248,"children":1249},{},[1250],{"type":24,"value":1251},"# 因此它输出的是空间共现统计线索，不是机制证明。\n",{"type":18,"tag":181,"props":1253,"children":1254},{"class":183,"line":221},[1255],{"type":18,"tag":181,"props":1256,"children":1257},{"emptyLinePlaceholder":197},[1258],{"type":24,"value":200},{"type":18,"tag":181,"props":1260,"children":1261},{"class":183,"line":230},[1262],{"type":18,"tag":181,"props":1263,"children":1264},{},[1265],{"type":24,"value":1266},"# n_perms：随机置换次数。次数越大，统计更稳，但运行更慢。\n",{"type":18,"tag":181,"props":1268,"children":1269},{"class":183,"line":239},[1270],{"type":18,"tag":181,"props":1271,"children":1272},{},[1273],{"type":24,"value":1274},"# 学习阶段用 100 是为了快速看懂流程；正式报告建议提高，并记录 seed 方便复现。\n",{"type":18,"tag":181,"props":1276,"children":1277},{"class":183,"line":248},[1278],{"type":18,"tag":181,"props":1279,"children":1280},{},[1281],{"type":24,"value":1282},"# seed：固定随机种子，避免每次运行 zscore 有轻微变化。\n",{"type":18,"tag":181,"props":1284,"children":1285},{"class":183,"line":257},[1286],{"type":18,"tag":181,"props":1287,"children":1288},{},[1289],{"type":24,"value":1290},"sq.gr.nhood_enrichment(\n",{"type":18,"tag":181,"props":1292,"children":1293},{"class":183,"line":266},[1294],{"type":18,"tag":181,"props":1295,"children":1296},{},[1297],{"type":24,"value":1040},{"type":18,"tag":181,"props":1299,"children":1300},{"class":183,"line":275},[1301],{"type":18,"tag":181,"props":1302,"children":1303},{},[1304],{"type":24,"value":1305},"    cluster_key=cluster_key,\n",{"type":18,"tag":181,"props":1307,"children":1308},{"class":183,"line":284},[1309],{"type":18,"tag":181,"props":1310,"children":1311},{},[1312],{"type":24,"value":1313},"    n_perms=100,\n",{"type":18,"tag":181,"props":1315,"children":1316},{"class":183,"line":292},[1317],{"type":18,"tag":181,"props":1318,"children":1319},{},[1320],{"type":24,"value":1321},"    seed=42,\n",{"type":18,"tag":181,"props":1323,"children":1324},{"class":183,"line":301},[1325],{"type":18,"tag":181,"props":1326,"children":1327},{},[1328],{"type":24,"value":1329},"    show_progress_bar=False,\n",{"type":18,"tag":181,"props":1331,"children":1332},{"class":183,"line":310},[1333],{"type":18,"tag":181,"props":1334,"children":1335},{},[1336],{"type":24,"value":1080},{"type":18,"tag":181,"props":1338,"children":1339},{"class":183,"line":319},[1340],{"type":18,"tag":181,"props":1341,"children":1342},{"emptyLinePlaceholder":197},[1343],{"type":24,"value":200},{"type":18,"tag":181,"props":1345,"children":1346},{"class":183,"line":328},[1347],{"type":18,"tag":181,"props":1348,"children":1349},{},[1350],{"type":24,"value":1351},"# 【可视化】\n",{"type":18,"tag":181,"props":1353,"children":1354},{"class":183,"line":337},[1355],{"type":18,"tag":181,"props":1356,"children":1357},{},[1358],{"type":24,"value":1359},"# 热图颜色通常表示 z-score：越高说明两个 cluster 比随机情况更常相邻。\n",{"type":18,"tag":181,"props":1361,"children":1362},{"class":183,"line":345},[1363],{"type":18,"tag":181,"props":1364,"children":1365},{},[1366],{"type":24,"value":1367},"# 但“更常相邻”仍然只是空间统计，不要写成“发生了相互作用”。\n",{"type":18,"tag":181,"props":1369,"children":1370},{"class":183,"line":354},[1371],{"type":18,"tag":181,"props":1372,"children":1373},{},[1374],{"type":24,"value":1375},"sq.pl.nhood_enrichment(adata, cluster_key=cluster_key, title=\"Neighborhood enrichment\")\n",{"type":18,"tag":181,"props":1377,"children":1378},{"class":183,"line":363},[1379],{"type":18,"tag":181,"props":1380,"children":1381},{"emptyLinePlaceholder":197},[1382],{"type":24,"value":200},{"type":18,"tag":181,"props":1384,"children":1385},{"class":183,"line":372},[1386],{"type":18,"tag":181,"props":1387,"children":1388},{},[1389],{"type":24,"value":1390},"# 【结果落点】\n",{"type":18,"tag":181,"props":1392,"children":1393},{"class":183,"line":381},[1394],{"type":18,"tag":181,"props":1395,"children":1396},{},[1397],{"type":24,"value":1398},"# Squidpy 会把结果写进 adata.uns，而不是直接返回一个表。\n",{"type":18,"tag":181,"props":1400,"children":1401},{"class":183,"line":389},[1402],{"type":18,"tag":181,"props":1403,"children":1404},{},[1405],{"type":24,"value":1406},"# 这体现了 AnnData 的工作方式：分析步骤不断往 adata 里追加结果，adata 就是整个 pipeline 的状态容器。\n",{"type":18,"tag":181,"props":1408,"children":1409},{"class":183,"line":398},[1410],{"type":18,"tag":181,"props":1411,"children":1412},{},[1413],{"type":24,"value":1414},"nhood_result = adata.uns[f\"{cluster_key}_nhood_enrichment\"]\n",{"type":18,"tag":181,"props":1416,"children":1417},{"class":183,"line":407},[1418],{"type":18,"tag":181,"props":1419,"children":1420},{},[1421],{"type":24,"value":1422},"print(\"邻域富集结果字段:\", list(nhood_result.keys()))\n",{"type":18,"tag":181,"props":1424,"children":1425},{"class":183,"line":416},[1426],{"type":18,"tag":181,"props":1427,"children":1428},{},[1429],{"type":24,"value":1430},"print(\"zscore 矩阵形状:\", nhood_result[\"zscore\"].shape)\n",{"type":18,"tag":67,"props":1432,"children":1434},{"id":1433},"_5-空间可变基因morans-i",[1435],{"type":24,"value":1436},"5. 空间可变基因：Moran's I",{"type":18,"tag":31,"props":1438,"children":1439},{},[1440],{"type":24,"value":1441},"空间可变基因回答的是：",{"type":18,"tag":171,"props":1443,"children":1445},{"className":772,"code":1444,"language":774,"meta":7,"style":7},"这个基因的表达是否呈现空间聚集\u002F空间模式？\n",[1446],{"type":18,"tag":137,"props":1447,"children":1448},{"__ignoreMap":7},[1449],{"type":18,"tag":181,"props":1450,"children":1451},{"class":183,"line":184},[1452],{"type":18,"tag":181,"props":1453,"children":1454},{},[1455],{"type":24,"value":1444},{"type":18,"tag":31,"props":1457,"children":1458},{},[1459],{"type":24,"value":1460},"如果基因 A 的高表达 spot 在空间上成片聚集，\n而不是随机散落，",{"type":18,"tag":31,"props":1462,"children":1463},{},[1464],{"type":24,"value":1465},"说明基因 A 可能和某个空间区域 \u002F 组织结构 \u002F 生物学 domain 有关。\n它仍然是统计线索，不是靶点验证。",{"type":18,"tag":31,"props":1467,"children":1468},{},[1469],{"type":24,"value":1470},"邻域富集说的是\"谁和谁挨着\"（cluster 之间），空间可变基因说的是\"一个基因在哪里高\"（基因×空间）。",{"type":18,"tag":171,"props":1472,"children":1474},{"className":173,"code":1473,"language":175,"meta":7,"style":7},"# 【空间可变基因】\n# Moran's I 衡量一个基因表达是否呈现空间自相关：\n# 如果高表达 spot 倾向于挨在一起，Moran's I 往往更高。\n# 这一步回答的是“表达是否有空间模式”，不是“这个基因是否是有效药物靶点”。\n\n# 示例数据是小鼠脑组织，这里挑几个常见脑组织相关基因做入门观察。\n# 将来封装成 tool 时，这个列表应来自用户入参 target_genes。\ncandidate_genes = [\"Mbp\", \"Hpca\", \"Ttr\", \"Snap25\", \"Pcp4\"]\n\n# 【输入护栏】\n# 真实数据里用户给的基因名可能不存在，比如大小写不一致、人鼠同源基因混淆、面板数据未检测该基因。\n# 这里先过滤存在的基因，避免整个流程因为一个缺失基因直接失败。  \ngenes = [gene for gene in candidate_genes if gene in adata.var_names]\n\n# 【异常处理】\n# 如果一个目标基因都不存在，就明确报错。\n# 这比静默返回空结果更适合 Agent tool，因为 LLM 需要知道失败原因，才能给用户解释或换参数。\nif not genes:\n    raise ValueError(\"候选基因都不在 adata.var_names 中，请换一组 target_genes。\")\n\n# 【统计计算】\n# mode=\"moran\" 使用 Moran's I。\n# n_perms=None 表示这里先不做置换检验，学习阶段只看空间自相关分数本身。\n# 如果要写正式报告，应加入置换检验或多重检验校正信息。\nsq.gr.spatial_autocorr(\n    adata,\n    genes=genes,\n    mode=\"moran\",\n    n_perms=None,\n    show_progress_bar=False,\n)\n\n# 【结果排序】\n# adata.uns[\"moranI\"] 是 Squidpy 写回的结果表。\n# 按 I 从高到低排序，方便找空间模式最明显的候选基因。\nmoran_table = adata.uns[\"moranI\"].sort_values(\"I\", ascending=False)\ndisplay(moran_table)\n\n# 【把统计结果投回真实空间】\n# 只看表格不知道表达区域在哪里，所以把 Moran's I 最高的基因画回组织坐标。\n# 这一步是空间组学的关键：统计线索必须回到 tissue context 里解释。\ntop_gene = str(moran_table.index[0])\nsq.pl.spatial_scatter(adata, color=top_gene, title=f\"Spatial expression: {top_gene}\")\n  \n",[1475],{"type":18,"tag":137,"props":1476,"children":1477},{"__ignoreMap":7},[1478,1486,1494,1502,1510,1517,1525,1533,1541,1548,1556,1564,1572,1580,1587,1595,1603,1611,1619,1627,1634,1642,1650,1658,1666,1674,1681,1689,1697,1706,1714,1722,1730,1739,1748,1757,1766,1775,1783,1792,1801,1810,1819],{"type":18,"tag":181,"props":1479,"children":1480},{"class":183,"line":184},[1481],{"type":18,"tag":181,"props":1482,"children":1483},{},[1484],{"type":24,"value":1485},"# 【空间可变基因】\n",{"type":18,"tag":181,"props":1487,"children":1488},{"class":183,"line":193},[1489],{"type":18,"tag":181,"props":1490,"children":1491},{},[1492],{"type":24,"value":1493},"# Moran's I 衡量一个基因表达是否呈现空间自相关：\n",{"type":18,"tag":181,"props":1495,"children":1496},{"class":183,"line":203},[1497],{"type":18,"tag":181,"props":1498,"children":1499},{},[1500],{"type":24,"value":1501},"# 如果高表达 spot 倾向于挨在一起，Moran's I 往往更高。\n",{"type":18,"tag":181,"props":1503,"children":1504},{"class":183,"line":212},[1505],{"type":18,"tag":181,"props":1506,"children":1507},{},[1508],{"type":24,"value":1509},"# 这一步回答的是“表达是否有空间模式”，不是“这个基因是否是有效药物靶点”。\n",{"type":18,"tag":181,"props":1511,"children":1512},{"class":183,"line":221},[1513],{"type":18,"tag":181,"props":1514,"children":1515},{"emptyLinePlaceholder":197},[1516],{"type":24,"value":200},{"type":18,"tag":181,"props":1518,"children":1519},{"class":183,"line":230},[1520],{"type":18,"tag":181,"props":1521,"children":1522},{},[1523],{"type":24,"value":1524},"# 示例数据是小鼠脑组织，这里挑几个常见脑组织相关基因做入门观察。\n",{"type":18,"tag":181,"props":1526,"children":1527},{"class":183,"line":239},[1528],{"type":18,"tag":181,"props":1529,"children":1530},{},[1531],{"type":24,"value":1532},"# 将来封装成 tool 时，这个列表应来自用户入参 target_genes。\n",{"type":18,"tag":181,"props":1534,"children":1535},{"class":183,"line":248},[1536],{"type":18,"tag":181,"props":1537,"children":1538},{},[1539],{"type":24,"value":1540},"candidate_genes = [\"Mbp\", \"Hpca\", \"Ttr\", \"Snap25\", \"Pcp4\"]\n",{"type":18,"tag":181,"props":1542,"children":1543},{"class":183,"line":257},[1544],{"type":18,"tag":181,"props":1545,"children":1546},{"emptyLinePlaceholder":197},[1547],{"type":24,"value":200},{"type":18,"tag":181,"props":1549,"children":1550},{"class":183,"line":266},[1551],{"type":18,"tag":181,"props":1552,"children":1553},{},[1554],{"type":24,"value":1555},"# 【输入护栏】\n",{"type":18,"tag":181,"props":1557,"children":1558},{"class":183,"line":275},[1559],{"type":18,"tag":181,"props":1560,"children":1561},{},[1562],{"type":24,"value":1563},"# 真实数据里用户给的基因名可能不存在，比如大小写不一致、人鼠同源基因混淆、面板数据未检测该基因。\n",{"type":18,"tag":181,"props":1565,"children":1566},{"class":183,"line":284},[1567],{"type":18,"tag":181,"props":1568,"children":1569},{},[1570],{"type":24,"value":1571},"# 这里先过滤存在的基因，避免整个流程因为一个缺失基因直接失败。  \n",{"type":18,"tag":181,"props":1573,"children":1574},{"class":183,"line":292},[1575],{"type":18,"tag":181,"props":1576,"children":1577},{},[1578],{"type":24,"value":1579},"genes = [gene for gene in candidate_genes if gene in adata.var_names]\n",{"type":18,"tag":181,"props":1581,"children":1582},{"class":183,"line":301},[1583],{"type":18,"tag":181,"props":1584,"children":1585},{"emptyLinePlaceholder":197},[1586],{"type":24,"value":200},{"type":18,"tag":181,"props":1588,"children":1589},{"class":183,"line":310},[1590],{"type":18,"tag":181,"props":1591,"children":1592},{},[1593],{"type":24,"value":1594},"# 【异常处理】\n",{"type":18,"tag":181,"props":1596,"children":1597},{"class":183,"line":319},[1598],{"type":18,"tag":181,"props":1599,"children":1600},{},[1601],{"type":24,"value":1602},"# 如果一个目标基因都不存在，就明确报错。\n",{"type":18,"tag":181,"props":1604,"children":1605},{"class":183,"line":328},[1606],{"type":18,"tag":181,"props":1607,"children":1608},{},[1609],{"type":24,"value":1610},"# 这比静默返回空结果更适合 Agent tool，因为 LLM 需要知道失败原因，才能给用户解释或换参数。\n",{"type":18,"tag":181,"props":1612,"children":1613},{"class":183,"line":337},[1614],{"type":18,"tag":181,"props":1615,"children":1616},{},[1617],{"type":24,"value":1618},"if not genes:\n",{"type":18,"tag":181,"props":1620,"children":1621},{"class":183,"line":345},[1622],{"type":18,"tag":181,"props":1623,"children":1624},{},[1625],{"type":24,"value":1626},"    raise ValueError(\"候选基因都不在 adata.var_names 中，请换一组 target_genes。\")\n",{"type":18,"tag":181,"props":1628,"children":1629},{"class":183,"line":354},[1630],{"type":18,"tag":181,"props":1631,"children":1632},{"emptyLinePlaceholder":197},[1633],{"type":24,"value":200},{"type":18,"tag":181,"props":1635,"children":1636},{"class":183,"line":363},[1637],{"type":18,"tag":181,"props":1638,"children":1639},{},[1640],{"type":24,"value":1641},"# 【统计计算】\n",{"type":18,"tag":181,"props":1643,"children":1644},{"class":183,"line":372},[1645],{"type":18,"tag":181,"props":1646,"children":1647},{},[1648],{"type":24,"value":1649},"# mode=\"moran\" 使用 Moran's I。\n",{"type":18,"tag":181,"props":1651,"children":1652},{"class":183,"line":381},[1653],{"type":18,"tag":181,"props":1654,"children":1655},{},[1656],{"type":24,"value":1657},"# n_perms=None 表示这里先不做置换检验，学习阶段只看空间自相关分数本身。\n",{"type":18,"tag":181,"props":1659,"children":1660},{"class":183,"line":389},[1661],{"type":18,"tag":181,"props":1662,"children":1663},{},[1664],{"type":24,"value":1665},"# 如果要写正式报告，应加入置换检验或多重检验校正信息。\n",{"type":18,"tag":181,"props":1667,"children":1668},{"class":183,"line":398},[1669],{"type":18,"tag":181,"props":1670,"children":1671},{},[1672],{"type":24,"value":1673},"sq.gr.spatial_autocorr(\n",{"type":18,"tag":181,"props":1675,"children":1676},{"class":183,"line":407},[1677],{"type":18,"tag":181,"props":1678,"children":1679},{},[1680],{"type":24,"value":1040},{"type":18,"tag":181,"props":1682,"children":1683},{"class":183,"line":416},[1684],{"type":18,"tag":181,"props":1685,"children":1686},{},[1687],{"type":24,"value":1688},"    genes=genes,\n",{"type":18,"tag":181,"props":1690,"children":1691},{"class":183,"line":747},[1692],{"type":18,"tag":181,"props":1693,"children":1694},{},[1695],{"type":24,"value":1696},"    mode=\"moran\",\n",{"type":18,"tag":181,"props":1698,"children":1700},{"class":183,"line":1699},29,[1701],{"type":18,"tag":181,"props":1702,"children":1703},{},[1704],{"type":24,"value":1705},"    n_perms=None,\n",{"type":18,"tag":181,"props":1707,"children":1709},{"class":183,"line":1708},30,[1710],{"type":18,"tag":181,"props":1711,"children":1712},{},[1713],{"type":24,"value":1329},{"type":18,"tag":181,"props":1715,"children":1717},{"class":183,"line":1716},31,[1718],{"type":18,"tag":181,"props":1719,"children":1720},{},[1721],{"type":24,"value":1080},{"type":18,"tag":181,"props":1723,"children":1725},{"class":183,"line":1724},32,[1726],{"type":18,"tag":181,"props":1727,"children":1728},{"emptyLinePlaceholder":197},[1729],{"type":24,"value":200},{"type":18,"tag":181,"props":1731,"children":1733},{"class":183,"line":1732},33,[1734],{"type":18,"tag":181,"props":1735,"children":1736},{},[1737],{"type":24,"value":1738},"# 【结果排序】\n",{"type":18,"tag":181,"props":1740,"children":1742},{"class":183,"line":1741},34,[1743],{"type":18,"tag":181,"props":1744,"children":1745},{},[1746],{"type":24,"value":1747},"# adata.uns[\"moranI\"] 是 Squidpy 写回的结果表。\n",{"type":18,"tag":181,"props":1749,"children":1751},{"class":183,"line":1750},35,[1752],{"type":18,"tag":181,"props":1753,"children":1754},{},[1755],{"type":24,"value":1756},"# 按 I 从高到低排序，方便找空间模式最明显的候选基因。\n",{"type":18,"tag":181,"props":1758,"children":1760},{"class":183,"line":1759},36,[1761],{"type":18,"tag":181,"props":1762,"children":1763},{},[1764],{"type":24,"value":1765},"moran_table = adata.uns[\"moranI\"].sort_values(\"I\", ascending=False)\n",{"type":18,"tag":181,"props":1767,"children":1769},{"class":183,"line":1768},37,[1770],{"type":18,"tag":181,"props":1771,"children":1772},{},[1773],{"type":24,"value":1774},"display(moran_table)\n",{"type":18,"tag":181,"props":1776,"children":1778},{"class":183,"line":1777},38,[1779],{"type":18,"tag":181,"props":1780,"children":1781},{"emptyLinePlaceholder":197},[1782],{"type":24,"value":200},{"type":18,"tag":181,"props":1784,"children":1786},{"class":183,"line":1785},39,[1787],{"type":18,"tag":181,"props":1788,"children":1789},{},[1790],{"type":24,"value":1791},"# 【把统计结果投回真实空间】\n",{"type":18,"tag":181,"props":1793,"children":1795},{"class":183,"line":1794},40,[1796],{"type":18,"tag":181,"props":1797,"children":1798},{},[1799],{"type":24,"value":1800},"# 只看表格不知道表达区域在哪里，所以把 Moran's I 最高的基因画回组织坐标。\n",{"type":18,"tag":181,"props":1802,"children":1804},{"class":183,"line":1803},41,[1805],{"type":18,"tag":181,"props":1806,"children":1807},{},[1808],{"type":24,"value":1809},"# 这一步是空间组学的关键：统计线索必须回到 tissue context 里解释。\n",{"type":18,"tag":181,"props":1811,"children":1813},{"class":183,"line":1812},42,[1814],{"type":18,"tag":181,"props":1815,"children":1816},{},[1817],{"type":24,"value":1818},"top_gene = str(moran_table.index[0])\n",{"type":18,"tag":181,"props":1820,"children":1822},{"class":183,"line":1821},43,[1823],{"type":18,"tag":181,"props":1824,"children":1825},{},[1826],{"type":24,"value":1827},"sq.pl.spatial_scatter(adata, color=top_gene, title=f\"Spatial expression: {top_gene}\")\n",{"type":18,"tag":67,"props":1829,"children":1831},{"id":1830},"_6-生成结构化质量报告",[1832],{"type":24,"value":1833},"6. 生成结构化质量报告",{"type":18,"tag":31,"props":1835,"children":1836},{},[1837],{"type":24,"value":1838},"这一步开始向 Agent tool 靠拢：不要只把图留在 notebook 里，而要返回机器和人都能读的结构化结果。",{"type":18,"tag":171,"props":1840,"children":1842},{"className":173,"code":1841,"language":175,"meta":7,"style":7},"# 【质量护栏 quality_flags】\n# Agent 不能像湿实验一样立刻验证一个空间组学结论是否真实。\n# 所以 tool 必须把“不确定性”和“不能过度解释的地方”显式返回给 LLM。\n# 这些 flags 不是报错，而是防止模型把统计线索说成生物学定论。\nquality_flags = []\n\n# 护栏 1：Visium 是 spot 级，不允许直接当单细胞解释。\n# 这是平台层面的限制，和代码是否跑通无关。\nquality_flags.append(\"Visium 是 spot 级数据；一个 spot 可能混合多个细胞，不能直接解释成单细胞结论。\")\n\n# 护栏 2：空间邻居数异常时，提示邻接图可能有问题。\n# 平均邻居数过低：空间图太稀，邻域统计不稳定。\n# 平均邻居数过高：邻域定义太宽，可能把本不相邻的区域混在一起。\nif avg_neighbors \u003C 3:\n    quality_flags.append(\"平均邻居数偏低，空间邻接图可能过稀。\")\nif avg_neighbors > 12:\n    quality_flags.append(\"平均邻居数偏高，空间邻接图可能过密。\")\n\n# 护栏 3：邻域富集只能说明空间共现，不能说明真实互作。\n# 这是把“统计结果”和“生物学机制”分开的关键，后续写报告必须保留。\nquality_flags.append(\"邻域富集只能说明空间共现\u002F邻近，不能证明细胞间真实互作。\")\n\n# 护栏 4：Moran's I 只能说明空间模式，不能直接推出药物靶点有效。\n# 对 AI 制药来说，它最多提供候选靶点\u002F候选区域线索，不能替代实验验证。\nquality_flags.append(\"空间可变基因是候选线索，不等于靶点验证。\")\n\n# 【结构化输出】\n# 这里开始按 tool schema 的思路组织结果：\n# - 基础元数据：platform、n_spots、n_genes\n# - 分析参数：cluster_key、spatial_key\n# - 核心结果：avg_spatial_neighbors、moran_top_gene、moran_table\n# - 可解释性材料：quality_flags、figure_paths\n# 这样 LLM 不需要从 notebook 输出里猜信息，可以直接消费这个 dict 生成报告。\nquality_report = {\n    \"platform\": \"10x Visium H&E\",\n    \"n_spots\": int(adata.n_obs),\n    \"n_genes\": int(adata.n_vars),\n    \"cluster_key\": cluster_key,\n    \"n_clusters\": int(adata.obs[cluster_key].nunique()),\n    \"spatial_key\": \"spatial\",\n    \"avg_spatial_neighbors\": round(float(avg_neighbors), 2),\n    \"moran_top_gene\": top_gene,\n    \"moran_table\": moran_table.reset_index().rename(columns={\"index\": \"gene\"}).to_dict(orient=\"records\"),\n    \"quality_flags\": quality_flags,\n    \"figure_paths\": figure_paths,\n}\n\n# 【最终 Observation】\n# notebook 里直接显示 dict；封装成函数时，这一行应改成 return quality_report。\nquality_report\n",[1843],{"type":18,"tag":137,"props":1844,"children":1845},{"__ignoreMap":7},[1846,1854,1862,1870,1878,1886,1893,1901,1909,1917,1924,1932,1940,1948,1956,1964,1972,1980,1987,1995,2003,2011,2018,2026,2034,2042,2049,2057,2065,2073,2081,2089,2097,2105,2113,2121,2129,2137,2145,2153,2161,2169,2177,2185,2194,2203,2211,2219,2228,2237],{"type":18,"tag":181,"props":1847,"children":1848},{"class":183,"line":184},[1849],{"type":18,"tag":181,"props":1850,"children":1851},{},[1852],{"type":24,"value":1853},"# 【质量护栏 quality_flags】\n",{"type":18,"tag":181,"props":1855,"children":1856},{"class":183,"line":193},[1857],{"type":18,"tag":181,"props":1858,"children":1859},{},[1860],{"type":24,"value":1861},"# Agent 不能像湿实验一样立刻验证一个空间组学结论是否真实。\n",{"type":18,"tag":181,"props":1863,"children":1864},{"class":183,"line":203},[1865],{"type":18,"tag":181,"props":1866,"children":1867},{},[1868],{"type":24,"value":1869},"# 所以 tool 必须把“不确定性”和“不能过度解释的地方”显式返回给 LLM。\n",{"type":18,"tag":181,"props":1871,"children":1872},{"class":183,"line":212},[1873],{"type":18,"tag":181,"props":1874,"children":1875},{},[1876],{"type":24,"value":1877},"# 这些 flags 不是报错，而是防止模型把统计线索说成生物学定论。\n",{"type":18,"tag":181,"props":1879,"children":1880},{"class":183,"line":221},[1881],{"type":18,"tag":181,"props":1882,"children":1883},{},[1884],{"type":24,"value":1885},"quality_flags = []\n",{"type":18,"tag":181,"props":1887,"children":1888},{"class":183,"line":230},[1889],{"type":18,"tag":181,"props":1890,"children":1891},{"emptyLinePlaceholder":197},[1892],{"type":24,"value":200},{"type":18,"tag":181,"props":1894,"children":1895},{"class":183,"line":239},[1896],{"type":18,"tag":181,"props":1897,"children":1898},{},[1899],{"type":24,"value":1900},"# 护栏 1：Visium 是 spot 级，不允许直接当单细胞解释。\n",{"type":18,"tag":181,"props":1902,"children":1903},{"class":183,"line":248},[1904],{"type":18,"tag":181,"props":1905,"children":1906},{},[1907],{"type":24,"value":1908},"# 这是平台层面的限制，和代码是否跑通无关。\n",{"type":18,"tag":181,"props":1910,"children":1911},{"class":183,"line":257},[1912],{"type":18,"tag":181,"props":1913,"children":1914},{},[1915],{"type":24,"value":1916},"quality_flags.append(\"Visium 是 spot 级数据；一个 spot 可能混合多个细胞，不能直接解释成单细胞结论。\")\n",{"type":18,"tag":181,"props":1918,"children":1919},{"class":183,"line":266},[1920],{"type":18,"tag":181,"props":1921,"children":1922},{"emptyLinePlaceholder":197},[1923],{"type":24,"value":200},{"type":18,"tag":181,"props":1925,"children":1926},{"class":183,"line":275},[1927],{"type":18,"tag":181,"props":1928,"children":1929},{},[1930],{"type":24,"value":1931},"# 护栏 2：空间邻居数异常时，提示邻接图可能有问题。\n",{"type":18,"tag":181,"props":1933,"children":1934},{"class":183,"line":284},[1935],{"type":18,"tag":181,"props":1936,"children":1937},{},[1938],{"type":24,"value":1939},"# 平均邻居数过低：空间图太稀，邻域统计不稳定。\n",{"type":18,"tag":181,"props":1941,"children":1942},{"class":183,"line":292},[1943],{"type":18,"tag":181,"props":1944,"children":1945},{},[1946],{"type":24,"value":1947},"# 平均邻居数过高：邻域定义太宽，可能把本不相邻的区域混在一起。\n",{"type":18,"tag":181,"props":1949,"children":1950},{"class":183,"line":301},[1951],{"type":18,"tag":181,"props":1952,"children":1953},{},[1954],{"type":24,"value":1955},"if avg_neighbors \u003C 3:\n",{"type":18,"tag":181,"props":1957,"children":1958},{"class":183,"line":310},[1959],{"type":18,"tag":181,"props":1960,"children":1961},{},[1962],{"type":24,"value":1963},"    quality_flags.append(\"平均邻居数偏低，空间邻接图可能过稀。\")\n",{"type":18,"tag":181,"props":1965,"children":1966},{"class":183,"line":319},[1967],{"type":18,"tag":181,"props":1968,"children":1969},{},[1970],{"type":24,"value":1971},"if avg_neighbors > 12:\n",{"type":18,"tag":181,"props":1973,"children":1974},{"class":183,"line":328},[1975],{"type":18,"tag":181,"props":1976,"children":1977},{},[1978],{"type":24,"value":1979},"    quality_flags.append(\"平均邻居数偏高，空间邻接图可能过密。\")\n",{"type":18,"tag":181,"props":1981,"children":1982},{"class":183,"line":337},[1983],{"type":18,"tag":181,"props":1984,"children":1985},{"emptyLinePlaceholder":197},[1986],{"type":24,"value":200},{"type":18,"tag":181,"props":1988,"children":1989},{"class":183,"line":345},[1990],{"type":18,"tag":181,"props":1991,"children":1992},{},[1993],{"type":24,"value":1994},"# 护栏 3：邻域富集只能说明空间共现，不能说明真实互作。\n",{"type":18,"tag":181,"props":1996,"children":1997},{"class":183,"line":354},[1998],{"type":18,"tag":181,"props":1999,"children":2000},{},[2001],{"type":24,"value":2002},"# 这是把“统计结果”和“生物学机制”分开的关键，后续写报告必须保留。\n",{"type":18,"tag":181,"props":2004,"children":2005},{"class":183,"line":363},[2006],{"type":18,"tag":181,"props":2007,"children":2008},{},[2009],{"type":24,"value":2010},"quality_flags.append(\"邻域富集只能说明空间共现\u002F邻近，不能证明细胞间真实互作。\")\n",{"type":18,"tag":181,"props":2012,"children":2013},{"class":183,"line":372},[2014],{"type":18,"tag":181,"props":2015,"children":2016},{"emptyLinePlaceholder":197},[2017],{"type":24,"value":200},{"type":18,"tag":181,"props":2019,"children":2020},{"class":183,"line":381},[2021],{"type":18,"tag":181,"props":2022,"children":2023},{},[2024],{"type":24,"value":2025},"# 护栏 4：Moran's I 只能说明空间模式，不能直接推出药物靶点有效。\n",{"type":18,"tag":181,"props":2027,"children":2028},{"class":183,"line":389},[2029],{"type":18,"tag":181,"props":2030,"children":2031},{},[2032],{"type":24,"value":2033},"# 对 AI 制药来说，它最多提供候选靶点\u002F候选区域线索，不能替代实验验证。\n",{"type":18,"tag":181,"props":2035,"children":2036},{"class":183,"line":398},[2037],{"type":18,"tag":181,"props":2038,"children":2039},{},[2040],{"type":24,"value":2041},"quality_flags.append(\"空间可变基因是候选线索，不等于靶点验证。\")\n",{"type":18,"tag":181,"props":2043,"children":2044},{"class":183,"line":407},[2045],{"type":18,"tag":181,"props":2046,"children":2047},{"emptyLinePlaceholder":197},[2048],{"type":24,"value":200},{"type":18,"tag":181,"props":2050,"children":2051},{"class":183,"line":416},[2052],{"type":18,"tag":181,"props":2053,"children":2054},{},[2055],{"type":24,"value":2056},"# 【结构化输出】\n",{"type":18,"tag":181,"props":2058,"children":2059},{"class":183,"line":747},[2060],{"type":18,"tag":181,"props":2061,"children":2062},{},[2063],{"type":24,"value":2064},"# 这里开始按 tool schema 的思路组织结果：\n",{"type":18,"tag":181,"props":2066,"children":2067},{"class":183,"line":1699},[2068],{"type":18,"tag":181,"props":2069,"children":2070},{},[2071],{"type":24,"value":2072},"# - 基础元数据：platform、n_spots、n_genes\n",{"type":18,"tag":181,"props":2074,"children":2075},{"class":183,"line":1708},[2076],{"type":18,"tag":181,"props":2077,"children":2078},{},[2079],{"type":24,"value":2080},"# - 分析参数：cluster_key、spatial_key\n",{"type":18,"tag":181,"props":2082,"children":2083},{"class":183,"line":1716},[2084],{"type":18,"tag":181,"props":2085,"children":2086},{},[2087],{"type":24,"value":2088},"# - 核心结果：avg_spatial_neighbors、moran_top_gene、moran_table\n",{"type":18,"tag":181,"props":2090,"children":2091},{"class":183,"line":1724},[2092],{"type":18,"tag":181,"props":2093,"children":2094},{},[2095],{"type":24,"value":2096},"# - 可解释性材料：quality_flags、figure_paths\n",{"type":18,"tag":181,"props":2098,"children":2099},{"class":183,"line":1732},[2100],{"type":18,"tag":181,"props":2101,"children":2102},{},[2103],{"type":24,"value":2104},"# 这样 LLM 不需要从 notebook 输出里猜信息，可以直接消费这个 dict 生成报告。\n",{"type":18,"tag":181,"props":2106,"children":2107},{"class":183,"line":1741},[2108],{"type":18,"tag":181,"props":2109,"children":2110},{},[2111],{"type":24,"value":2112},"quality_report = {\n",{"type":18,"tag":181,"props":2114,"children":2115},{"class":183,"line":1750},[2116],{"type":18,"tag":181,"props":2117,"children":2118},{},[2119],{"type":24,"value":2120},"    \"platform\": \"10x Visium H&E\",\n",{"type":18,"tag":181,"props":2122,"children":2123},{"class":183,"line":1759},[2124],{"type":18,"tag":181,"props":2125,"children":2126},{},[2127],{"type":24,"value":2128},"    \"n_spots\": int(adata.n_obs),\n",{"type":18,"tag":181,"props":2130,"children":2131},{"class":183,"line":1768},[2132],{"type":18,"tag":181,"props":2133,"children":2134},{},[2135],{"type":24,"value":2136},"    \"n_genes\": int(adata.n_vars),\n",{"type":18,"tag":181,"props":2138,"children":2139},{"class":183,"line":1777},[2140],{"type":18,"tag":181,"props":2141,"children":2142},{},[2143],{"type":24,"value":2144},"    \"cluster_key\": cluster_key,\n",{"type":18,"tag":181,"props":2146,"children":2147},{"class":183,"line":1785},[2148],{"type":18,"tag":181,"props":2149,"children":2150},{},[2151],{"type":24,"value":2152},"    \"n_clusters\": int(adata.obs[cluster_key].nunique()),\n",{"type":18,"tag":181,"props":2154,"children":2155},{"class":183,"line":1794},[2156],{"type":18,"tag":181,"props":2157,"children":2158},{},[2159],{"type":24,"value":2160},"    \"spatial_key\": \"spatial\",\n",{"type":18,"tag":181,"props":2162,"children":2163},{"class":183,"line":1803},[2164],{"type":18,"tag":181,"props":2165,"children":2166},{},[2167],{"type":24,"value":2168},"    \"avg_spatial_neighbors\": round(float(avg_neighbors), 2),\n",{"type":18,"tag":181,"props":2170,"children":2171},{"class":183,"line":1812},[2172],{"type":18,"tag":181,"props":2173,"children":2174},{},[2175],{"type":24,"value":2176},"    \"moran_top_gene\": top_gene,\n",{"type":18,"tag":181,"props":2178,"children":2179},{"class":183,"line":1821},[2180],{"type":18,"tag":181,"props":2181,"children":2182},{},[2183],{"type":24,"value":2184},"    \"moran_table\": moran_table.reset_index().rename(columns={\"index\": \"gene\"}).to_dict(orient=\"records\"),\n",{"type":18,"tag":181,"props":2186,"children":2188},{"class":183,"line":2187},44,[2189],{"type":18,"tag":181,"props":2190,"children":2191},{},[2192],{"type":24,"value":2193},"    \"quality_flags\": quality_flags,\n",{"type":18,"tag":181,"props":2195,"children":2197},{"class":183,"line":2196},45,[2198],{"type":18,"tag":181,"props":2199,"children":2200},{},[2201],{"type":24,"value":2202},"    \"figure_paths\": figure_paths,\n",{"type":18,"tag":181,"props":2204,"children":2206},{"class":183,"line":2205},46,[2207],{"type":18,"tag":181,"props":2208,"children":2209},{},[2210],{"type":24,"value":904},{"type":18,"tag":181,"props":2212,"children":2214},{"class":183,"line":2213},47,[2215],{"type":18,"tag":181,"props":2216,"children":2217},{"emptyLinePlaceholder":197},[2218],{"type":24,"value":200},{"type":18,"tag":181,"props":2220,"children":2222},{"class":183,"line":2221},48,[2223],{"type":18,"tag":181,"props":2224,"children":2225},{},[2226],{"type":24,"value":2227},"# 【最终 Observation】\n",{"type":18,"tag":181,"props":2229,"children":2231},{"class":183,"line":2230},49,[2232],{"type":18,"tag":181,"props":2233,"children":2234},{},[2235],{"type":24,"value":2236},"# notebook 里直接显示 dict；封装成函数时，这一行应改成 return quality_report。\n",{"type":18,"tag":181,"props":2238,"children":2240},{"class":183,"line":2239},50,[2241],{"type":18,"tag":181,"props":2242,"children":2243},{},[2244],{"type":24,"value":2245},"quality_report\n",{"type":18,"tag":67,"props":2247,"children":2249},{"id":2248},"_7-这一节要带走什么",[2250],{"type":24,"value":2251},"7. 这一节要带走什么",{"type":18,"tag":31,"props":2253,"children":2254},{},[2255],{"type":24,"value":2256},"你现在已经跑通了空转最小闭环：",{"type":18,"tag":171,"props":2258,"children":2260},{"className":772,"code":2259,"language":774,"meta":7,"style":7},"读取 Visium 数据 -> 空间可视化 -> 空间邻接图 -> 邻域富集 -> 空间可变基因 -> 质量报告\n",[2261],{"type":18,"tag":137,"props":2262,"children":2263},{"__ignoreMap":7},[2264],{"type":18,"tag":181,"props":2265,"children":2266},{"class":183,"line":184},[2267],{"type":18,"tag":181,"props":2268,"children":2269},{},[2270],{"type":24,"value":2259},{"type":18,"tag":31,"props":2272,"children":2273},{},[2274],{"type":24,"value":2275},"这套流程后面可以封装成一个 tool：",{"type":18,"tag":171,"props":2277,"children":2279},{"className":772,"code":2278,"language":774,"meta":7,"style":7},"analyze_spatial_transcriptomics(adata_path, platform, cluster_key, target_genes) -> dict\n",[2280],{"type":18,"tag":137,"props":2281,"children":2282},{"__ignoreMap":7},[2283],{"type":18,"tag":181,"props":2284,"children":2285},{"class":183,"line":184},[2286],{"type":18,"tag":181,"props":2287,"children":2288},{},[2289],{"type":24,"value":2278},{"type":18,"tag":31,"props":2291,"children":2292},{},[2293],{"type":24,"value":2294},"重点不是 Agent 会不会调用 Squidpy，而是 tool 的输出必须带护栏：",{"type":18,"tag":112,"props":2296,"children":2297},{},[2298,2303,2308,2313],{"type":18,"tag":46,"props":2299,"children":2300},{},[2301],{"type":24,"value":2302},"Visium 不能说成单细胞结果。",{"type":18,"tag":46,"props":2304,"children":2305},{},[2306],{"type":24,"value":2307},"邻域富集不能说成真实互作。",{"type":18,"tag":46,"props":2309,"children":2310},{},[2311],{"type":24,"value":2312},"Moran's I 不能说成靶点验证。",{"type":18,"tag":46,"props":2314,"children":2315},{},[2316],{"type":24,"value":2317},"notebook 里的图要配合结构化结果一起返回。",{"type":18,"tag":2319,"props":2320,"children":2321},"style",{},[2322],{"type":24,"value":2323},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":193,"depth":193,"links":2325},[2326,2327,2328,2329,2330,2331,2332,2333,2334],{"id":69,"depth":193,"text":69},{"id":127,"depth":193,"text":130},{"id":425,"depth":193,"text":428},{"id":756,"depth":193,"text":759},{"id":907,"depth":193,"text":910},{"id":1185,"depth":193,"text":1188},{"id":1433,"depth":193,"text":1436},{"id":1830,"depth":193,"text":1833},{"id":2248,"depth":193,"text":2251},"markdown","content:articles:AI制药理论:spatial-transcriptomics.md","content","articles\u002FAI制药理论\u002Fspatial-transcriptomics.md","articles\u002FAI制药理论\u002Fspatial-transcriptomics","md",1783051875974]