-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
451 lines (239 loc) · 293 KB
/
atom.xml
File metadata and controls
451 lines (239 loc) · 293 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Juse's Blog</title>
<subtitle>积跬步 至千里</subtitle>
<link href="https://biojuse.com/atom.xml" rel="self"/>
<link href="https://biojuse.com/"/>
<updated>2026-03-11T07:25:20.414Z</updated>
<id>https://biojuse.com/</id>
<author>
<name>Juse</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>hexo+butterfly 博客评论系统迁移(Valine+LeanCloud 到 Waline+Neon)</title>
<link href="https://biojuse.com/2026/03/09/hexo+butterfly%20%E5%8D%9A%E5%AE%A2%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E8%BF%81%E7%A7%BB%EF%BC%88Valine+LeanCloud%20%E5%88%B0%20Waline+Neon%EF%BC%89/"/>
<id>https://biojuse.com/2026/03/09/hexo+butterfly%20%E5%8D%9A%E5%AE%A2%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E8%BF%81%E7%A7%BB%EF%BC%88Valine+LeanCloud%20%E5%88%B0%20Waline+Neon%EF%BC%89/</id>
<published>2026-03-09T13:10:00.000Z</published>
<updated>2026-03-11T07:25:20.414Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在前不久,LeanCloud 发布<a href="https://docs.leancloud.cn/sdk/announcements/sunset-announcement">公告</a>,宣布其停止新用户注册和新应用创建,且将于 2027 年 1 月 12 日停止服务。</p><p>此时我刚捣鼓好 Valine 的邮件通知系统没有多久,但可惜的是我不得不面对现实。由于 Valine 官方基本沉寂,我大概率等不来一个好的替代方案(Valine 仅支持 LeanCloud 作为数据库)。在这段时间忙完自己的论文后,终于有了时间来处理博客事务,研究了一天成功将博客的评论系统进行了迁移,故有此文。</p><h2 id="评论系统迁移"><a href="#评论系统迁移" class="headerlink" title="评论系统迁移"></a>评论系统迁移</h2><h3 id="一些可能需要提前注意的事项"><a href="#一些可能需要提前注意的事项" class="headerlink" title="一些可能需要提前注意的事项"></a>一些可能需要提前注意的事项</h3><p>迁移到 Waline 可能需要你有一个<em><strong>自己的域名</strong></em>(因为要绑定一个子域名作为评论管理系统页面),如果你没有自己的域名,可以考虑购买一个或使用其他评论系统作为替代(即本文内容对你可能无用)。此外,本博客基于 <u>hexo+butterfly</u> 搭建,<strong>一些后续的调整可能在其他主题中不适用</strong>。</p><h3 id="Waline-部署"><a href="#Waline-部署" class="headerlink" title="Waline 部署"></a>Waline 部署</h3><p>这一部分内容主要来自:<a href="https://waline.js.org/guide/get-started/">Waline 官方教程 —— 快速上手</a>,并针对部分内容进行细节补充。</p><h4 id="部署服务端及创建数据库"><a href="#部署服务端及创建数据库" class="headerlink" title="部署服务端及创建数据库"></a>部署服务端及创建数据库</h4><p>这两个部分基本与上述官方教程无异,虽然当前 Vercel 页面布局可能存在些许不同,但总体流程没有变化,所以请根据官方教程进行操作:</p><ul><li>部署服务端:<a href="https://waline.js.org/guide/get-started/#%E9%83%A8%E7%BD%B2%E6%9C%8D%E5%8A%A1%E7%AB%AF">链接</a></li><li>创建数据库:<a href="https://waline.js.org/guide/get-started/#%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93">链接</a></li></ul><h4 id="绑定域名"><a href="#绑定域名" class="headerlink" title="绑定域名"></a>绑定域名</h4><p>在完成以上两步后,点击项目页面左侧的 <code>Domains</code>,在弹出页面里点击 <code>Add Existing</code> 将弹出以下窗口:</p><img src="/pic2/20260306_08.png" style="zoom: 50%;" /><p>此时,请根据你的域名,填入以下地址模板:<code>[你个人想要的前缀].[你的域名]</code>,以我的博客 <code>biojuse.com</code> 为例,我想要前缀代表评论,因此我最终的选择为:</p><ul><li><code>comment.biojuse.com</code>(<code>comment</code> 为前缀,<code>biojuse.com</code> 为域名)</li><li>这里使用的域名即后续的 <strong>serverURL</strong>。</li></ul><p>由于还没创建有关的子域名,此时其会显示错误(<code>Invalid Configuration</code>),点击 <code>Learn More</code> 我们可以看到以下信息:</p><p><img src="/pic2/20260306_03.png"></p><p><strong>复制保存此处的 <code>Value</code> 后</strong>,请先根据自己的域名服务商创造子域名。以<strong>腾讯云</strong>为例:</p><ol><li>打开腾讯云域名控制台:<a href="https://console.cloud.tencent.com/domain/all-domain/all">链接</a></li><li>选择自己的域名,点击<strong>解析</strong>。</li></ol><p><img src="/pic2/20260306_01.png"></p><ol start="3"><li>在 DNS 记录中,如果你在 GitHub 上托管博客,那么你应该已有几条与 GitHub 有关的记录,例如:</li></ol><p><img src="/pic2/20260306_02.png"></p><ol start="4"><li>新建记录,<strong>主机填写之前添加的 domain 前缀,记录类型选择 CNAME,记录值填写之前复制的 <code>Value</code>:</strong></li></ol><p><img src="/pic2/20260306_04.png"></p><p>填写完毕后,点击确认添加该记录,过段时间刷新 Vercel project 中 Domains(点击 <code>Refresh</code>),若其通过则上述工作完成:</p><p><img src="/pic2/20260306_05.png"></p><p>类似地,如果你的域名 DNS 是在 <strong>CloudFlare</strong> 中管理的,可以在左侧 <code>DNS-记录</code> 里进行类似操作:</p><center>添加的记录如图所示</center><p><img src="/pic2/20260306_06.png"></p><center>最后所有的记录大致如图</center><p><img src="/pic2/20260311_01.png"></p><p>至此,Waline 服务端/数据库/管理端均已完成。访问 <code><serverURL>/ui/register</code> 进行注册,<strong>首个注册的人会被设定成管理员</strong>。管理员登陆后,可以看到管理界面,并对评论进行相关操作。</p><h3 id="Hexo-Butterfly-切换至-Waline"><a href="#Hexo-Butterfly-切换至-Waline" class="headerlink" title="Hexo+Butterfly 切换至 Waline"></a>Hexo+Butterfly 切换至 Waline</h3><p>此处我使用的 Hexo+Butterfly 版本为 <code>V4.4.0</code>,该版本中要正常使用 Waline 评论系统,需要经过以下几点操作:</p><ol><li>在 <code>_config.butterfly.yml</code> 中修改 <code>comments</code> 并添加 waline 配置:</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">comments:</span></span><br><span class="line"> <span class="attr">use:</span> <span class="string">Waline</span></span><br><span class="line"> <span class="attr">text:</span> <span class="literal">true</span> <span class="comment"># Display the comment name next to the button</span></span><br><span class="line"> <span class="attr">lazyload:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">count:</span> <span class="literal">true</span> <span class="comment"># Display comment count in top_img</span></span><br><span class="line"></span><br><span class="line"><span class="attr">waline:</span></span><br><span class="line"> <span class="attr">serverURL:</span> <span class="string"><填写你的</span> <span class="string">serverURL,结尾无需斜杠></span></span><br><span class="line"> <span class="attr">bg:</span> <span class="comment"># waline background</span></span><br><span class="line"> <span class="attr">pageview:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">option:</span></span><br></pre></td></tr></table></figure><p><strong>可选</strong>:结合 waline.pug 以及<a href="https://waline.js.org/cookbook/customize/locale.html#locale-%E9%80%89%E9%A1%B9">官网文档</a>,如果想修改评论框中的文字(默认为 '欢迎评论'),可以在 <code>option</code> 中添加 <code>locale</code>,再在 <code>locale</code> 中添加 <code>placeholder</code>,在 <code>placeholder</code> 中指定你想替换的字符。更多 option 可见文档说明。</p><ol start="2"><li>由于最新的 Waline (v3) 在 butterfly v4.4.0 中存在问题,这里需<strong>手动修改配置文件使其替换为 Waline v2</strong>。具体而言,搜索并打开 butterfly theme 中的 <code>plugins.yml</code> 文件,将其中的 <code>waline_js</code> 及 <code>waline_css</code> 替换为以下内容:</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">waline_js:</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">'@waline/client@2'</span></span><br><span class="line"> <span class="attr">file:</span> <span class="string">dist/waline.min.js</span></span><br><span class="line"> <span class="attr">other_name:</span> <span class="string">waline</span></span><br><span class="line"> <span class="attr">version:</span> <span class="number">2.6</span><span class="number">.3</span></span><br><span class="line"><span class="attr">waline_css:</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">'@waline/client@2'</span></span><br><span class="line"> <span class="attr">file:</span> <span class="string">dist/waline.css</span></span><br><span class="line"> <span class="attr">other_name:</span> <span class="string">waline</span></span><br><span class="line"> <span class="attr">version:</span> <span class="number">2.6</span><span class="number">.3</span></span><br></pre></td></tr></table></figure><p>进行以上修改后,你应该能发现博客的评论系统正确地变为了 Waline。</p><h2 id="评论数据迁移"><a href="#评论数据迁移" class="headerlink" title="评论数据迁移"></a>评论数据迁移</h2><p>先前的评论并不在 Waline 中保存,为了保持评论数据一致,需要从其他系统迁移过来。</p><p>本文展示的为 <strong>Valine+LeanCloud 转移至 Waline</strong> 的操作方式,如果你的评论数据原先存储在其他平台,也可以参考本文的方式进行对应调整。</p><h3 id="从-LeanCloud-下载评论数据"><a href="#从-LeanCloud-下载评论数据" class="headerlink" title="从 LeanCloud 下载评论数据"></a>从 LeanCloud 下载评论数据</h3><p>打开 LeanCloud 控制台,选中自己博客的评论管理项目,点击左侧的 <code>数据存储</code>-<code>导入导出</code>,选择 <code>数据导出</code>,勾选 <code>限定 Class</code>,勾选 <code>Comment</code> 并点击导出。</p><p><img src="/pic2/20260306_07.png"></p><p>后续你会在邮箱里收到相关的文件,解压后会得到一个 <code>.jsonl</code> 文件,里面<strong>存储了博客中所有的评论数据</strong>,假设其名为 <code>a.jsonl</code>,留作后用。</p><h3 id="从-Waline-管理界面中导出数据"><a href="#从-Waline-管理界面中导出数据" class="headerlink" title="从 Waline 管理界面中导出数据"></a>从 Waline 管理界面中导出数据</h3><p>打开 Waline 管理界面,点击左上角 <code>管理</code>-<code>导入导出</code>,点击导出按钮获得文件 <code>waline.json</code>,这里导出数据的<strong>目的仅为获取管理员信息</strong>,并与 LeanCloud 数据合并。</p><h3 id="运行-Python-获得整合后的-Waline-评论数据"><a href="#运行-Python-获得整合后的-Waline-评论数据" class="headerlink" title="运行 Python 获得整合后的 Waline 评论数据"></a>运行 Python 获得整合后的 Waline 评论数据</h3><p>新建 python 脚本文件,输入以下内容(<strong>根据注释部分将一些文件路径换为自己对应的文件路径</strong>)并运行:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line">objs = []</span><br><span class="line"><span class="comment"># 将此处的 xxx.jsonl 换为你在 LeanCloud 导出并解压得到的文件</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">"xxx.jsonl"</span>, <span class="string">"r"</span>, encoding=<span class="string">"utf-8"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.readline()</span><br><span class="line"> <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line"> obj = json.loads(line)</span><br><span class="line"> objs.append(obj)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 将原先评论的结构映射到 Waline 系统中的结构</span></span><br><span class="line">target_attributes = [</span><br><span class="line"> <span class="string">"user_id"</span>,</span><br><span class="line"> <span class="string">"comment"</span>,</span><br><span class="line"> <span class="string">"ip"</span>,</span><br><span class="line"> <span class="string">"link"</span>,</span><br><span class="line"> <span class="string">"mail"</span>,</span><br><span class="line"> <span class="string">"nick"</span>,</span><br><span class="line"> <span class="string">"pid"</span>,</span><br><span class="line"> <span class="string">"rid"</span>,</span><br><span class="line"> <span class="string">"sticky"</span>,</span><br><span class="line"> <span class="string">"status"</span>,</span><br><span class="line"> <span class="string">"like"</span>,</span><br><span class="line"> <span class="string">"ua"</span>,</span><br><span class="line"> <span class="string">"url"</span>,</span><br><span class="line"> <span class="string">"objectId"</span>,</span><br><span class="line"> <span class="string">"insertedAt"</span>,</span><br><span class="line"> <span class="string">"createdAt"</span>,</span><br><span class="line"> <span class="string">"updatedAt"</span>,</span><br><span class="line">]</span><br><span class="line">new_objs = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> obj <span class="keyword">in</span> objs:</span><br><span class="line"> new_obj = {}</span><br><span class="line"> <span class="keyword">for</span> key <span class="keyword">in</span> target_attributes:</span><br><span class="line"> <span class="keyword">if</span> key <span class="keyword">in</span> obj:</span><br><span class="line"> <span class="keyword">if</span> key != <span class="string">"insertedAt"</span>:</span><br><span class="line"> new_obj[key] = obj[key]</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> new_obj[key] = obj[key][<span class="string">"iso"</span>]</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> new_obj[key] = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"> new_objs.append(new_obj)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将此处的 waline.json 换为你在 Waline 管理界面中导出的数据文件路径</span></span><br><span class="line">waline_json = json.load(<span class="built_in">open</span>(<span class="string">'waline.json'</span>, <span class="string">'r'</span>))</span><br><span class="line">waline_json[<span class="string">"data"</span>][<span class="string">"Comment"</span>] = new_objs</span><br><span class="line"></span><br><span class="line"><span class="comment"># 此处的 waline.integrate.json 即整合后的评论数据</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">"waline.integrate.json"</span>, <span class="string">"w"</span>, encoding=<span class="string">"utf-8"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> json.dump(waline_json, f, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">4</span>)</span><br></pre></td></tr></table></figure><p>以上脚本运行完成后,打开 Waline 管理界面,点击左上角 <code>管理</code>-<code>导入导出</code>,点击导入按钮,将输出的文件(此处即 <code>waline.integrate.json</code>)导入,即可将 LeanCloud 中的评论数据全部迁移至 Waline 中。</p><h2 id="开通邮件提示功能"><a href="#开通邮件提示功能" class="headerlink" title="开通邮件提示功能"></a>开通邮件提示功能</h2><p>Waline 开通邮件通知的方式与 Valine 极其相近,涉及到的环境变量详细可见:<a href="https://waline.js.org/guide/features/notification.html">Waline-功能-评论通知</a></p><p>具体而言,先打开 Vercel 中的对应项目,点击左侧栏中 <code>Settings</code>,再点击 <code>Environmental Variables</code>。在环境变量列表中,点击 <code>Add Environment Variable</code>,输入以下环境变量(<strong>加粗的 key 为必填</strong>,其他为选填):</p><table><thead><tr><th align="center">Key</th><th align="center">Value</th></tr></thead><tbody><tr><td align="center"><strong>SMTP_SERVICE</strong></td><td align="center">SMTP 邮件发送服务提供商(QQ 邮箱则填写 'QQ',163 邮箱则填写 '163')</td></tr><tr><td align="center"><strong>SMTP_USER</strong></td><td align="center">SMTP 邮件发送服务的用户名,一般为登录邮箱</td></tr><tr><td align="center"><strong>SMTP_PASS</strong></td><td align="center">SMTP 邮件发送服务的密码,开通 SMTP 及设置授权码的教程可见<a href="https://zhuanlan.zhihu.com/p/551399559">此处</a>,注意不要和登录密码混为一谈</td></tr><tr><td align="center"><strong>SMTP_SECURE</strong></td><td align="center">填写 'true' 即可</td></tr><tr><td align="center"><strong>SITE_NAME</strong></td><td align="center">网站名称(例如 Juse's blog)</td></tr><tr><td align="center"><strong>SITE_URL</strong></td><td align="center">网站网址(例如 <a href="https://biojuse.com)/">https://biojuse.com)</a></td></tr><tr><td align="center"><strong>AUTHOR_EMAIL</strong></td><td align="center">博主邮箱,用来接收新评论通知(可选择填写与 SMTP_USER 相同的值)</td></tr><tr><td align="center">SENDER_NAME</td><td align="center">自定义发送邮件的发件人(例如 Juse)</td></tr><tr><td align="center">SENDER_EMAIL</td><td align="center">自定义发送邮件的发件地址(可选择填写与 SMTP_USER 相同的值)</td></tr><tr><td align="center">MAIL_SUBJECT</td><td align="center">自定义评论回复邮件标题</td></tr><tr><td align="center">MAIL_TEMPLATE</td><td align="center">自定义评论回复邮件内容</td></tr><tr><td align="center">MAIL_SUBJECT_ADMIN</td><td align="center">自定义新评论通知邮件标题</td></tr><tr><td align="center">MAIL_TEMPLATE_ADMIN</td><td align="center">自定义新评论通知邮件内容</td></tr></tbody></table><p>后四个 Key 可以<strong>填写一些相关变量</strong>作为补充(具体可见<a href="https://waline.js.org/guide/features/notification.html#%E9%80%9A%E7%9F%A5%E6%A8%A1%E6%9D%BF">此处</a>),例如:</p><ul><li>MAIL_SUBJECT:<code>{{site.name|safe}} 上的评论收到了回复</code>,此处 <code>{{site.name|safe}}</code> 会变为 <strong>SITE_NAME</strong> 填写的值。</li><li>MAIL_SUBJECT_ADMIN:<code>{{site.name|safe}} 上有新评论了</code>,同上。</li><li>MAIL_TEMPLATE:以下给出一个博主本人基于 ChatGPT 润色生成的简易模板:</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"max-width:600px;margin:40px auto;padding:0 20px;font-family:Arial,Helvetica,sans-serif;color:#333;line-height:1.6;font-size:14px;background:#ffffff;border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.05);"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"padding:20px 25px;border-bottom:1px solid #f0f0f0;background:#f9fbfc;border-radius:8px 8px 0 0;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span> <span class="attr">style</span>=<span class="string">"font-size:16px;margin:0;color:#0c99c8;font-weight:600;"</span>></span>您在 <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"{{site.url}}"</span> <span class="attr">target</span>=<span class="string">"_blank"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"color:#0c99c8;text-decoration:none;"</span>></span>{{site.name|safe}}<span class="tag"></<span class="name">a</span>></span> 上的评论有新回复<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"padding:25px 25px 10px 25px;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin:0 0 10px 0;font-size:14px;"</span>></span>{{parent.nick}},您曾发表评论:<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"background:#f5f7f8;border-left:3px solid #0c99c8;padding:12px 15px;margin:12px 0;border-radius:4px;word-wrap:break-word;"</span>></span></span><br><span class="line"> {{parent.comment|safe}}<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin:15px 0 10px 0;font-size:14px;"</span>></span><span class="tag"><<span class="name">strong</span>></span>{{self.nick}}<span class="tag"></<span class="name">strong</span>></span> 回复说:<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"background:#f5f7f8;border-left:3px solid #5cb85c;padding:12px 15px;margin:12px 0;border-radius:4px;word-wrap:break-word;"</span>></span></span><br><span class="line"> {{self.comment|safe}}<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin-top:20px;font-size:14px;"</span>></span>您可以点击 <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"{{site.postUrl}}"</span> <span class="attr">target</span>=<span class="string">"_blank"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"color:#0c99c8;text-decoration:none;"</span>></span>查看完整回复<span class="tag"></<span class="name">a</span>></span>。欢迎再次访问 <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"{{site.url}}"</span> <span class="attr">target</span>=<span class="string">"_blank"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"color:#0c99c8;text-decoration:none;"</span>></span>{{site.name|safe}}<span class="tag"></<span class="name">a</span>></span>。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"padding:15px 25px;font-size:12px;color:#888;background:#fafafa;border-top:1px solid #f0f0f0;border-radius:0 0 8px 8px;text-align:center;"</span>></span></span><br><span class="line"> 本邮件为系统自动发送,请勿直接回复。<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><ul><li>MAIL_TEMPLATE_ADMIN:同上,给出博主本人模板:</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"max-width:600px;margin:40px auto;padding:0 20px;font-family:Arial,Helvetica,sans-serif;color:#333;line-height:1.6;font-size:14px;background:#ffffff;border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.05);"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"padding:20px 25px;border-bottom:1px solid #f0f0f0;background:#f9fbfc;border-radius:8px 8px 0 0;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span> <span class="attr">style</span>=<span class="string">"font-size:16px;margin:0;color:#0c99c8;font-weight:600;"</span>></span>您在 <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">'{{site.url}}'</span> <span class="attr">target</span>=<span class="string">'_blank'</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">'color:#0c99c8;text-decoration:none;'</span>></span>{{site.name|safe}}<span class="tag"></<span class="name">a</span>></span> 上的文章有了新的评论<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"padding:25px 25px 10px 25px;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin:0 0 10px 0;font-size:14px;"</span>></span><span class="tag"><<span class="name">strong</span>></span>{{self.nick}}<span class="tag"></<span class="name">strong</span>></span> 评论说:<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"background:#f5f7f8;border-left:3px solid #5cb85c;padding:12px 15px;margin:12px 0;border-radius:4px;word-wrap:break-word;"</span>></span></span><br><span class="line"> {{self.comment|safe}}<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin-top:20px;font-size:14px;"</span>></span>您可以点击 <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">'{{site.postUrl}}'</span> <span class="attr">target</span>=<span class="string">'_blank'</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">'color:#0c99c8;text-decoration:none;'</span>></span>查看评论的完整內容<span class="tag"></<span class="name">a</span>></span>。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"padding:15px 25px;font-size:12px;color:#888;background:#fafafa;border-top:1px solid #f0f0f0;border-radius:0 0 8px 8px;text-align:center;"</span>></span></span><br><span class="line"> 本邮件为系统自动发送,请勿直接回复。<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>在所有环境变量填写成功后,对 Vercel 项目进行<strong>重新部署</strong>,邮件提示功能即可生效。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>至此,所有的评论数据/功能已从 <code>Valine+LeanCloud</code> 移植到 <code>Waline+Neon</code>,在此特作记录,以帮助其他可能有需要的朋友。</p>]]></content>
<summary type="html">该文详细阐述了如何将博客评论从 Valine+LeanCloud 迁移至 Waline+Neon,包括绑定域名的具体操作、评论数据的处理和迁移、以及邮件通知功能的开通等。</summary>
<category term="博客" scheme="https://biojuse.com/categories/%E5%8D%9A%E5%AE%A2/"/>
</entry>
<entry>
<title>使用 PheTK 进行全表型组关联分析</title>
<link href="https://biojuse.com/2025/11/20/%E4%BD%BF%E7%94%A8%20PheTK%20%E8%BF%9B%E8%A1%8C%E5%85%A8%E8%A1%A8%E5%9E%8B%E7%BB%84%E5%85%B3%E8%81%94%E5%88%86%E6%9E%90/"/>
<id>https://biojuse.com/2025/11/20/%E4%BD%BF%E7%94%A8%20PheTK%20%E8%BF%9B%E8%A1%8C%E5%85%A8%E8%A1%A8%E5%9E%8B%E7%BB%84%E5%85%B3%E8%81%94%E5%88%86%E6%9E%90/</id>
<published>2025-11-20T07:00:00.000Z</published>
<updated>2025-11-21T03:01:47.244Z</updated>
<content type="html"><![CDATA[<html><head></head><body><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>全表型组关联分析(PheWAS,Phenome-wide Association Studies)是一类用于系统性探索遗传变异与多种表型之间关联的研究方法。与常见的全基因组关联分析(GWAS)相比,二者的视角恰好相反:</p><ul><li><strong>GWAS</strong> 关注的是“针对一个表型,在基因组范围内寻找哪些变异与之相关”;</li><li><strong>PheWAS</strong> 则关注“针对某个特定变异(或一类变异),在众多表型中寻找它会影响哪些性状”。</li></ul><p>近年来,随着测序规模的扩大与统计方法的发展,<strong>稀有变异(rare variants)在复杂性状中的作用逐渐受到重视</strong>。由于单个稀有变异往往难以单独检出显著关联,研究者通常采用 <em>burden analysis</em> 等策略将同一基因中的有害变异聚合起来,以提升统计功效。在典型的 burden 分析流程中,通常会:</p><ol><li><strong>识别基因中的功能有害变异</strong>,例如预测的 loss-of-function 变异或机器学习模型给出的高致病性错义变异;</li><li><strong>根据样本是否携带这些有害变异进行编码</strong>,编码方式可以是简单的二元(0/1),也可以使用携带数量作为计数值;</li><li><strong>对编码后的变量在多种表型上执行关联分析</strong>,从而识别哪些表型与该类有害变异显著相关。</li></ol><p>本文将介绍一个基于 Python 实现的 PheWAS 工具 —— <strong>PheTK</strong>,并展示其在实际分析中的使用方式。</p><h2 id="PheTK"><a href="#PheTK" class="headerlink" title="PheTK"></a>PheTK</h2><p>文章:<a href="https://doi.org/10.1093/bioinformatics/btae719">PheWAS analysis on large-scale biobank data with PheTK</a></p><p>github repository: <a href="https://github.com/nhgritctran/PheTK">https://github.com/nhgritctran/PheTK</a></p><p>安装可以通过 <code>pip</code> 进行:<code>pip install PheTK==0.1.47</code></p><p><u>目前该软件最新版本为 v0.2.2,但为确保内容无误此处指定的为博主使用的版本</u>,对最新版本感兴趣的朋友也可直接在 github 页面查看详细 guideline。</p><h3 id="需要准备的数据"><a href="#需要准备的数据" class="headerlink" title="需要准备的数据"></a>需要准备的数据</h3><ul><li>样本的 <strong>ICD-9 & ICD-10</strong> 记录。</li><li>样本的<strong>基本信息</strong>(例如性别、年龄、遗传结构前十个 PC 等,用作协变量)。</li><li><strong>感兴趣的变量</strong>(可以是上文提到的稀有变异 burden,也可以是其他个体属性)。</li></ul><p>后两者需单独整理成一个 csv 表格,例如:</p><img src="/pic2/20251119_01.png" width="700"><center>这里 age, sex, pc1-10 即后续分析中拟使用的协变量,burden 即通过处理得到的每个人的 rare burden</center><center>这些变量的选择因研究场景而异,请根据具体情况进行调整</center><h3 id="转换得到样本的-Phecode"><a href="#转换得到样本的-Phecode" class="headerlink" title="转换得到样本的 Phecode"></a>转换得到样本的 Phecode</h3><p>由于 ICD-9 & ICD-10 的种类众多,且部分 ICD 指代的疾病症状相近或重叠,因此一种名为 <strong>Phecode</strong> 的概念被提出,它可以当作是一个粗粒度更大的类别,即一个 Phecode 可能包含多个 ICD code,以更好地概括某一表型,并提高检测关联的效力。</p><p>在安装 PheTK 后,可以按照以下流程将样本的 ICD-9 & ICD-10 记录转换为 Phecode 携带信息:</p><ol><li><strong>首先,根据样本的 ICD-9 & ICD-10 记录,转换为以下格式的表格(csv 格式):</strong></li></ol><table><thead><tr><th align="right">person_id</th><th align="right">flag</th><th>ICD</th></tr></thead><tbody><tr><td align="right">sample1</td><td align="right">10</td><td>D17.1</td></tr><tr><td align="right">sample1</td><td align="right">10</td><td>F40.2</td></tr><tr><td align="right">sample1</td><td align="right">10</td><td>Z96.1</td></tr><tr><td align="right">sample2</td><td align="right">10</td><td>F32.9</td></tr><tr><td align="right">sample2</td><td align="right">10</td><td>S30.1</td></tr></tbody></table><p>其中:</p><ul><li>第一列 <code>person_id</code> 为样本的 ID,需要与后续进行关联分析时使用的样本 ID 对应。</li><li>第二列 <code>flag</code> 对应 ICD 版本,10 对应 ICD-10,如果为 ICD-9 则在对应位置填写 9。</li><li>第三列 <code>ICD</code> 对应具体的 ICD code。</li></ul><p>注意,<strong>同一个样本可以有多个重复的 ICD 记录</strong>(表示 ta 具有该疾病的多次诊断历史)。</p><ol start="2"><li><strong>通过以下 Python 命令将其转换为 Phecode count:</strong></li></ol><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> PheTK.Phecode <span class="keyword">import</span> Phecode</span><br><span class="line"></span><br><span class="line">phecode = Phecode(</span><br><span class="line"> platform=<span class="string">"custom"</span>,</span><br><span class="line"> icd_df_path=<span class="string">'ukb_icd9_icd10_record.csv'</span> <span class="comment"># 指定以上文件的路径</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">phecode.count_phecode(</span><br><span class="line"> phecode_version=<span class="string">"X"</span>, </span><br><span class="line"> icd_version=<span class="string">"WHO"</span>, </span><br><span class="line"> phecode_map_file_path=<span class="literal">None</span>,</span><br><span class="line"> output_file_name=<span class="string">"ukb_phecode_counts.icd9_icd10.csv"</span> <span class="comment"># 输出的 Phecode count 文件</span></span><br><span class="line">)</span><br></pre></td></tr></tbody></table></figure><p>此处可调节的参数为 <code>phecode.count_phecode()</code> 中的 <code>phecode_version</code> & <code>icd_version</code>:</p><ul><li><code>phecode_version</code>:指定<strong>使用的 Phecode 版本</strong>,默认为 <code>X</code>,另外的可选项是 <code>1.2</code>。</li><li><code>icd_version</code>:指定<strong>使用的 ICD 版本,默认为 <code>US</code>(对应 ICD-10-CM),另外的可选项是 <code>WHO</code>(对应 ICD-10)</strong>。需根据自身所用数据进行调整,例如此处的场景是使用 UKB 数据,因此使用 <code>WHO</code> 以对应数据库所采用版本。</li><li>如果有自定义的 Phecode 映射关系,也可以调整参数并使用 <code>phecode_map_file_path</code> 指定映射文件,感兴趣的朋友自行探索,此处不做深入介绍。</li></ul><p>输出的文件示例(上述命令的 <code>ukb_phecode_counts.icd9_icd10.csv</code>):</p><table><thead><tr><th align="right">person_id</th><th align="right">phecode</th><th>count</th></tr></thead><tbody><tr><td align="right">samplex</td><td align="right">ID_089</td><td>2</td></tr><tr><td align="right">samplex</td><td align="right">GU_602</td><td>1</td></tr><tr><td align="right">sampley</td><td align="right">BI_160</td><td>1</td></tr><tr><td align="right">samplez</td><td align="right">GI_522.9</td><td>1</td></tr><tr><td align="right">samplez</td><td align="right">GU_582.2</td><td>1</td></tr></tbody></table><p>输出的 Phecode count 文件中:</p><ul><li>第一列 <code>person_id</code> 与步骤 1 中的文件相同</li><li>第二列 <code>phecode</code> 对应 Phecode 体系中的编码,第三列 <code>count</code> 表示该个体具有多少该 Phecode 的记录。</li></ul><p>如果你对 <strong>ICD code 和 Phecode 之间的映射关系</strong>感兴趣,可以在 jupyter notebook 中运行以下命令查看:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> PheTK <span class="keyword">import</span> _utils</span><br><span class="line"></span><br><span class="line">phecode_df = _utils.get_phecode_mapping_table(</span><br><span class="line"> phecode_version=<span class="string">"X"</span>,</span><br><span class="line"> icd_version=<span class="string">"WHO"</span>,</span><br><span class="line"> phecode_map_file_path=<span class="literal">None</span>,</span><br><span class="line"> keep_all_columns=<span class="literal">True</span></span><br><span class="line">).to_pandas()</span><br><span class="line"></span><br><span class="line">phecode_df.head()</span><br></pre></td></tr></tbody></table></figure><h3 id="进行-PheWAS"><a href="#进行-PheWAS" class="headerlink" title="进行 PheWAS"></a>进行 PheWAS</h3><p>在处理完上述数据后,使用 PheTK 即可快速进行 PheWAS:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">python -m PheTK.PheWAS \</span><br><span class="line"> --phecode_version X \</span><br><span class="line"> --cohort_csv_path [cohort csv] \</span><br><span class="line"> --phecode_count_csv_path ukb_phecode_counts.icd9_icd10.csv \</span><br><span class="line"> --sex_at_birth_col sex \</span><br><span class="line"> --male_as_one False \</span><br><span class="line"> --covariates age sex pc1 pc2 pc3 pc4 pc5 pc6 pc7 pc8 pc9 pc10 \</span><br><span class="line"> --independent_variable_of_interest burden \</span><br><span class="line"> --min_case 50 \</span><br><span class="line"> --min_phecode_count 1 \</span><br><span class="line"> --threads 8 \</span><br><span class="line"> --output_file_name PheWAS_result.csv</span><br></pre></td></tr></tbody></table></figure><p>各个参数的含义:</p><ul><li><code>--phecode_version</code>:使用的 phecode 版本,应与 <code>phecode.count_phecode()</code> 使用的一致。</li><li><code>--cohort_csv_path</code>:可见前文部分 “需要准备的数据” 中的说明,该 csv 文件的列中需要包含 <code>--covariates</code> 和 <code>--independent_variable_of_interest</code> 指定的变量,此外<strong>需要有列名为 "person_id" 的列对应样本 ID</strong>。</li><li><code>--phecode_count_csv_path</code>:输出的 Phecode count 文件。</li><li><code>--sex_at_birth_col</code>:性别信息对应的列名,在某些性别特异的 Phecode 里需要根据性别信息取出特定性别的个体(例如流产只有女性、精子畸形只有男性)。</li><li><code>--male_as_one</code>:性别列中,男性的编码方式是否为 1,如果是则填写 <code>True</code>。</li><li><code>--covariates</code>:拟合模型时使用的<strong>协变量</strong>。</li><li><code>--independent_variable_of_interest</code>:感兴趣的<strong>目标变量</strong>。</li><li><code>--min_case</code> & <code>--min_phecode_count</code>:前者用于筛选 case(可一定程度上筛选掉偶然患病的情况,但也可能错失真实信号),后者用于过滤包含病例过少的 Phecode。此处即:当 <strong>phecode count 至少为 1 时,才将该个体视作病例,并过滤包含病例不多于 50 个的 Phecode</strong>。</li><li><code>--threads</code>:使用的线程数。需注意部分 Scipy 版本下该参数的调节不起作用,建议配合 slurm 等作业调度系统对其使用的线程数进行限制,<strong>过高的线程数有时并不会加快运行速度</strong>。</li><li><code>--output_file_name</code>:输出的 PheWAS 结果,可基于该结果进行可视化。</li></ul><p>可视化也可以通过其内置的绘图函数进行:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> PheTK.Plot</span><br><span class="line"></span><br><span class="line">p = PheTK.Plot.Plot(<span class="string">"PheWAS_result.csv"</span>)</span><br><span class="line">p.manhattan(</span><br><span class="line"> label_values=<span class="string">"p_value"</span>, label_count=<span class="number">5</span>, label_size=<span class="number">7</span>, legend_marker_size=<span class="number">7</span>,</span><br><span class="line"> title=<span class="string">"PheWAS result"</span>, save_plot=<span class="literal">False</span></span><br><span class="line">)</span><br></pre></td></tr></tbody></table></figure><img src="/pic2/20251120_01.png" width="700"><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><p>本文章介绍的 <strong>PheTK 主要是根据个体的 ICD 携带情况,对其做分类后,再将每一种 Phecode 视作一种表型进行关联分析</strong>。</p><p>其主要的原理即构建逻辑斯蒂回归模型,并进行拟合得到相关的统计量。因此,如果你关注的全表型组和本文所说的 ICD 有所区别,你也可以按照这种思路自行进行相关的 PheWAS。事实上,你仅需将你关注的表型组整合到上述所说的 cohort_csv 中,你就可以自行构建模型并对每个表型进行相关的关联测试(例如<a href="https://www.nature.com/articles/s41586-018-0571-7">这篇文章</a>)。</p><p>作为参考,PheTK 进行关联分析的方式你可以详细浏览其源码中的 phewas.py (<a href="https://github.com/nhgritctran/PheTK/blob/main/src/phetk/phewas.py">link</a>)。如果你关注的表型是连续型数值而非二元分类,你可以考虑将相关的模型替换为最小二乘回归(<code>sm.OLS</code>)。</p><h2 id="声明"><a href="#声明" class="headerlink" title="声明"></a>声明</h2><p>本文章的<strong>前言部分</strong>使用了 ChatGPT v5.1 进行润色,并人工核对了润色后内容的准确性。</p></body></html>]]></content>
<summary type="html">本文介绍了如何使用 PheTK 在 python 上进行 PheWAS 分析,其中包括详细的数据准备步骤和分析流程介绍。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>使用 igv-reports 进行基因组数据可视化</title>
<link href="https://biojuse.com/2025/05/24/%E4%BD%BF%E7%94%A8%20igv-reports%20%E8%BF%9B%E8%A1%8C%E5%9F%BA%E5%9B%A0%E7%BB%84%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%86%E5%8C%96/"/>
<id>https://biojuse.com/2025/05/24/%E4%BD%BF%E7%94%A8%20igv-reports%20%E8%BF%9B%E8%A1%8C%E5%9F%BA%E5%9B%A0%E7%BB%84%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%86%E5%8C%96/</id>
<published>2025-05-24T11:00:00.000Z</published>
<updated>2025-08-21T08:06:46.123Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>基因组数据分析中,Integrative Genomics Viewer(IGV)以其直观的交互式界面和丰富的可视化功能,成为我们查看变异和注释等基因组信息的首选工具。然而,在某些情景下 IGV 的可视化优势难以发挥:基因组数据在服务器上,但在服务器中无法方便地调用图形页面,而基因组数据下载到本地再使用 IGV 可视化又过于不便且耗时(特别是在数据量庞大的情况下)。</p><p>针对这一问题,igv-reports 成为了一个很好的替代工具。它能够将 IGV 的会话内容和基因组浏览快照一并打包成静态 HTML 报告。生成的报告文件下载到本地后,用户可直接在浏览器中查看和交互,操作体验与 IGV 类似,同时支持灵活分享,兼顾效率与便捷性。</p><img src="/pic2/20250524145330.png" width="700"><center>Source: https://igvteam.github.io/igv-reports/examples/example_vcf.html</center><p>需要注意的是,<strong>igv-reports 适用于可视化基因组部分区域时使用</strong>,例如你关注的是某些变异位置或特定基因附近的比对情况/信号时,使用 igv-reports 是合适的。但如果你想要大尺度地进行可视化(例如展示整条染色体的变化情况等),该方法可能具有一定局限性(见下文)。请根据自己的需求选择合适的方案。</p><h2 id="igv-reports-安装"><a href="#igv-reports-安装" class="headerlink" title="igv-reports 安装"></a>igv-reports 安装</h2><p>关于 igv-reports 的运行,最全面的资料请前往 <a href="https://github.com/igvteam/igv-reports">igv-reports github page</a>。</p><p>igv-reports 需要 python 3.8 及以上版本,可使用 conda 或 pip 安装:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">conda</span></span><br><span class="line">conda install bioconda::igv-reports</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">pip</span></span><br><span class="line">pip install igv-reports</span><br></pre></td></tr></tbody></table></figure><p>如果遇到依赖问题,可检查是否是 pysam 未正常安装,如果是,可通过 conda 解决:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install bioconda::pysam</span><br></pre></td></tr></tbody></table></figure><h2 id="igv-reports-使用"><a href="#igv-reports-使用" class="headerlink" title="igv-reports 使用"></a>igv-reports 使用</h2><p>igv-reports 可用于多种类型的数据可视化,本文将详细介绍其中一个使用示例,涉及到<strong>可视化变异位点及其附近的比对情况</strong>。为了复现该使用示例,你可以运行以下命令以克隆 igv-reports repository 并移动至有关文件夹:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/igvteam/igv-reports.git</span><br><span class="line">cd igv-reports</span><br></pre></td></tr></tbody></table></figure><h3 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h3><p>完成上述操作后,运行以下命令:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">create_report test/data/variants/variants.vcf.gz \</span><br><span class="line"> --genome hg38 \</span><br><span class="line"> --ideogram test/data/hg38/cytoBandIdeo.txt \</span><br><span class="line"> --flanking 1000 \</span><br><span class="line"> --info-columns GENE TISSUE TUMOR COSMIC_ID GENE SOMATIC \</span><br><span class="line"> --samples reads_1_fastq \</span><br><span class="line"> --sample-columns DP GQ \</span><br><span class="line"> --tracks \</span><br><span class="line"> test/data/variants/variants.vcf.gz \</span><br><span class="line"> test/data/variants/recalibrated.bam \</span><br><span class="line"> test/data/hg38/refGene.txt.gz \</span><br><span class="line"> --output example_vcf.html</span><br></pre></td></tr></tbody></table></figure><p>输出的页面概览可见(结合该页面以理解下述参数说明):<a href="https://igvteam.github.io/igv-reports/examples/example_vcf.html">https://igvteam.github.io/igv-reports/examples/example_vcf.html</a></p><p>重点理解这里面各个参数的作用:</p><ul><li>输入文件为 <code>test/data/variants/variants.vcf.gz</code>,除了 VCF 文件外,也可提供 BED 或 MAF 等以制表符分隔的格式文件。需注意<strong>使用 VCF 文件时部分参数的表现会有些许不同</strong>。</li><li><code>--genome</code>:指定参考基因组版本。也<strong>可使用 <code>--fasta</code> 替换该参数并自行指定基因组序列文件路径</strong>(需建好索引)。</li><li><code>--ideogram</code>:染色体带型文件路径,用于<strong>绘制染色体带型</strong>,非必需。</li><li><code>--flanking</code>:指定<strong>目标区域两侧扩展的碱基数</strong>(默认 1000),仅扩展后的区域中 track 信息会被保留。</li><li><code>--info-columns</code>:包含在<strong>页面上方表格(此例中即变异表格)中的信息字段名称列表。</strong>如果输入的文件为 VCF,则这里给定的列表对应 INFO 列中的字段。如果输入的文件为其他制表符分隔格式,则对应列名。</li></ul><img src="/pic2/20250524163444.png" width="700"><center>该示例文件中的 INFO 字段信息</center><ul><li><code>--samples</code> & <code>--sample-columns</code>:包含在<strong>变异表格中的样本信息</strong>。<code>--samples</code> 未指定时默认使用全部样本,<code>--sample-columns</code> 指定具体展示样本的哪些信息(此例中即展示深度 DP 及基因型质量 GQ)。</li><li><code>--tracks</code>:展示的 track,支持多种文件类型,如比对文件(BAM/CRAM)、变异数据(VCF)、注释文件(GFF3/GTF)及基因模型(UCSC genePred)等。<strong>此处分别指定了变异 VCF 文件、比对文件和 UCSC genePred 注释文件</strong>。</li><li><code>--output</code>:输出的 html 报告文件。</li></ul><p>最详细的参数说明可见:<a href="https://github.com/igvteam/igv-reports?tab=readme-ov-file#creating-a-report">https://github.com/igvteam/igv-reports?tab=readme-ov-file#creating-a-report</a></p><p>更多的命令示例可见:<a href="https://github.com/igvteam/igv-reports?tab=readme-ov-file#examples">https://github.com/igvteam/igv-reports?tab=readme-ov-file#examples</a></p><p>此处将额外提及一些其他<strong>需要注意</strong>&<strong>使用场景更多</strong>的参数。</p><h3 id="其他场景及需注意参数"><a href="#其他场景及需注意参数" class="headerlink" title="其他场景及需注意参数"></a>其他场景及需注意参数</h3><p>使用除 VCF 文件以外的制表符格式文件时:</p><ul><li><code>--sequence</code> 参数用于指定染色体名称所在的列数。</li><li><code>--begin</code> 及 <code>--end</code> 分别对应起始位置和终止位置。</li><li>使用 <code>--info-columns</code> 可以限制展示的信息。</li></ul><p>在 track 中包含 BAM/CRAM 等比对文件时:</p><ul><li><code>--exclude-flags</code> 用于过滤比对,其默认值为 1536(即过滤重复和不可靠的比对)。将该值设为 0 将不做过滤。</li></ul><h3 id="有关局限性"><a href="#有关局限性" class="headerlink" title="有关局限性"></a>有关局限性</h3><p>经过测试,igv-reports 的主要时间开销在于加载 track 文件和基因组文件中,因此运行速度上不需要过于担心。但需要注意,其<strong>输出文件的大小与展示的区域大小有关</strong>,上述示例的输出文件仅 1mb 左右是由于其仅涉及到 13 个变异位点及其两侧 1kbp 区域,如果想可视化整条染色体序列,输出文件就会大很多(作为参考:我使用 5 个 track 的人类 22 号染色体可视化结果文件大小为 1.45G)。</p><p>综上,若需展示大范围区域(如整条染色体),建议优先使用 IGV 本地客户端。igv-reports 更适用于靶向分析(如特定基因或变异位点),以平衡可视化效果与文件体积。</p></body></html>]]></content>
<summary type="html">这篇文章介绍了基于 python 开发的应用程序 igv-reports,其可整合各种基因组数据生成 HTML 报告(无需依赖原始输入数据),适用于无法使用可视化交互界面的情况(例如在远程 Linux 服务器上等)。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>linux 中使用 du 排查文件数量过大的目录</title>
<link href="https://biojuse.com/2025/04/14/linux%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20du%20%E6%8E%92%E6%9F%A5%E6%96%87%E4%BB%B6%E6%95%B0%E9%87%8F%E8%BF%87%E5%A4%A7%E7%9A%84%E7%9B%AE%E5%BD%95/"/>
<id>https://biojuse.com/2025/04/14/linux%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20du%20%E6%8E%92%E6%9F%A5%E6%96%87%E4%BB%B6%E6%95%B0%E9%87%8F%E8%BF%87%E5%A4%A7%E7%9A%84%E7%9B%AE%E5%BD%95/</id>
<published>2025-04-14T08:00:00.000Z</published>
<updated>2025-04-14T08:17:41.416Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>这一段时间对服务器进行备份时发现有些目录的扫描速度非常慢,经调查这些目录大多都是某些 pipeline 在运行时产生的中间文件,虽然其存储占用并不大但由于文件小且多导致在处理这一部分目录时不必要地产生了过大的时间开销,遂在网上寻找定位这些目录的方法并觉得可以水篇博客,固有此文。</p><p>这篇文章将介绍如何使用 du 识别出那些包含文件数量过大的目录。</p><h2 id="基础介绍"><a href="#基础介绍" class="headerlink" title="基础介绍"></a>基础介绍</h2><p>每个文件/目录在 Linux 中都有一个 <strong>Inode(Index Node)</strong>,文件系统创建时有固定的 Inode 总数,用尽会导致无法新建文件,而过大的 Inode 数量也会导致一些困扰(在我的应用场景中即<strong>部分命令的运行时间开销大幅增加</strong>),可以使用 <code>df -ih</code> 查看当前各个文件系统的的 Inode 情况:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">df</span> -ih</span></span><br><span class="line">Filesystem Inodes IUsed IFree IUse% Mounted on</span><br><span class="line">devtmpfs 63M 1.1K 63M 1% /dev</span><br><span class="line">tmpfs 63M 30K 63M 1% /dev/shm</span><br><span class="line">/dev/nvme0n1p4 353M 3.8M 349M 2% /</span><br><span class="line">/dev/sda 2.3G 155M 2.2G 7% /xxx</span><br></pre></td></tr></tbody></table></figure><p><code>du</code>(Disk Usage)是 Linux 中用于<strong>分析文件和目录占用磁盘空间</strong>的核心工具。虽然该工具最常被用来分析存储占用情况,但是其也具有 Inode 统计的功能。</p><h2 id="文件数量统计"><a href="#文件数量统计" class="headerlink" title="文件数量统计"></a>文件数量统计</h2><p>使用 du 统计包含文件数量过大目录的方法:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">du</span> --inodes --threshold=100000 [path]</span> </span><br></pre></td></tr></tbody></table></figure><p>参数介绍:</p><ul><li><code>--inodes</code>:列出 Inode 使用情况而非存储使用情况。</li><li><code>--threshold</code>:打印的最低阈值,该情景下即至少 10w 个 Inode 才打印。</li><li><code>[path]</code>:扫描的路径。</li></ul><p>需注意,<strong>每个目录(或者说文件夹)本身就占用 1 个 Inode</strong>。根据自身需求,你可以考虑使用以下参数进行灵活调整:</p><ul><li><code>-S</code> / <code>--separate-dirs</code>:指定后,对每个目录的统计将<strong>不包含其子目录信息</strong>,即子目录的值不会被累加到其父目录中。</li><li><code>-d</code> / <code>--max-depth</code>:<strong>最大的扫描深度</strong>,例如 <code>-d 6</code> 表示只统计到 6 级子目录,深度更高的子目录则不会被打印,但其包含的 Inode 依然会被累加父目录中(不使用 <code>-S</code> 参数的情况下)。</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>Stack Exchange askubuntu "Find directories with lots of files in"</p><p><a href="https://askubuntu.com/questions/316027/find-directories-with-lots-of-files-in">https://askubuntu.com/questions/316027/find-directories-with-lots-of-files-in</a></p></body></html>]]></content>
<summary type="html">这篇文章介绍了如何使用 du 命令排查那些包含文件数量过大的目录,以作为清理空间时的参考。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>trim_galore 后 Per Base Sequence Content 出现问题的原因</title>
<link href="https://biojuse.com/2025/04/14/trim_galore%20%E5%90%8E%20Per%20Base%20Sequence%20Content%20%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E5%8E%9F%E5%9B%A0/"/>
<id>https://biojuse.com/2025/04/14/trim_galore%20%E5%90%8E%20Per%20Base%20Sequence%20Content%20%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E5%8E%9F%E5%9B%A0/</id>
<published>2025-04-14T07:00:00.000Z</published>
<updated>2025-04-14T08:15:13.569Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>最近在处理基因组数据时,遇到的大多数据都已经处理过,所以 fastQC 里基本没有问题,可以直接用来比对。不过这几天有一个数据比较异常,所以按照惯例地跑了一下 <code>trim_galore</code>,发现了一个意料之外的情况:</p><ul><li>质控前,所有数据都有 Adapter Content 的警告,但是其他方面没有问题:</li></ul><img src="/pic2/20250414113239.png" width="600px"><ul><li>质控后,Adapter Content 的警告消失,但是 Per Base Sequence Content 全部爆红:</li></ul><img src="/pic2/20250414143318.png" width="600px"><p>经过比较,原因都是 reads 末端的碱基组成异常导致:</p><img src="/pic2/20250414144930.png" width="600px"><p>经调查在 Trim Galore github 中已经有人提到过(<a href="https://github.com/FelixKrueger/TrimGalore/issues/81">issue #81</a>),这里简单阐述下原因:</p><p>Trim Galore 在过滤时采用非常严格的策略,具体来说,<strong>它并不只在接头完全匹配时才进行移除,而是在末端开始进行逐个匹配</strong>。例如,假设识别到的接头序列为 <code>AGATCGGAAGAGC</code>,而某一个 reads 从末端开始为 <code>ACCTCG</code>,虽然这里从第二个碱基开始就不能和接头匹配上,但是<strong>由于第一个碱基 <code>A</code> 和接头相匹配,因此被 Trim Galore 移除</strong>,这也是为什么能在运行 <code>trim_galore</code> 后发现最末端位置上碱基 A 的比例是 0。</p><img src="/pic2/20250414151201.png" width="600px"><center>同理,如果前两个碱基都匹配则前两个碱基被移除</center><p>这一行为的背后动机是在亚硫酸氢盐测序数据分析中 ,reads 里如果掺入人工序列将会对结果造成很大影响,因为每个碱基都可能参与最终的甲基化状态判断,所以此处采用了一个看上去略显极端的方式来避免该问题的发生。调整这一行为的方式也很简单,<code>trim_galore</code> 的参数 <code>--stringency</code> 默认指定为 1,将其提升到 n 就会使 Trim Galore 在至少 n 个碱基与接头匹配时进行移除。不过此处开发者强调<strong>即使默认设置会使数据在 fastQC 里表现异常,但它大概率不会对之后的 mapping 产生任何影响</strong>。</p><p>在此记录,希望能帮助到其他有同样疑惑的人。</p></body></html>]]></content>
<summary type="html">trim_galore 质控后 fastQC 报告中会多出一些异常的地方,本文旨在解释相关的原因。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>基于 genmap 计算基因组 mappability</title>
<link href="https://biojuse.com/2025/03/22/%E5%9F%BA%E4%BA%8E%20genmap%20%E8%AE%A1%E7%AE%97%E5%9F%BA%E5%9B%A0%E7%BB%84%20mappability/"/>
<id>https://biojuse.com/2025/03/22/%E5%9F%BA%E4%BA%8E%20genmap%20%E8%AE%A1%E7%AE%97%E5%9F%BA%E5%9B%A0%E7%BB%84%20mappability/</id>
<published>2025-03-22T03:30:00.000Z</published>
<updated>2025-08-22T05:00:53.321Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>基因组的 mappability 是指测序产生的短读长能够被<strong>唯一且准确比对</strong>到参考基因组特定区域的能力。它反映了基因组不同区域因序列特征(如重复序列、低复杂度区域等)对比对结果可靠性的影响。例如,高度重复区域(如 Alu 元件)的 mappability 通常较低,因为 reads 可能匹配到多个位置。</p><p>在实际分析中,有时我们会过滤低 mappability 区域的突变,避免因比对错误导致的假阳性误判。本文将介绍如何使用生物信息学工具 GenMap 获得基因组的 mappability 信息。</p><h2 id="GenMap"><a href="#GenMap" class="headerlink" title="GenMap"></a>GenMap</h2><p>GenMap github page:</p><p><a href="https://github.com/cpockrandt/genmap">https://github.com/cpockrandt/genmap</a></p><p>GenMap paper:</p><p><strong>GenMap: ultra-fast computation of genome mappability</strong></p><p><a href="https://doi.org/10.1093/bioinformatics/btaa222">https://doi.org/10.1093/bioinformatics/btaa222</a></p><p>相较于先前的 mappability 计算工具(例如 GEM),GenMap 的搜索策略有所优化并且算法不依赖于启发式方法,因此具有更高的运行效率和准确性。此外 GenMap 支持多种输出格式,便于下游灵活分析。</p><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>推荐通过 conda 安装 GenMap :</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install -c bioconda genmap</span><br></pre></td></tr></tbody></table></figure><p>也可以通过下载源码进行编译:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git clone --recursive https://github.com/cpockrandt/genmap.git</span><br><span class="line">mkdir genmap-build && cd genmap-build</span><br><span class="line">cmake ../genmap -DCMAKE_BUILD_TYPE=Release</span><br><span class="line">make genmap</span><br></pre></td></tr></tbody></table></figure><p>运行以下命令以检查安装是否完成:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">genmap --help</span><br></pre></td></tr></tbody></table></figure><h3 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h3><h4 id="构建索引"><a href="#构建索引" class="headerlink" title="构建索引"></a>构建索引</h4><p>在计算 mappability 前,需要对目标基因组文件建立索引:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">genmap index -F /path/to/fasta.fasta -I /path/to/index/folder</span><br></pre></td></tr></tbody></table></figure><p><code>-F</code> 指定参考基因组序列路径,<code>-I</code> 指定索引输出目录位置,请注意<strong>此处需要的内存较大</strong>:</p><blockquote><p>It needs about <code>6n</code> space in main memory (or <code>10n</code> for fasta files >2GB). <code>n</code> is the number of bases in your fasta file(s). It might be more or less depending on the number and length of the individual sequences.</p></blockquote><p>如果你分析的物种为常见的模式生物,可以在以下表格下载 GenMap 提前构建好的索引文件:</p><table><thead><tr><th><strong>Genome</strong></th><th><strong>Index size (compressed)</strong></th><th><strong>Download</strong></th></tr></thead><tbody><tr><td>Human GRCh38 [<a href="https://github.com/cpockrandt/genmap#id4">1]</a></td><td>5.4 GB</td><td><a href="http://ftp.imp.fu-berlin.de/pub/cpockrandt/genmap/indices/grch38-no-alt.tar.gz">GRCh38 index</a></td></tr><tr><td>Human hs37-1kg [<a href="https://github.com/cpockrandt/genmap#id5">2]</a></td><td>5.4 GB</td><td><a href="http://ftp.imp.fu-berlin.de/pub/cpockrandt/genmap/indices/hs37-1kg.tar.gz">hs37-1kg index</a></td></tr><tr><td><a href="http://hgdownload.soe.ucsc.edu/goldenPath/mm10/chromosomes(mergedintoonefastafile)">Mouse GRCm38</a></td><td>4.9 GB</td><td><a href="http://ftp.imp.fu-berlin.de/pub/cpockrandt/genmap/indices/grcm38.tar.gz">GRCm38 index</a></td></tr><tr><td><a href="http://hgdownload.soe.ucsc.edu/goldenPath/dm6/bigZips/dm6.fa.gz">D. melanogaster dm6</a></td><td>0.2 GB</td><td><a href="http://ftp.imp.fu-berlin.de/pub/cpockrandt/genmap/indices/dm6.tar.gz">dm6 index</a></td></tr><tr><td><a href="http://hgdownload.soe.ucsc.edu/goldenPath/ce11/chromosomes(mergedintoonefastafile)">C. elegans ce11</a></td><td>0.1 GB</td><td><a href="http://ftp.imp.fu-berlin.de/pub/cpockrandt/genmap/indices/ce11.tar.gz">ce11 index</a></td></tr><tr><td>Wheat T. aestivum ta45 [<a href="https://github.com/cpockrandt/genmap#id6">3]</a></td><td>21.9 GB</td><td><a href="http://ftp.imp.fu-berlin.de/pub/cpockrandt/genmap/indices/ta45.tar.gz">ta45 index</a></td></tr></tbody></table><center>Source: https://github.com/cpockrandt/genmap#pre-built-indices</center><p>如果你拟分析的物种不在该列表中且内存量不足,可以考虑调整 <code>genmap index</code> 的 <code>-S</code> 参数至 20,不过这会导致后续计算 mappability 的速度减慢。</p><h4 id="计算-mappability"><a href="#计算-mappability" class="headerlink" title="计算 mappability"></a>计算 mappability</h4><p>在构建好索引后,可以运行以下命令计算基因组的 mappability:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">genmap map -K 100 -E 2 -I /path/to/index/folder -O /path/to/output/folder -w</span><br></pre></td></tr></tbody></table></figure><p>各参数的含义如下:</p><ul><li><code>-K</code>:使用的 k-mer 长度,此处<strong>应当依据自己使用的测序数据读长决定</strong>,例如如果你使用的是 150bp 的 reads 数据,那么该参数应当调整为 150 以对应实际比对时的情况。</li><li><code>-E</code>:允许的错配数量,此处<strong>错配数量的选择需根据应用场景决定</strong>,例如 CRISPR 设计需要特定的错配容忍度、在变异调用中映射 reads 时允许一定的碱基错配数量等。</li><li><code>-I</code>:索引文件所处路径 。</li><li><code>-O</code>:结果文件输出路径,可包含文件前缀,例如 <code>./hg38.mappability.k30_e2</code>(此时输出路径为当前所在目录)。</li><li><code>-w</code>:输出 wig 文件格式,此外可选的还有 <code>-t</code> / <code>-bg</code> / <code>-d</code> 等参数,对应的文件具体信息可见 <code>genmap map --help</code>。</li></ul><p>计算得到的 mappbility 值即 k-mer 出现次数的倒数,1 表示该 k-mer 在基因组内是唯一的,0.5 表示出现两次,0.33 表示出现三次,以此类推。越高的值代表该区域的可映射性越强。</p><p>在输出 wig 文件后,可通过 <code>wigToBigWig</code> 将 wig 文件转为 bigwig 文件(可选):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装</span></span><br><span class="line">conda install bioconda::ucsc-wigtobigwig</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">使用</span></span><br><span class="line">wigToBigWig [input wig] [chrom size file] [output bigwig]</span><br></pre></td></tr></tbody></table></figure><p>各参数的含义如下:</p><ul><li><code>[input wig]</code>:输入的 wig 文件。</li><li><code>[chrom size file]</code>:染色体大小信息文件,可以通过 <code>samtools faidx</code> 获得。</li><li><code>[output bigwig]</code>:输出的 bigwig 文件。</li></ul><h3 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h3><p>需要注意的是,GenMap 是为了<strong>短 reads 和最多 4 个错配</strong>开发的,并且目前并不支持 InDel(插入/缺失)的计算。</p><p>此外,你可以在 <code>genmap map</code> 的运行中通过指定 <code>-S</code> 参数,以计算特定区域内的 mappability:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">genmap map -S exome.bed -K 100 -E 2 -I /path/to/index/folder -O /path/to/output/folder -w</span><br></pre></td></tr></tbody></table></figure><p>以上述命令为例,你可以将 <code>exome.bed</code> 替换为自己的<strong>外显子组坐标文件</strong>,以使 genmap 仅对外显子组中的区域进行计算。</p><h2 id="参考材料"><a href="#参考材料" class="headerlink" title="参考材料"></a>参考材料</h2><ol><li><p><a href="https://doi.org/10.1093/bioinformatics/btaa222">GenMap: ultra-fast computation of genome mappability</a></p></li><li><p><a href="https://github.com/cpockrandt/genmap/issues/19">https://github.com/cpockrandt/genmap/issues/19</a></p></li><li><p><a href="https://github.com/cpockrandt/genmap/issues/5">https://github.com/cpockrandt/genmap/issues/5</a></p></li><li><p><a href="https://github.com/cpockrandt/genmap/issues/24">https://github.com/cpockrandt/genmap/issues/24</a></p></li></ol></body></html>]]></content>
<summary type="html">本文介绍了如何使用 genmap 快速获得基因组的 mappability。同时说明了如何指定特定区域进行计算(例如全外显子组区域的 mappability)。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>VCF 文件转坐标的方法汇总及注意事项</title>
<link href="https://biojuse.com/2025/02/12/VCF%20%E6%96%87%E4%BB%B6%E8%BD%AC%E5%9D%90%E6%A0%87%E7%9A%84%E6%96%B9%E6%B3%95%E6%B1%87%E6%80%BB%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/"/>
<id>https://biojuse.com/2025/02/12/VCF%20%E6%96%87%E4%BB%B6%E8%BD%AC%E5%9D%90%E6%A0%87%E7%9A%84%E6%96%B9%E6%B3%95%E6%B1%87%E6%80%BB%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/</id>
<published>2025-02-12T08:00:00.000Z</published>
<updated>2025-04-14T03:22:54.356Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>历史数据的整合分析往往涉及不同时期使用不同参考基因组版本产生的数据。例如,某些较早的研究可能基于 hg19/GRCh37 进行分析,而新的研究多采用 hg38/GRCh38。<strong>为了进行比较分析,需要将这些数据统一到同一个版本</strong>。</p><p>坐标转换是通过 chain file 来实现的,<strong>该文件记录了两个基因组版本之间的对应关系</strong>,包含了基因组重排、插入和删除等信息。转换工具会根据这些信息将原始坐标映射到目标版本的相应位置。</p><h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p>本文将介绍如何使用两款主流工具 <strong><u>CrossMap</u></strong> 和 <strong><u>GATK LiftoverVcf</u></strong> 以及最近的一个新工具 <strong><u>BCFtools liftover</u></strong> 进行 VCF 文件的坐标转换。在说明之前,首先简要介绍三个软件的差别:</p><ul><li>CrossMap 除了 VCF 的坐标映射外,也支持很多其他文件格式的坐标转换。对于 VCF 文件而言,CrossMap 并不要求其具有完备的 Meta-information 和 Header line 部分,<strong>只需要突变数据行符合格式即可</strong>。</li><li>GATK LiftoverVcf 则主要支持对 VCF 文件的坐标进行转换。其<strong>要求 VCF 文件具有完整的 Meta-information 和 Header line 部分</strong>。相对 CrossMap 而言对 VCF 文件格式的要求更加严格。</li><li>BCFtools liftover 在前两者的基础上,<strong>对 InDel 的转换逻辑进行了完善,从而支持更准确的 InDel 坐标转换</strong>,并且相对而言功能更加完善齐全(例如多等位基因的支持、INFO 字段的更新等)& 效率更高。其对 Meta-information 的要求更加严格,需要包含原始坐标的 contig 长度信息。</li></ul><img src="/pic2/20250414112201.png" width="800px"><center>各种坐标转换工具的内存使用和时间开销对比</center><p>VCF 文件格式详解:<a href="https://samtools.github.io/hts-specs/VCFv4.2.pdf">https://samtools.github.io/hts-specs/VCFv4.2.pdf</a></p><p>Meta-information & Header line 示例可见以下文件开头以 # 起始的行:</p><p><a href="https://github.com/vcflib/vcflib/blob/master/samples/sample.vcf">https://github.com/vcflib/vcflib/blob/master/samples/sample.vcf</a></p><h3 id="CrossMap"><a href="#CrossMap" class="headerlink" title="CrossMap"></a>CrossMap</h3><p>github 链接:<a href="https://github.com/liguowang/CrossMap">https://github.com/liguowang/CrossMap</a></p><p>官方文档链接:<a href="https://crossmap.readthedocs.io/en/latest/">https://crossmap.readthedocs.io/en/latest/</a></p><p>使用 pip 安装 CrossMap:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pip3 install git+https://github.com/liguowang/CrossMap.git # 从 github 上安装,相对而言可能更新一些</span><br><span class="line">或者</span><br><span class="line">pip3 install CrossMap # 从 PyPI 安装,按照版本发布情况安装</span><br></pre></td></tr></tbody></table></figure><p>需准备的输入文件:</p><ul><li><strong>指定了转换方向的 chain 文件</strong>,可以从 UCSC 网站下载(所有物种的下载信息详细可见 <a href="https://hgdownload.soe.ucsc.edu/downloads.html">UCSC download</a> 中的 LiftOver files)。以人类基因组为例,hg19 到 hg38 的 chain file 下载链接: <a href="https://hgdownload.soe.ucsc.edu/goldenPath/hg19/liftOver/hg19ToHg38.over.chain.gz">hg19ToHg38.over.chain.gz</a>。</li><li><strong>目标参考基因组文件</strong>,例如转坐标是 hg19 到 hg38 的话,则准备 hg38 的参考基因组文件。</li></ul><p>运行命令示例(链文件和参考基因组文件按需求更改为自己的路径):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CrossMap vcf [chain file] [input vcf] [target genome] [output vcf]</span><br></pre></td></tr></tbody></table></figure><p>各框注部分需填写的文件路径:</p><ul><li><code>[chain file]</code>:填入使用的链文件路径,例如 hg19ToHg38.over.chain.gz。</li><li><code>[target genome]</code>:填入转换的目标基因组文件,例如 hg38.fa。</li><li><code>[input vcf]</code>:填入需要进行转换的 VCF 文件路径</li><li><code>[output vcf]</code>:填入输出的转换后的 VCF 文件路径。</li></ul><p>可选参数如下:</p><ul><li><code>--chromid</code>:指定输出的染色体 id 格式,详细可见 <code>CrossMap vcf -h</code> 说明。</li><li><code>--no-comp-alleles</code>:指定该参数后,CrossMap 会取消对 ref & alt 的检查。默认不指定该参数时,如果转换后 ref 和 alt 一致则会被认为转换失败。</li><li><code>--compress</code>:指定该参数后,CrossMap 会调用 <code>gzip</code> 对输出的 VCF 文件进行压缩。</li></ul><p>未转换成功的变异信息可见 <code>[output vcf].unmap</code>。</p><h3 id="GATK-LiftoverVcf"><a href="#GATK-LiftoverVcf" class="headerlink" title="GATK LiftoverVcf"></a>GATK LiftoverVcf</h3><p>官方文档链接:<a href="https://gatk.broadinstitute.org/hc/en-us/articles/360036831351-LiftoverVcf-Picard">https://gatk.broadinstitute.org/hc/en-us/articles/360036831351-LiftoverVcf-Picard</a></p><p>使用 conda 安装 GATK:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install -c bioconda gatk4</span><br></pre></td></tr></tbody></table></figure><p>需准备的输入文件与 CrossMap 相同(可见上文),以下是一个运行示例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">gatk LiftoverVcf \</span><br><span class="line"> -I [input vcf] \</span><br><span class="line"> -O [output vcf] \</span><br><span class="line"> -CHAIN [chain file] \</span><br><span class="line"> -R [target genome] \</span><br><span class="line"> --REJECT [unmap vcf]</span><br></pre></td></tr></tbody></table></figure><p>各框注部分需填写的文件路径:</p><ul><li><code>[input vcf]</code>:填入需要进行转换的 VCF 文件路径</li><li><code>[output vcf]</code>:填入输出的转换后的 VCF 文件路径。</li><li><code>[unmap vcf]</code>:指定转换失败的变异输出文件。</li><li><code>[chain file]</code>:填入使用的<strong>链文件</strong>路径,例如 hg19ToHg38.over.chain.gz。</li><li><code>[target genome]</code>:填入转换的<strong>目标基因组文件</strong>,例如 hg38.fa。</li></ul><p>需注意 GATK 会识别文件尾缀以判断输出的文件类型,因此相关的 VCF 文件名应当以 <code>.vcf</code> 结尾。</p><p>一些可能比较常用的参数(详细信息请使用 <code>gatk LiftoverVcf -h</code> 查看):</p><ul><li><code>--ALLOW_MISSING_FIELDS_IN_HEADER</code>:允许 header 中缺失 INFO & FORMAT 字段的说明,但仍需其他的部分(例如 header line 等)。</li><li><code>--WRITE_ORIGINAL_ALLELES</code> & <code>--WRITE_ORIGINAL_POSITION</code>:输出原始的等位基因信息 & 输出原始的坐标信息。</li></ul><h3 id="BCFtools-liftover"><a href="#BCFtools-liftover" class="headerlink" title="BCFtools liftover"></a>BCFtools liftover</h3><p>该工具是 BCFtools 的插件,<strong>因此需要先安装 BCFtools 后再使用</strong>。</p><p>github 链接:<a href="https://github.com/freeseek/score?tab=readme-ov-file#liftover-vcfs">https://github.com/freeseek/score?tab=readme-ov-file#liftover-vcfs</a></p><p>安装方法:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">首先需要安装 bcftools,可以自行编译或者使用 conda 安装</span></span><br><span class="line">conda install -c bioconda bcftools</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">自行选择一个合适的路径,开始下载插件文件 (Linux)</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">插件也可以自行编译,有需求请在上方 github 链接中自行查阅</span></span><br><span class="line">wget https://software.broadinstitute.org/software/score/score_1.20-20240927.zip</span><br><span class="line">mkdir -p bcftools_plugin</span><br><span class="line">unzip -d bcftools_plugin score_1.20-20240927.zip</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将插件的路径设置为 BCFtools 插件的环境路径</span></span><br><span class="line">export BCFTOOLS_PLUGINS=$(pwd .)/bcftools_plugin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">或者保存到 .bashrc 文件中保证以后能直接调用</span></span><br><span class="line">echo "export BCFTOOLS_PLUGINS=$(pwd .)/bcftools_plugin" >> ~/.bashrc</span><br><span class="line">source ~/.bashrc</span><br></pre></td></tr></tbody></table></figure><p>查看是否安装成功:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bcftools +liftover -h</span><br></pre></td></tr></tbody></table></figure><p>需准备的输入文件与 CrossMap 相同,在此基础上还需要准备<strong>源基因组版本的基因组序列文件</strong>,以下是一个运行示例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">bcftools +liftover -Ou [input vcf] -- \</span><br><span class="line"> -s [source genome] \</span><br><span class="line"> -f [target genome] \</span><br><span class="line"> -c [chain file] \</span><br><span class="line"> --reject [unmap vcf] --write-reject | \</span><br><span class="line"> bcftools sort -Ou -o [output vcf]</span><br></pre></td></tr></tbody></table></figure><p>各框注部分需填写的文件路径:</p><ul><li><code>[input vcf]</code>:填入需要进行转换的 VCF 文件路径</li><li><code>[output vcf]</code>:填入输出的转换后的 VCF 文件路径。</li><li><code>[unmap vcf]</code>:指定转换失败的变异输出文件。</li><li><code>[chain file]</code>:填入使用的<strong>链文件</strong>路径,例如 hg19ToHg38.over.chain.gz。</li><li><code>[source genome]</code>:填入转换的<strong>源基因组文件</strong>,例如 hg19.fa。</li><li><code>[target genome]</code>:填入转换的<strong>目标基因组文件</strong>,例如 hg38.fa。</li></ul><p>更多可选项的说明详细可见 <code>bcftools +liftover -h</code>。以上命令行在输出后对 VCF 进行了排序并以未压缩格式输出(<code>-Ou</code>),可根据自身需求调整以上命令的工作流。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>关于三个工具,就我个人的使用经验而言(主要是一些 SNV),三者的结果相差并不大,所以目前使用较多的是更方便的 CrossMap(因为它不对 header 做要求)。如果在你的应用场景中涉及到较多的 InDel,可以考虑使用专门为 InDel 进行过优化的 BCFtools liftvcf 进行。以下是 BCFtools liftvcf 文章中的一个表格,展示了各个工具的功能和限制,可按照自己的需求进行挑选:</p><table><thead><tr><th align="left">Tool</th><th align="left">BCFtools liftvcf</th><th align="left">GATK LiftoverVcf</th><th align="left">CrossMap</th></tr></thead><tbody><tr><td align="left">Version tested</td><td align="left">2023-12-06</td><td align="left">3.1.1</td><td align="left">0.6.6</td></tr><tr><td align="left">GitHub username</td><td align="left">freeseek</td><td align="left">broadinstitute</td><td align="left">liguowang</td></tr><tr><td align="left">GitHub repository</td><td align="left">score</td><td align="left">picard</td><td align="left">CrossMap</td></tr><tr><td align="left">License</td><td align="left">MIT</td><td align="left">MIT</td><td align="left">GPLv3</td></tr><tr><td align="left"><strong>Main features</strong></td><td align="left"></td><td align="left"></td><td align="left"></td></tr><tr><td align="left">Can reverse-complements alleles</td><td align="left">Yes</td><td align="left">Yes</td><td align="left">Yes</td></tr><tr><td align="left">Handles multi-allelic records</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left">Can swap SNV alleles</td><td align="left">Yes</td><td align="left">Bi-allelic only</td><td align="left">No</td></tr><tr><td align="left">Can swap indel alleles</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left">Can add new reference allele</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left">Can recover SNVs at chain gaps</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left"><strong>File input/output options</strong></td><td align="left"></td><td align="left"></td><td align="left"></td></tr><tr><td align="left">Sort records after liftover</td><td align="left">No</td><td align="left">Yes</td><td align="left">No</td></tr><tr><td align="left">Left-aligns indels after liftover</td><td align="left">Yes</td><td align="left">Yes</td><td align="left">No</td></tr><tr><td align="left">Can record the original position</td><td align="left">Yes</td><td align="left">Yes</td><td align="left">No</td></tr><tr><td align="left">Flexible with contig names</td><td align="left">Yes</td><td align="left">No</td><td align="left">Yes</td></tr><tr><td align="left">Loads full reference in memory</td><td align="left">No</td><td align="left">Yes</td><td align="left">No</td></tr><tr><td align="left">Can input VCF as a file stream</td><td align="left">Yes</td><td align="left">No</td><td align="left">Yes</td></tr><tr><td align="left">Can output VCF as a file stream</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left">Can input/output binary VCFs</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left"><strong>Variants annotations updates</strong></td><td align="left"></td><td align="left"></td><td align="left"></td></tr><tr><td align="left">Updates INFO/END field</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left">Updates Number=G/R fields</td><td align="left">Yes</td><td align="left">PL and AD</td><td align="left">No</td></tr><tr><td align="left">Updates AC-like fields</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr><tr><td align="left">Updates AF-like fields</td><td align="left">Yes</td><td align="left">Yes</td><td align="left">No</td></tr><tr><td align="left">Updates GWAS-VCF fields</td><td align="left">Yes</td><td align="left">No</td><td align="left">No</td></tr></tbody></table><center>Source: https://academic.oup.com/view-large/438467641</center><p>但也正如该文章中结尾的部分所说,<strong>不管哪个工具能做得更好,liftover 终究是一个有损过程</strong>,无论什么情况下,重新比对进行 call variant 总会产生比 liftover 更好的结果。当然有些情况下我们也不可避免地只能选择 liftover 这一途径(比如说原始测序数据无法访问或需申请权限等)。</p><h2 id="相关文章链接"><a href="#相关文章链接" class="headerlink" title="相关文章链接"></a>相关文章链接</h2><ul><li>CrossMap:<a href="https://pubmed.ncbi.nlm.nih.gov/24351709/">CrossMap: a versatile tool for coordinate conversion between genome assemblies.</a></li><li>BCFtools liftover:<a href="https://pubmed.ncbi.nlm.nih.gov/38261650/">BCFtools/liftover: an accurate and comprehensive tool to convert genetic variants across genome assemblies</a></li></ul></body></html>]]></content>
<summary type="html">本文介绍了如何使用 CrossMap / GATK LiftoverVcf / BCFtools liftover 对 vcf 中的突变进行不同基因组版本之间的坐标转换。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>bedtools merge 不合并相邻区间(仅合并重叠区间)</title>
<link href="https://biojuse.com/2024/12/28/bedtools%20merge%20%E4%B8%8D%E5%90%88%E5%B9%B6%E7%9B%B8%E9%82%BB%E5%8C%BA%E9%97%B4%EF%BC%88%E4%BB%85%E5%90%88%E5%B9%B6%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4%EF%BC%89/"/>
<id>https://biojuse.com/2024/12/28/bedtools%20merge%20%E4%B8%8D%E5%90%88%E5%B9%B6%E7%9B%B8%E9%82%BB%E5%8C%BA%E9%97%B4%EF%BC%88%E4%BB%85%E5%90%88%E5%B9%B6%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4%EF%BC%89/</id>
<published>2024-12-28T09:30:00.000Z</published>
<updated>2024-12-28T09:42:32.613Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>关于 bedtools merge 的详细参数设定可见:</p><p>bedtools merge documentation:</p><p><a href="https://bedtools.readthedocs.io/en/latest/content/tools/merge.html">https://bedtools.readthedocs.io/en/latest/content/tools/merge.html</a></p><img src="https://bedtools.readthedocs.io/en/latest/_images/merge-glyph.png" height="300px"><p>默认情况下,bedtools merge 会对所有重叠区间及相邻区间进行合并,例如:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">运行前</span></span><br><span class="line">chr11000010001</span><br><span class="line">chr11000110002</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">运行后</span></span><br><span class="line">chr11000010002</span><br></pre></td></tr></tbody></table></figure><p>但在最近遇到的分析场景中,我仅想合并重叠的点并对它们的值求平均,并不想将它们与相邻的点合并起来,例如:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">chr110000100010.1</span><br><span class="line">chr110000100010.2</span><br><span class="line">chr110001100020.3</span><br></pre></td></tr></tbody></table></figure><p>这里我仅想在 <code>chr1:10000-10001</code> 上进行合并且求平均,但此时使用 <code>bedtools merge -c 4 -o mean</code> 会输出以下结果:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chr1 10000 10002 0.2</span><br></pre></td></tr></tbody></table></figure><p>这并不符合我的预期工作情况,在查阅参数说明后,发现了以下关键参数:</p><ul><li><code>-d</code>:Maximum distance between features allowed for features to be merged. Default is 0. That is, overlapping and/or features are merged.</li></ul><p>这里的 <code>-d</code> 默认值为 0,说明中提到<strong>该值会将重叠的以及首尾相接的区间合并到一起</strong>,于是乎我尝试了将该参数改为 <strong>-1</strong>:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">bedtools merge -i test.bed -d -1 -c 4 -o mean</span></span><br><span class="line">chr1 10000 10001 0.15</span><br><span class="line">chr1 10001 10002 0.3</span><br></pre></td></tr></tbody></table></figure><p>在设置为 -1 后,bedtools merge 仅对重叠区间进行合并,以下是一个测试:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">运行前</span></span><br><span class="line">chr110000100010.1</span><br><span class="line">chr110000100010.2</span><br><span class="line">chr110001100090.3</span><br><span class="line">chr110008100200.4</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">运行后</span></span><br><span class="line">chr1 10000 10001 0.15</span><br><span class="line">chr1 10001 10020 0.35</span><br></pre></td></tr></tbody></table></figure></body></html>]]></content>
<summary type="html">本文介绍了如何通过设置恰当参数使 bedtools merge 仅对重叠区间进行合并(而不合并相邻区间)。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>基于 US-align 计算两个蛋白结构之间的 RMSD 和 TM-score</title>
<link href="https://biojuse.com/2024/12/18/%E5%9F%BA%E4%BA%8E%20US-align%20%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E8%9B%8B%E7%99%BD%E7%BB%93%E6%9E%84%E4%B9%8B%E9%97%B4%E7%9A%84%20RMSD%20%E5%92%8C%20TM-score/"/>
<id>https://biojuse.com/2024/12/18/%E5%9F%BA%E4%BA%8E%20US-align%20%E8%AE%A1%E7%AE%97%E4%B8%A4%E4%B8%AA%E8%9B%8B%E7%99%BD%E7%BB%93%E6%9E%84%E4%B9%8B%E9%97%B4%E7%9A%84%20RMSD%20%E5%92%8C%20TM-score/</id>
<published>2024-12-18T08:40:00.000Z</published>
<updated>2024-12-18T08:54:14.022Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>在蛋白质结构研究里,比较不同蛋白质结构之间的相似性是一个核心任务,尤其是在结构预测、同源建模和结构功能关系研究中。以下是一些常用到的衡量蛋白结构间相似性的指标:</p><ul><li><strong>RMSD(均方根偏差)</strong>:该值是衡量两种蛋白质三维结构之间原子位置差异的标准度量。较小的 RMSD 值意味着两个结构相似度较高,RMSD 值对大分子在全局对齐时的大小和形状变化非常敏感。</li><li><strong>TM-score</strong>:相较于 RMSD,TM-score 更加关注<strong>结构的全局相似性</strong>,且对于蛋白质的尺寸和形状变化具有更高的鲁棒性。TM-score 的值介于 0 到 1 之间,值越接近 1,表示两个结构越相似。</li></ul><p>本文将介绍如何使用 US-align 对两个蛋白结构进行 align(对齐)并计算其 RMSD 及 TM-score。</p><h2 id="US-align"><a href="#US-align" class="headerlink" title="US-align"></a>US-align</h2><p>US-align github page:</p><p><a href="https://github.com/pylelab/USalign">https://github.com/pylelab/USalign</a></p><p>该软件的研究团队也同样是 <a href="https://zhanggroup.org/TM-align/">TM-align</a> 的开发者。相比其他的计算工具,US-align 有以下优点:</p><ul><li>支持比对<strong>除蛋白质以外的生物大分子及其复合物</strong>(例如 DNA / RNA 等)。</li><li>通过改进的计算方法从而实现高效及准确的比对过程。</li></ul><p>下文介绍的为其本地部署的使用方式,如果你没有在本地大量运行的需求,也可以使用网页版:</p><p><a href="https://zhanggroup.org/US-align/">https://zhanggroup.org/US-align/</a></p><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>推荐通过 conda 安装 US-align:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install -c bioconda usalign</span><br></pre></td></tr></tbody></table></figure><p>在安装后,可通过 <code>USalign -h</code> 查看是否能正常工作:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">USalign -h</span></span><br><span class="line"></span><br><span class="line"> ********************************************************************</span><br><span class="line"> * US-align (Version 20240730) *</span><br><span class="line"> * Universal Structure Alignment of Proteins and Nucleic Acids *</span><br><span class="line"> * Reference: C Zhang, M Shine, AM Pyle, Y Zhang. (2022) Nat Methods*</span><br><span class="line"> * C Zhang, AM Pyle (2022) iScience. *</span><br><span class="line"> * Please email comments and suggestions to zhang@zhanggroup.org *</span><br><span class="line"> ********************************************************************</span><br><span class="line"></span><br><span class="line">Usage: USalign PDB1.pdb PDB2.pdb [Options]</span><br><span class="line"></span><br><span class="line">......</span><br></pre></td></tr></tbody></table></figure><h3 id="参数"><a href="#参数" class="headerlink" title="参数"></a>参数</h3><p>这里仅对其最重要的几个参数进行介绍,介绍完后将提供部分运行示例。</p><ul><li><p><code>-mm</code>:该参数为比对选项,其<strong>默认值为 0,对应两个单体结构(monomer)的比对</strong>。</p><ul><li>将 <code>-mm</code> <strong>设置为 1 时,对应两个寡聚体结构(oligomer)的比对</strong>。如果结构文件中涉及到多条链,使用该参数以正常工作(如果未设置,则默认取第一条链进行比对)。</li><li>其余 <code>-mm</code> 的设置对应的应用场景较特殊,例如 <code>-mm 2</code> 将单条链与寡聚体结构进行比对(适用于确定单个蛋白链在寡聚化的构象变化)、<code>-mm 6</code> 为半非连续比对(允许有限的重排)。</li></ul></li><li><p><code>-ter</code>:该参数指定比对的链数量,其默认值为 2,对应仅比对第一条链。</p><ul><li>当 <code>-mm</code> 设置为 1 或 2 时,<code>-ter</code> 仅可选择 0 或 1。其中 <strong>0 适用于比对不对称单元,1 适用于比对生物学单元</strong>(例如血红蛋白四聚体)。这一部分的概念介绍可见<a href="https://www.novopro.cn/articles/201904261180.html">此处</a>。</li><li><code>-ter</code> 设置为 3 时可选择性地仅比对第一条链的其中一部分(将该部分标记为 'TER' 以进行识别)。</li></ul></li></ul><p>此外,US-align 也提供了批量比对的功能以及其他调整比对行为的参数,详细请通过 <code>USalign -h</code> 查看。本文不再对此进行更多延申。</p><h3 id="运行示例"><a href="#运行示例" class="headerlink" title="运行示例"></a>运行示例</h3><p>本文以两个 PDB 上的结构 <a href="https://www.rcsb.org/structure/2PGH">2PGH</a> 和 <a href="https://www.rcsb.org/structure/3GOU">3GOU</a> 作为示例进行比对:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">数据下载</span></span><br><span class="line">wget https://files.rcsb.org/download/2PGH.cif</span><br><span class="line">wget https://files.rcsb.org/download/3GOU.cif</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">比对</span></span><br><span class="line">USalign -mm 1 -ter 1 ./2PGH.cif ./3GOU.cif -o HEMOGLOBIN</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"> ********************************************************************</span><br><span class="line"> * US-align (Version 20240730) *</span><br><span class="line"> * Universal Structure Alignment of Proteins and Nucleic Acids *</span><br><span class="line"> * Reference: C Zhang, M Shine, AM Pyle, Y Zhang. (2022) Nat Methods*</span><br><span class="line"> * C Zhang, AM Pyle (2022) iScience. *</span><br><span class="line"> * Please email comments and suggestions to zhang@zhanggroup.org *</span><br><span class="line"> ********************************************************************</span><br><span class="line"></span><br><span class="line">Name of Structure_1: ./2PGH.cif:A:B:C:D (to be superimposed onto Structure_2)</span><br><span class="line">Name of Structure_2: ./3GOU.cif:C:D:A:B</span><br><span class="line">Length of Structure_1: 574 residues</span><br><span class="line">Length of Structure_2: 574 residues</span><br><span class="line"></span><br><span class="line">Aligned length= 574, RMSD= 1.03, Seq_ID=n_identical/n_aligned= 0.815</span><br><span class="line">TM-score= 0.98690 (normalized by length of Structure_1: L=574, d0=8.41)</span><br><span class="line">TM-score= 0.98690 (normalized by length of Structure_2: L=574, d0=8.41)</span><br><span class="line">(You should use TM-score normalized by length of the reference structure)</span><br><span class="line"></span><br><span class="line">(":" denotes residue pairs of d < 5.0 Angstrom, "." denotes other aligned residues)</span><br><span class="line">VLSAADKANVKAAWGKVGGQAGAHGAEALERMFLGFPTTKTYFPHFNLSHGSDQVKAHGQKVADALTKAVGHLDDLPGALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHHPDDFNPSVHASLDKFLANVSTVLTSKYR*VHLSAEEKEAVLGLWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSNADAVMGNPKVKAHGKKVLQSFSDGLKHLDNLKGTFAKLSELHCDQLHVDPENFRLLGNVIVVVLARRLGHDFNPDVQAAFQKVVAGVANALAHKYH*VLSAADKANVKAAWGKVGGQAGAHGAEALERMFLGFPTTKTYFPHFNLSHGSDQVKAHGQKVADALTKAVGHLDDLPGALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHHPDDFNPSVHASLDKFLANVSTVLTSKYR*VHLSAEEKEAVLGLWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSNADAVMGNPKVKAHGKKVLQSFSDGLKHLDNLKGTFAKLSELHCDQLHVDPENFRLLGNVIVVVLARRLGHDFNPDVQAAFQKVVAGVANALAHKYH*</span><br><span class="line">::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::.*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::.*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::.*</span><br><span class="line">VLSPADKTNIKSTWDKIGGHAGDYGGEALDRTFQSFPTTKTYFPHFDLSPGSAQVKAHGKKVADALTTAVAHLDDLPGALSALSDLHAYKLRVDPVNFKLLSHCLLVTLACHHPTEFTPAVHASLDKFFAAVSTVLTSKYR*VHLTAEEKSLVSGLWGKVNVDEVGGEALGRLLIVYPWTQRFFDSFGDLSTPDAVMSNAKVKAHGKKVLNSFSDGLKNLDNLKGTFAKLSELHCDKLHVDPENFKLLGNVLVCVLAHHFGKEFTPQVQAAYQKVVAGVANALAHKYH*VLSPADKTNIKSTWDKIGGHAGDYGGEALDRTFQSFPTTKTYFPHFDLSPGSAQVKAHGKKVADALTTAVAHLDDLPGALSALSDLHAYKLRVDPVNFKLLSHCLLVTLACHHPTEFTPAVHASLDKFFAAVSTVLTSKYR*VHLTAEEKSLVSGLWGKVNVDEVGGEALGRLLIVYPWTQRFFDSFGDLSTPDAVMSNAKVKAHGKKVLNSFSDGLKNLDNLKGTFAKLSELHCDKLHVDPENFKLLGNVLVCVLAHHFGKEFTPQVQAAYQKVVAGVANALAHKYH*</span><br><span class="line"></span><br><span class="line">#Total CPU time is 1.05 seconds</span><br></pre></td></tr></tbody></table></figure><p>在其<strong>屏幕输出</strong>中,可以看到比对的详细情况(包括计算得到的 RMSD 以及 TM-score)。该过程里你可以将屏幕输出保存到一个文件中,以便后续查看,此外你也可以通过 <code>-outfmt 2</code> 参数,将其转化为更简洁的形式:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">USalign -mm 1 -ter 1 ./2PGH.cif ./3GOU.ci2 -outfmt 2 -o HEMOGLOBIN</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">PDBchain1 PDBchain2 TM1 TM2 RMSD ID1 ID2 IDali L1 L2 Lali</span></span><br><span class="line">./2PGH.cif:A:B:C:D ./3GOU.cif:C:D:A:B 0.9869 0.9869 1.03 0.815 0.815 0.815 574 574 574</span><br></pre></td></tr></tbody></table></figure><p>在运行上述示例后,你可以在运行目录里看到以下新增文件:</p><ul><li><code>HEMOGLOBIN.cif</code>:Structure1 的晶体文件,此处各原子的坐标根据比对结果进行了修改。</li><li><code>HEMOGLOBIN_*.pml</code>:用于输入给 PyMol 进行比对结果可视化的文件。不同的 pml 文件对应不同的可视化方式。</li></ul><p>你可以运行以下命令安装 PyMol 并可视化比对结果:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装</span></span><br><span class="line">conda install schrodinger::pymol</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">可视化</span></span><br><span class="line">pymol -c -d @HEMOGLOBIN_all_atm.pml -x 3000 -y 3000 -g HEMOGLOBIN_all_atm.png</span><br></pre></td></tr></tbody></table></figure><img src="/pic2/HEMOGLOBIN_all_atm.png" height="400px"><center>红色和蓝色分别对应两个比对的结构</center><p>如果你想要输出的图片更清晰,也可以手动对 pml 文件进行调节,按照 PyMol 的命令方式对其添加抗锯齿并增大分辨率,修改相关设置后的图片示例可见<a href="/pic2/HEMOGLOBIN_all_atm_high_quality.png">此处</a>。你也可以将两个结构加载到交互式 PyMol 应用程序中进行调节。</p><h2 id="参考材料"><a href="#参考材料" class="headerlink" title="参考材料"></a>参考材料</h2><ol><li><p>Zhang et al. 2022, Nat Methods: US-align: universal structure alignments of proteins, nucleic acids, and macromolecular complexes</p></li><li><p>不对称单元与晶胞,生物单元概念解析:<a href="https://www.novopro.cn/articles/201904261180.html">https://www.novopro.cn/articles/201904261180.html</a></p></li></ol></body></html>]]></content>
<summary type="html">本文介绍了如何使用 US-align 计算两个蛋白结构之间的相似性。除此之外,该软件同样可以进行蛋白质寡聚体(多个蛋白或蛋白与 DNA 复合物)结构之间的比较。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>使用 ESM 计算蛋白序列的突变影响分数</title>
<link href="https://biojuse.com/2024/11/16/%E4%BD%BF%E7%94%A8%20ESM%20%E8%AE%A1%E7%AE%97%E8%9B%8B%E7%99%BD%E5%BA%8F%E5%88%97%E7%9A%84%E7%AA%81%E5%8F%98%E5%BD%B1%E5%93%8D%E5%88%86%E6%95%B0/"/>
<id>https://biojuse.com/2024/11/16/%E4%BD%BF%E7%94%A8%20ESM%20%E8%AE%A1%E7%AE%97%E8%9B%8B%E7%99%BD%E5%BA%8F%E5%88%97%E7%9A%84%E7%AA%81%E5%8F%98%E5%BD%B1%E5%93%8D%E5%88%86%E6%95%B0/</id>
<published>2024-11-16T08:00:00.000Z</published>
<updated>2024-12-06T08:28:30.538Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p><strong>ESM(Evolutionary Scale Modeling)</strong> 是由 Facebook AI Research 开发的蛋白质语言模型。其基于大规模的蛋白质序列数据进行训练,能够捕捉蛋白质序列中的复杂模式和进化信息,从而在多个生物学下游应用中表现出色。</p><p>预训练好的 ESM 可用于多个生物学下游应用,本文将就其突变影响分析方面的功能进行介绍。</p><h2 id="关于-ESM"><a href="#关于-ESM" class="headerlink" title="关于 ESM"></a>关于 ESM</h2><p>ESM 预测突变影响的方式是无监督的,也就是说 <strong>ESM 的训练过程中并未使用到与突变影响相关的标签进行有监督学习</strong>。其评估每个氨基酸错义突变影响程度的方式是掩蔽该位置上的氨基酸并<strong>计算两种氨基酸(参考氨基酸与突变氨基酸)在该位置出现的对数似然比</strong>:</p><img src="/pic2/esm_score.png" height="100px"><p>ESM 的预训练阶段使用了类似于 BERT 的掩码任务,因此 ESM 可以给出每个位点里各类氨基酸出现的概率,而参考氨基酸(Ref,即未突变时的氨基酸)和突变氨基酸(Alt)的概率差异即可作为一种 effect score。可以简单理解为,<strong>突变氨基酸出现的概率比参考氨基酸低越多,说明这类突变越有害</strong>。这种无监督的评估方式也被用于许多其他的深度学习工具,例如 EVE 和 GPN 等。</p><img src="/pic2/esm1.png" height="200px"><center>Reference: Brandes et al. 2023, Nat Genet</center><p>对于多氨基酸突变,ESM 采取的策略是简单的加性模型,即对每个位置计算出其对数似然比后,进行加和得到最终评分(Meier et al. 2021, bioRxiv)。其他研究者的 benchmark 测试表明,ESM 在诸多数据集上具有出色性能(Tabet et al. 2024, Genome Biol)。</p><h2 id="ESM-使用"><a href="#ESM-使用" class="headerlink" title="ESM 使用"></a>ESM 使用</h2><p>在使用下述任何一种方法前需提前安装好需要的 module:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install tqdm numpy pandas biopython torch fair-esm</span><br></pre></td></tr></tbody></table></figure><p>其他需要的 module 可在后续使用 script 报错时自行通过 pip 或 conda 补充安装。</p><h3 id="原始的-ESM-工作流程"><a href="#原始的-ESM-工作流程" class="headerlink" title="原始的 ESM 工作流程"></a>原始的 ESM 工作流程</h3><p>使用 <code>git clone</code> 下载其 github repository:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/facebookresearch/esm.git</span><br></pre></td></tr></tbody></table></figure><p>如果你的设备无法连接外网,你也可以尝试克隆 gitee 源:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://gitee.com/li42110/esm.git</span><br></pre></td></tr></tbody></table></figure><p>在 <code>git clone</code> 下载完成后,运行以下命令跳转到为 variant prediction 提供的示例文件夹中:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd esm/examples/variant-prediction</span><br></pre></td></tr></tbody></table></figure><p>该文件夹下的 <code>predict.py</code> 即 ESM 团队提供的用于计算 effect score 的 script,其中示例命令如下:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">python predict.py \</span><br><span class="line"> --model-location esm1v_t33_650M_UR90S_1 esm1v_t33_650M_UR90S_2 esm1v_t33_650M_UR90S_3 esm1v_t33_650M_UR90S_4 esm1v_t33_650M_UR90S_5 \</span><br><span class="line"> --sequence HPETLVKVKDAEDQLGARVGYIELDLNSGKILESFRPEERFPMMSTFKVLLCGAVLSRVDAGQEQLGRRIHYSQNDLVEYSPVTEKHLTDGMTVRELCSAAITMSDNTAANLLLTTIGGPKELTAFLHNMGDHVTRLDRWEPELNEAIPNDERDTTMPAAMATTLRKLLTGELLTLASRQQLIDWMEADKVAGPLLRSALPAGWFIADKSGAGERGSRGIIAALGPDGKPSRIVVIYTTGSQATMDERNRQIAEIGASLIKHW \</span><br><span class="line"> --dms-input ./data/BLAT_ECOLX_Ranganathan2015.csv \</span><br><span class="line"> --mutation-col mutant \</span><br><span class="line"> --dms-output ./data/BLAT_ECOLX_Ranganathan2015_labeled.csv \</span><br><span class="line"> --offset-idx 24 \</span><br><span class="line"> --scoring-strategy wt-marginals</span><br></pre></td></tr></tbody></table></figure><ul><li><code>--model-location</code>:选择模型,此处选择的是 5 个 ESM-1v 模型(这些模型使用不同的随机种子生成训练数据集并进行训练),也可选择其他模型例如 ESM-1b (esm1b_t33_650M_UR50S) 或 ESM2 (esm2_t33_650M_UR50D) 等。</li><li><code>--sequence</code>:输入的蛋白序列,注意过长的序列可能会引发内存溢出或者其他意外状况(由于 ESM 的 token 最大数量限制),如果你输入的序列过长,请直接跳转后文 <strong><u>优化的 ESM 工作框架</u></strong> 部分。</li><li><code>--dms-input</code>:输入的表格,其中需包含 mutant 列(<code>--mutation-col</code> 中指定的列名),mutant 格式为 <code>[ref amino][idx][alt amino]</code>,例如示例文件中的 <code>H24C</code> 表示的为第 24 位上的 H 突变为 C。<strong>请在该表格的该列中填入所有你希望计算 effect score 的 mutant</strong>。</li><li><code>--mutation-col</code>:指定突变在输入表格中所在的列名,此处即 <code>mutant</code>。</li></ul><img src="/pic2/esm_mutant.png" height="200px"><center>红框标注即 mutant 列,后续的列并不是必需的</center><ul><li><code>--dms-output</code>:输出的表格,其中最后 n 列(n 的具体数字取决于你输入的模型数量)为各模型的评分。</li><li><code>--offset-idx</code>:突变位置的偏倚,例如在 ESM 示例中第一个 mutant 为 H24C,但 H 其实位于序列的开端,因此将此参数设置为 24 使其从 24 开始位置计数。如果你从 1 开始,则将该项设置为 1 即可。</li><li><code>--scoring-strategy</code>:计算 effect score 的策略,分为三种。<code>wt-marginals</code> 直接<strong>将 wt 序列(未突变序列)输入模型</strong>得到每个 token 的 embedding 并计算其出现各个氨基酸的概率;<code>masked-marginals</code> <strong>将 wt 序列中每个 token 逐个掩码</strong>并计算掩码位置上出现各个氨基酸的概率;<code>pseudo-ppl</code> <strong>将 mutant 序列中每个 token 逐个掩码并计算整条序列的伪困惑度</strong>作为其 effect score。</li></ul><p>关于 <code>--scoring-strategy</code> 参数,<code>masked-marginals</code> 的计算方式是最符合直觉和逻辑的,但由于 <code>masked-marginals</code> 计算中要对每个 token 都掩码计算一次,因此时间开销很高(特别是在序列很长的情况下)。相比之下,<code>wt-marginals</code> 虽然向模型暴露了原始序列信息,但仅需计算一次便可得到所有位置上各个氨基酸的出现概率。已有研究分析表明 <code>wt-marginals</code> 也具有很好的性能表现(虽然其会高估原始氨基酸的概率),建议此处选择 <code>wt-margins</code> 即可。</p><p>运行以上命令后,<strong>如果你缺乏这些模型的文件,脚本会自行开始下载并存储到特定位置</strong>(例如 Linux 为 home 目录下的 <code>.cache/torch/hub/checkpoints/</code>)。该步骤同样可能遇到网络问题,此时你可以选择自行从其他途径下载到指定路径中(例如通过 hugging face 的国内镜像)。如果要预测其他序列,请修改 <code>--sequence</code> 参数并自行准备 <code>--dms-input</code> 的输入文件。</p><img src="/pic2/esm_model.png" height="200px"><center>下载的 ESM model</center><h3 id="优化的-ESM-工作框架"><a href="#优化的-ESM-工作框架" class="headerlink" title="优化的 ESM 工作框架"></a>优化的 ESM 工作框架</h3><p>使用 ESM github repository 中的 script 虽然可以达成目的,但是它有以下一些缺点:</p><ul><li>不适用过长的蛋白序列。</li><li>定制化程度较高,迁移到自己的需求上时有一定的难度(如果数据量大),门槛相对更高。</li></ul><p>基于此,这里推荐一个由 <a href="https://www.nature.com/articles/s41588-023-01465-0">Brandes 等人</a>开发的流程,该流程经过了以下改进:</p><ul><li><strong>只需输入 fasta 文件</strong>即可批量计算这些序列中所有氨基酸位点上各个突变的 effect score。</li><li>对于过长的序列,该流程<strong>使用滑动窗口方法将长序列分解成具有 overlap 的多条序列</strong>,并对重叠位置上的 effect score 进行加权平均计算。</li><li>对于序列中的<strong>插入或缺失同样可以进行计算</strong>(衡量标准是整条序列的似然比差异)。</li></ul><p>使用 <code>git clone</code> 配置:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/ntranoslab/esm-variants.git</span><br><span class="line">cd esm-variants</span><br></pre></td></tr></tbody></table></figure><p>该文件夹下的 <code>esm_score_missense_mutations.py</code> 使用示例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python esm_score_missense_mutations.py --input-fasta-file [fasta] --output-csv-file [output csv] --model-name esm1b_t33_650M_UR50S</span><br></pre></td></tr></tbody></table></figure><ul><li><code>[fasta]</code> 处填入你所需要分析的 fasta 序列文件路径,<code>[output csv]</code> 处填入输出的 csv 表格文件路径(含文件名)。</li><li>可通过 <code>--model-name</code> 指定使用的 ESM 模型,默认为 ESM-1b。</li></ul><p>以下是一个运行示例,展示如何使用,首先建立一个 <code>test.fa</code>,填入以下内容:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">>seq1</span><br><span class="line">FISHWISHFQRCHIPSTHATARECRISP</span><br><span class="line">>seq2</span><br><span class="line">RAGEAGAINSTTHEMACHINE</span><br></pre></td></tr></tbody></table></figure><p>运行脚本,指定输出文件为 <code>test.csv</code>:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python esm_score_missense_mutations.py --input-fasta-file test.fa --output-csv-file test.csv</span><br></pre></td></tr></tbody></table></figure><p>运行结束后,<code>test.csv</code> 中的第一列为 fasta 文件中对应的序列 id,第二列为该序列所有可能的氨基酸突变,第三列为基于 ESM1b 计算得到的 ESM score:</p><img src="/pic2/esm_output.png" height="300px"><p>关于 InDel 的 score 计算此处不进行详细阐述,如感兴趣可参考其 github repository README:</p><p><a href="https://github.com/ntranoslab/esm-variants?tab=readme-ov-file#scoring-multi-residue-mutations">https://github.com/ntranoslab/esm-variants?tab=readme-ov-file#scoring-multi-residue-mutations</a></p><h2 id="参考材料"><a href="#参考材料" class="headerlink" title="参考材料"></a>参考材料</h2><ol><li>Meier et al. 2021, bioRxiv: Language models enable zero-shot prediction of the effects of mutations on protein function</li><li>Tabet et al. 2024, Genome Biol: Benchmarking computational variant effect predictors by their ability to infer human traits</li><li>Brandes et al. 2023, Nat Genet: Genome-wide prediction of disease variant effects with a deep protein language model</li><li>EVE (Nature 2021): Disease variant prediction with deep generative models of evolutionary data</li><li>GPN (PNAS 2023): DNA language models are powerful predictors of genome-wide variant effects</li></ol><h2 id="题外话"><a href="#题外话" class="headerlink" title="题外话"></a>题外话</h2><p>esm-variant repository 中有一个 issue 针对其函数命名提出了质疑:</p><p><a href="https://github.com/ntranoslab/esm-variants/issues/12">https://github.com/ntranoslab/esm-variants/issues/12</a></p><p>发起该 issue 的人是 <a href="https://github.com/sokrypton/ColabFold">ColabFold</a> 的创建者,大致内容是 esm-variant 算出来的 score 不应该称作 pll (pseudo-likelihood)。基于这一点,本文也避开了 pll 这一说法直接使用 score 一词进行说明。各位朋友如果要使用其他关于 score 的称呼请自行斟酌。</p></body></html>]]></content>
<summary type="html">本文介绍了如何使用 ESM 计算蛋白序列中各个氨基酸突变的影响分数,并提供了一些其他使用 ESM 的方法和思路。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>基于 JCVI 共线性分析确定一对一直系同源基因</title>
<link href="https://biojuse.com/2024/11/09/%E5%9F%BA%E4%BA%8E%20JCVI%20%E5%85%B1%E7%BA%BF%E6%80%A7%E5%88%86%E6%9E%90%E7%A1%AE%E5%AE%9A%E4%B8%80%E5%AF%B9%E4%B8%80%E7%9B%B4%E7%B3%BB%E5%90%8C%E6%BA%90%E5%9F%BA%E5%9B%A0/"/>
<id>https://biojuse.com/2024/11/09/%E5%9F%BA%E4%BA%8E%20JCVI%20%E5%85%B1%E7%BA%BF%E6%80%A7%E5%88%86%E6%9E%90%E7%A1%AE%E5%AE%9A%E4%B8%80%E5%AF%B9%E4%B8%80%E7%9B%B4%E7%B3%BB%E5%90%8C%E6%BA%90%E5%9F%BA%E5%9B%A0/</id>
<published>2024-11-09T09:30:00.000Z</published>
<updated>2024-11-11T02:54:35.905Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>前段时间因为课题需求,需要获取两个物种间的所有一对一直系同源基因对(One-to-One ortholog, 为避免文章内容冗余后面统称 OTO)。由于两个目标物种在 Ensembl 上都有,所以可以直接通过 BioMart 下载其基因的同源关系并提取出来。</p><p>但经过筛选和过滤后,发现 BioMart 中能确定的 OTO 数量极低,不太能满足分析需求。考虑到我所分析的目标基因类型在不同物种间具有高顺序保守性和序列保守性,因此想看看能不能通过 JCVI 的共线性结果来获取更多的 OTO 作为补充,发现效果还挺不错,固有此文。</p><h2 id="JCVI-介绍"><a href="#JCVI-介绍" class="headerlink" title="JCVI 介绍"></a>JCVI 介绍</h2><p>JCVI 是一个用于比较基因组学分析的多功能工具包,<strong>共线性分析只是它其中的一个子功能</strong>。</p><p>JCVI 的安装,最简单的方式是通过 <code>pip</code>:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install jcvi</span><br></pre></td></tr></tbody></table></figure><p>也可通过 <code>conda</code> 或 <code>mamba</code> 安装:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install bioconda::jcvi</span><br></pre></td></tr></tbody></table></figure><p>此外也需要安装用于比对的 LAST:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda install bioconda::last</span><br></pre></td></tr></tbody></table></figure><p>关于 JCVI 的运行,更多细节可见:<a href="https://github.com/tanghaibao/jcvi/wiki/MCscan-%28Python-version%29">JCVI tutorial</a></p><p>请注意,JCVI 的 wiki 写的并不全面,<strong>很多参数需要灵活调整以适配自身需求</strong>。本文所用到的文件(如注释等)均来自 Ensembl 数据库,如果使用其他来源的文件,请灵活调整参数(或调整源文件格式)以达成相同目的。</p><h2 id="JCVI-寻找-OTO-示例"><a href="#JCVI-寻找-OTO-示例" class="headerlink" title="JCVI 寻找 OTO 示例"></a>JCVI 寻找 OTO 示例</h2><p>本文使用到的文件下载(人和黑猩猩 Ensembl V110):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">wget ftp://ftp.ensembl.org/pub/release-110/fasta/pan_troglodytes/cds/Pan_troglodytes.Pan_tro_3.0.cds.all.fa.gz</span><br><span class="line">wget ftp://ftp.ensembl.org/pub/release-110/gff3/pan_troglodytes/Pan_troglodytes.Pan_tro_3.0.110.gff3.gz</span><br><span class="line">wget ftp://ftp.ensembl.org/pub/release-110/fasta/homo_sapiens/cds/Homo_sapiens.GRCh38.cds.all.fa.gz</span><br><span class="line">wget ftp://ftp.ensembl.org/pub/release-110/gff3/homo_sapiens/Homo_sapiens.GRCh38.110.gff3.gz</span><br></pre></td></tr></tbody></table></figure><p>下文中的命令请根据自身情况修改相关输入和输出文件名,同时检查是否需要修改参数。</p><p>①、首先使用 <code>jcvi.formats.gff</code> 将其 <code>gff</code> 文件中特定的注释类型转为 <code>bed</code> 文件格式:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">python -m jcvi.formats.gff bed --type=mRNA --key=transcript_id --primary_only Pan_troglodytes.Pan_tro_3.0.110.gff3.gz -o chimpanzee.bed</span><br><span class="line">python -m jcvi.formats.gff bed --type=mRNA --key=transcript_id --primary_only Homo_sapiens.GRCh38.110.gff3.gz -o human.bed</span><br><span class="line">python -m jcvi.formats.bed uniq chimpanzee.bed</span><br><span class="line">python -m jcvi.formats.bed uniq human.bed</span><br><span class="line">mv chimpanzee.uniq.bed chimpanzee.bed</span><br><span class="line">mv human.uniq.bed human.bed</span><br></pre></td></tr></tbody></table></figure><p>参数说明:</p><ul><li><code>--type</code>:指定想要提取的注释类型,可以通过逗号分隔同时提取多种信息。这里指定提取蛋白编码基因的 mRNA 序列区间范围。</li><li><code>--key</code>:指定提取时使用的标志信息。由于 Ensembl CDS 文件中序列以 transcipt id 开头,因此此处使用 <code>transcript_id</code> 以和 CDS 文件对应。</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># Ensembl CDS 文件示例</span><br><span class="line">>ENST00000633790.1 cds scaffold:GRCh38:HSCHR7_2_CTG6:503684:504150:1 gene:ENSG00000282748.1 gene_biotype:TR_V_gene transcript_biotype:TR_V_gene gene_symbol:TRBV5-7 description:T cell receptor beta variable 5-7 (non-functional) [Source:HGNC Symbol;Acc:HGNC:12224]</span><br><span class="line">ATGGGCCCCGGGCTCCTCTGCTGGGTGCTGCTTTGTCCCCTAGGAGAAGGCCCAGTGGAC</span><br><span class="line">GCTGGAGTCACCCAAAGTCCCACACACCTGATCAAAACGAGAGGACAGCACGTGACTCTG</span><br><span class="line">AGATGCTCTCCTATCTCTGGGCACACCAGTGTGTCCTCGTACCAACAGGCCCTGGGTCAG</span><br><span class="line">GGGCCCCAGTTTATCTTTCAGTATTATGAGAAAGAAGAGAGAGGAAGAGGAAACTTCCCT</span><br><span class="line">GATCAATTCTCAGGTCACCAGTTCCCTAACTATAGCTCTGAGCTGAATGTGAACGCCTTG</span><br><span class="line">TTGCTAGGGGACTCGGCCCTCTATCTCTGTGCCAGCAGCTTGG</span><br></pre></td></tr></tbody></table></figure><ul><li><code>python -m jcvi.formats.bed uniq</code>:此步将提取的注释中存在重叠的部分删除,最终保留更长的那一个转录本。</li><li><code>--primary</code>:此步仅保留每个 transcript id 下最长的 mRNA。<strong>该步骤和 uniq 步骤的目的都是为了尽可能去除冗余性以提高后续的共线性得分</strong>。</li></ul><p>②、对于 CDS,使用 <code>jcvi.formats.fasta</code> 将其 ID 处理为不带版本尾缀的格式:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">python -m jcvi.formats.fasta format --sep=. Pan_troglodytes.Pan_tro_3.0.cds.all.fa.gz chimpanzee.cds</span><br><span class="line">python -m jcvi.formats.fasta format --sep=. Homo_sapiens.GRCh38.cds.all.fa.gz human.cds</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># 处理前</span><br><span class="line">>ENSPTRT00000098376.1</span><br><span class="line"># 处理后</span><br><span class="line">>ENSPTRT00000098376</span><br></pre></td></tr></tbody></table></figure><p>进行该步骤的原因主要是 <code>--key</code> 提取的 bed 文件中 <code>transcript_id</code> 里并不携带 version,因此这里通过 <code>--sep=.</code> 去除 CDS ID 中的 version 尾缀使 bed 文件和 cds 文件能够相吻合(<strong>在 Ensembl 同一版本的注释信息中不会出现一个 transcript id 同时具有多个 version 的情况</strong>)。</p><p>③、进行成对共线性搜索:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m jcvi.compara.catalog ortholog human chimpanzee --cscore=.99 --no_strip_names</span><br></pre></td></tr></tbody></table></figure><p>该步骤通过 LAST 进行比对,并通过 <code>--cscore=.99</code> 过滤 hit 以保留 reciprocal best hit (RBH)。</p><p>请注意 <code>jcvi.compara.catalog ortholog</code> 后紧接两个需要进行分析的物种文件名,此处应与前文输出的文件前缀对应,例如 human 对应 <code>human.bed</code> 及 <code>human.cds</code>。分析后输出的 <code>.anchors</code> 中即包含保留下的高质量共线性块。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># anchors 文件内容</span><br><span class="line">###</span><br><span class="line">ENST00000616016 ENSPTRT00000090571 2030</span><br><span class="line">ENST00000338591 ENSPTRT00000103781 3000</span><br><span class="line">ENST00000428771 ENSPTRT00000073784 871</span><br><span class="line">ENST00000624697 ENSPTRT00000076196 742</span><br><span class="line">ENST00000453464 ENSPTRT00000098436 1010</span><br><span class="line">ENST00000379339 ENSPTRT00000106256 459</span><br></pre></td></tr></tbody></table></figure><p>④、筛选 OTO:</p><p><code>.anchors</code> 文件中绝大多数都为 one-to-one 的对应关系,但也<strong>依然存在少数为多对一或一对多</strong>,因此需要进行进一步的过滤,该步骤可以通过 Python 实现:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line">jcvi_result_df = pd.read_csv(sys.argv[<span class="number">1</span>], sep=<span class="string">"\t"</span>, comment=<span class="string">"#"</span>, header=<span class="literal">None</span>)</span><br><span class="line">jcvi_result_df.columns = [<span class="string">"spe1"</span>, <span class="string">"spe2"</span>, <span class="string">"score"</span>]</span><br><span class="line"></span><br><span class="line">spe1_counts = jcvi_result_df[<span class="string">"spe1"</span>].value_counts()</span><br><span class="line">spe1_only_once = spe1_counts[spe1_counts == <span class="number">1</span>].index.to_list()</span><br><span class="line"></span><br><span class="line">spe2_counts = jcvi_result_df[<span class="string">"spe2"</span>].value_counts()</span><br><span class="line">spe2_only_once = spe2_counts[spe2_counts == <span class="number">1</span>].index.to_list()</span><br><span class="line"></span><br><span class="line">jcvi_result_filter_df = jcvi_result_df.loc[</span><br><span class="line"> (jcvi_result_df[<span class="string">"spe1"</span>].isin(spe1_only_once)) &</span><br><span class="line"> (jcvi_result_df[<span class="string">"spe2"</span>].isin(spe2_only_once))</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">jcvi_result_filter_df[[<span class="string">"spe1"</span>, <span class="string">"spe2"</span>]].to_csv(<span class="string">f"<span class="subst">{sys.argv[<span class="number">1</span>]}</span>.one2one"</span>, sep=<span class="string">"\t"</span>, index=<span class="literal">False</span>, header=<span class="literal">False</span>)</span><br></pre></td></tr></tbody></table></figure><p>将上述代码保存到一个名为 <code>jcvi_filter.py</code> 的文件中,运行以下命令:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python jcvi_filter.py human.chimpanzee.anchors</span><br></pre></td></tr></tbody></table></figure><p>随后将生成一个名为 <code>human.chimpanzee.anchors.one2one</code> 的文件,其中每一行都为一个 human 和 chimpanzee 间的共线性一对一直系同源基因。</p><h2 id="其他需要注意的地方"><a href="#其他需要注意的地方" class="headerlink" title="其他需要注意的地方"></a>其他需要注意的地方</h2><p>由于 JCVI 除了考虑比对情况以外,也考虑了这些基因在基因组位置上的线性关系,因此使用 JCVI 有利于回收在两种物种基因组上<strong>位置顺序保守</strong>的直系同源基因对,但相对的也掩盖了一些在不同物种间位置不一致的直系同源关系。如果你并不关心基因位置信息,可以考虑直接使用 RBH 方法进行确定。</p></body></html>]]></content>
<summary type="html">本文介绍了一种使用 JCVI 分析结果确定一对一直系同源基因的方法,以期通过这种方式寻找到更多的 One-to-One ortholog。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>获取 vcf 文件中每个个体携带的突变数量</title>
<link href="https://biojuse.com/2024/10/10/%E8%8E%B7%E5%8F%96%20vcf%20%E6%96%87%E4%BB%B6%E4%B8%AD%E6%AF%8F%E4%B8%AA%E4%B8%AA%E4%BD%93%E6%90%BA%E5%B8%A6%E7%9A%84%E7%AA%81%E5%8F%98%E6%95%B0%E9%87%8F/"/>
<id>https://biojuse.com/2024/10/10/%E8%8E%B7%E5%8F%96%20vcf%20%E6%96%87%E4%BB%B6%E4%B8%AD%E6%AF%8F%E4%B8%AA%E4%B8%AA%E4%BD%93%E6%90%BA%E5%B8%A6%E7%9A%84%E7%AA%81%E5%8F%98%E6%95%B0%E9%87%8F/</id>
<published>2024-10-10T06:30:00.000Z</published>
<updated>2024-10-17T02:01:31.557Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>最近的工作涉及到处理大量种群数据,其中有个分析需要统计每个个体携带的突变数量,在网上搜寻了诸多方法后确定了一些比较高效的策略,固有此文记录。</p><blockquote><p>本文的部分流程参考源:</p><p><a href="https://www.biostars.org/p/336206/">BioStars "how to count variants par sample per chromosome in a vcf file?"</a></p></blockquote><h2 id="提取每个个体携带的突变数量"><a href="#提取每个个体携带的突变数量" class="headerlink" title="提取每个个体携带的突变数量"></a>提取每个个体携带的突变数量</h2><p>该流程主要通过 plink2 实现:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">plink2 --vcf [vcf file] --out [plink output1]</span><br><span class="line">plink2 --pfile [plink output1] --sample-counts --out [plink output2]</span><br></pre></td></tr></tbody></table></figure><ul><li><code>[vcf file]</code>:此处填入 vcf 文件的路径。</li><li><code>[plink output1]</code>:此处填入 plink2 输出的文件前缀。</li><li><code>[plink output2]</code>:此处填入 plink2 输出的文件前缀。</li></ul><p>运行结束后,可以得到以 <code>.scount</code> 结尾的文件,其中各列的含义如下:</p><ol><li><strong>#IID</strong>:标识每个样本的编号。</li><li><strong>HOM_REF_CT</strong>:纯合参考基因型计数,个体纯合参考基因型的位点数量。</li><li><strong>HOM_ALT_SNP_CT</strong>:纯合变异基因型计数,个体纯合变异基因型的单核苷酸多态性(SNP)数量。</li><li><strong>HET_SNP_CT</strong>:杂合基因型计数,个体在该位点上是杂合基因型的SNP数量(一条染色体是参考基因型,另一条是变异基因型)。</li><li><strong>DIPLOID_TRANSITION_CT</strong>:二倍体转换变异的数量(转换:嘌呤与嘌呤或嘧啶与嘧啶之间的替换)。</li><li><strong>DIPLOID_TRANSVERSION_CT</strong>:二倍体颠换变异的数量(颠换:嘌呤与嘧啶之间的替换)。</li><li><strong>DIPLOID_NONSNP_NONSYMBOLIC_CT</strong>:二倍体非SNP非符号变异计数。包括一些插入、缺失(INDELs)等。</li><li><strong>DIPLOID_SINGLETON_CT</strong>:二倍体单例变异计数,表示在整个样本集中只出现在该个体中的变异数量。</li><li><strong>HAP_REF_INCL_FEMALE_Y_CT</strong>:单倍体参考基因型计数,即在单倍体区域(Y 染色体)的参考等位基因数量。</li><li><strong>HAP_ALT_INCL_FEMALE_Y_CT</strong>:单倍体变异基因型计数,在单倍体区域中出现的变异等位基因的数量。</li><li><strong>MISSING_INCL_FEMALE_Y_CT</strong>:缺失基因型计数,在样本中缺失的基因型数据计数。</li></ol><p>更多可输出列的详细信息可见:<a href="https://www.cog-genomics.org/plink/2.0/formats#scount">https://www.cog-genomics.org/plink/2.0/formats#scount</a></p><p>具体参数的用法可见:<a href="https://www.cog-genomics.org/plink/2.0/basic_stats#sample_counts">https://www.cog-genomics.org/plink/2.0/basic_stats#sample_counts</a></p><p>通过 <code>HOM_ALT_SNP_CT</code> <code>HET_SNP_CT</code> <code>DIPLOID_NONSNP_NONSYMBOLIC_CT</code> 计算每个个体携带的突变数量。</p><h2 id="提取每个突变对应的携带者信息"><a href="#提取每个突变对应的携带者信息" class="headerlink" title="提取每个突变对应的携带者信息"></a>提取每个突变对应的携带者信息</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bcftools norm -m -any [vcf file] | bcftools query -i 'GT="alt"' -f '%CHROM\t%POS\t%REF\t%ALT[\t%SAMPLE:%GT]\n' - > [output]</span><br></pre></td></tr></tbody></table></figure><ul><li><code>[vcf file]</code>:此处填入 vcf 文件的路径。</li><li> <code>[output]</code>:此处填入输出文件的路径。</li></ul><p>此处通过 <code>bcftools norm -m -any</code> 对多等位基因进行了拆分,可视自身需求去除该部分,此处 <code>GT="alt"</code> 将所有携带替代等位基因的个体提取出并打印。</p><p>输出结果示例:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chr1 228424930 G A 4922792:0/1 4884261:0/1 3988378:0/1 2479308:0/1 4414951:0/1 3862053:0/1</span><br></pre></td></tr></tbody></table></figure><p>前四列用于表示特定突变(分别指示染色体编号、位置、ref 和 alt),后续以制表符分隔记录该突变的携带者及基因型。</p></body></html>]]></content>
<summary type="html">这篇文章介绍了如何获取 vcf 中每个个体携带的突变数量(及每个突变对应的携带者 id)。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>vscode 在远程服务器上通过 jupyter notebook 使用 R</title>
<link href="https://biojuse.com/2024/09/19/vscode%20%E5%9C%A8%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E9%80%9A%E8%BF%87%20jupyter%20notebook%20%E4%BD%BF%E7%94%A8%20R/"/>
<id>https://biojuse.com/2024/09/19/vscode%20%E5%9C%A8%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E9%80%9A%E8%BF%87%20jupyter%20notebook%20%E4%BD%BF%E7%94%A8%20R/</id>
<published>2024-09-19T09:00:00.000Z</published>
<updated>2024-09-20T02:18:17.541Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>最近一段时间的关键分析要用到 R,但是办公电脑的机械硬盘前段时间出了问题,现在用 Rstudio 会非常卡顿,加上相关分析对计算资源的要求也比较高,就想着是否有什么办法能在服务器上也弄一个类似于 Rstudio 的可视化界面出来作为辅助。</p><p>不过看了一圈发现 Rstudio 的 server 版本似乎局限性不少,而且还要用到 root 权限。考虑到平时运行 Python 进行分析时大多数时候都在 jupyter notebook 上进行,就想着是否能套用过来,于是就有了这篇文章。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>该文章的具体实现在 Linux + mamba 环境下完成,如果你没有 conda/mamba 环境,可以参考网上资料进行安装。</p><p>首先确认你的环境中有 R,如果没有可以通过以下命令安装:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mamba install conda-forge::r-base</span><br><span class="line">R # 确认可以进入 R 终端,使用 q() 退出</span><br></pre></td></tr></tbody></table></figure><p>通过 mamba 安装 <a href="https://github.com/IRkernel/IRkernel">IRkernel</a> 和 jupyter(使用 conda 也行,本文章以 mamba 为准):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mamba install r::r-irkernel</span><br><span class="line">mamba install anaconda::jupyter</span><br></pre></td></tr></tbody></table></figure><p>完成上述步骤后,进入 R 终端,输入 <code>IRkernel::installspec()</code>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ R</span><br><span class="line">> IRkernel::installspec()</span><br></pre></td></tr></tbody></table></figure><p>如果在 VScode 中尚未安装 jupyter 扩展,则点开侧栏的 <strong><u>拓展</u></strong> 页面并搜索 jupyter 安装:</p><img src="/pic2/vscode_jupyter1.png" height="400px"><h2 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h2><p>新建一个 jupyter notebook 文件,使用 vscode 打开它:</p><img src="/pic2/vscode_jupyter2.png" height="300px"><p>点击右上角的 <code>select kernel</code>,此时如果仅存在 Python 相关的 kernel,则先在服务器上运行 jupyter notebook:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jupyter notebook</span><br></pre></td></tr></tbody></table></figure><p>此时屏幕输出里会返回 server 的链接,例如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:8888/tree?token=11d321403f5a4d62d94d07ddae56e48017cf641c059573b0</span><br></pre></td></tr></tbody></table></figure><p>在 <code>select kernel</code> 里,点击 <code>Select Another Kernel</code>,此后选择 <code>Existing Jupyter Server</code>,在弹出的窗口里输入上面得到的 server 链接,并选择弹出的 R。图示:</p><img src="/pic2/vscode_jupyter3.png" height="500px"><p>此后即可在 jupyter notebook 中使用 R 进行分析:</p><img src="/pic2/vscode_jupyter4.png" height="400px"></body></html>]]></content>
<summary type="html">这篇文章介绍了 vscode 上如何在远程服务器中通过 jupyter notebook 使用 R(无需 root 权限),包括详细的图文步骤。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>簇突变的类型简介及识别方法(Clustered mutation classification)</title>
<link href="https://biojuse.com/2024/08/23/%E7%B0%87%E7%AA%81%E5%8F%98%E7%9A%84%E7%B1%BB%E5%9E%8B%E7%AE%80%E4%BB%8B%E5%8F%8A%E8%AF%86%E5%88%AB%E6%96%B9%E6%B3%95/"/>
<id>https://biojuse.com/2024/08/23/%E7%B0%87%E7%AA%81%E5%8F%98%E7%9A%84%E7%B1%BB%E5%9E%8B%E7%AE%80%E4%BB%8B%E5%8F%8A%E8%AF%86%E5%88%AB%E6%96%B9%E6%B3%95/</id>
<published>2024-08-23T12:30:00.000Z</published>
<updated>2024-08-23T12:29:12.593Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>基因组中,某些特殊的突变过程可能会导致一小段区域内连续的突变发生。这些突变在基因组中呈现成簇分布,因此被称作 <strong><u>clustered mutation</u><strong>(本文将称其为</strong>簇突变</strong>)。研究这些突变的分布及组成有助于我们揭示导致其发生的内源性和外源性过程。该文章将主要讲述簇突变的类别并列举一些会导致特定类别的生物学原因,同时介绍该领域中的某些生物信息学工具等。</p><h2 id="簇突变类型"><a href="#簇突变类型" class="headerlink" title="簇突变类型"></a>簇突变类型</h2><p>簇突变类型主要被分作六类,其中五类针对碱基水平的变化,一类针对插入及缺失(InDel):</p><ul><li><p><strong>DSB(Doublet Base Substitutions)</strong>:双碱基替换,表示这两个突变<strong>发生在相邻的碱基上</strong>。某些外源性过程例如紫外线损伤会导致双碱基替换的发生(CC>TT),此外 DNA 修复缺陷和聚合酶功能突变也会导致 DSB 发生。</p></li><li><p><strong>MBS (Multiple Base Substitutions)<strong>:多碱基替换,表示在</strong>很短的序列范围内发生多个碱基突变且这些突变彼此相邻</strong>。由于该簇突变的出现数量很有限,因此尚未得到全面研究。</p></li><li><p><strong>Omikli</strong>:源自希腊语,意为 <u>雾</u> 或 <u>薄雾</u>,也被称作 <strong>diffuse hypermutation(弥漫性超突变)</strong>,表现为 IMD(Inter-Mutation Distance,突变间距)小于预期的少数几个碱基突变(两到三个)。多种外源过程可导致 Omikli 事件,内源过程则有单链错配修复等。</p></li><li><p><strong>Kataegis</strong>:源自希腊语,意为 <u>暴雨</u>。其本质上是 <strong>Omikli 的更大规模事件</strong>。表现为 IMD 小于预期的多个碱基突变聚集在一起。当前认为大多数导致 Kataegis 的过程都归因于 <a href="https://en.wikipedia.org/wiki/APOBEC">AID/APOBEC 家族</a>(Omikli 也受部分影响),与双链断裂具有一定关系。</p></li><li><p><strong>Other:</strong>IMD 小于预期但是 <strong>VAF(Variant allele frequency,突变等位基因频率)不相等的一些碱基突变聚集在一起</strong>。而<strong>上述几种突变类型中,一个 clustered mutation group 里所包含的突变 VAF 都相同</strong>。</p><ul><li><blockquote><p>如果簇突变在同一个事件中发生(也可以当作在同一个细胞中发生),那么这些突变的细胞谱系树将完全相同,因此最后得到的 VAF 预期一致。不同则表明这些突变是在多次突变事件中独立发生的。<strong>Other 类型的突变可能表明其所处的基因组区域本身突变率高</strong>,所以严格意义上讲它并不算主流研究中关注的簇突变。</p></blockquote></li></ul></li><li><p><strong>indels</strong>:IMD 小于预期的插入或缺失事件。与微卫星不稳定性有关并常见于错配修复缺陷的细胞中。</p></li></ul><img src="/pic2/clustered_mutation.jpeg" height="500px"><center>Refer: https://osf.io/qpmzw/wiki/2.%20Workflow/</center><h2 id="簇突变的确定"><a href="#簇突变的确定" class="headerlink" title="簇突变的确定"></a>簇突变的确定</h2><p>一般而言,确定簇突变的步骤如下:</p><ol><li>计算一个 IMD 阈值。</li><li>对基因组上的突变进行判断,如果存在与其距离低于 IMD 阈值的突变,则认为其是簇突变。</li></ol><p>在一些比较久之前的文章中,IMD 的阈值是直接人为确定的。例如 <a href="https://www.cell.com/cell/fulltext/S0092-8674(17)30774-2">Supek 等人</a>采用了以下判断标准:</p><p>①、突变之间的距离 <= 500bp。</p><p>②、突变的类型必须相同(例如都为 C>T)且链对应(例如 C 都在正链上)。</p><p>这种划分方法虽然在一定程度上可靠,但是并不适用于所有应用场景,且不同研究采取的标准也不完全相同(例如阈值大小),导致不方便整合比较。</p><p>随着期刊对分析的可靠性要求越来越严格,现在已经有许多用于分析簇突变的生物信息学工具被开发出来,它们采用了更加严谨的策略并且保证了可再现性,例如突变特征分析领域的主流工具套件 SigProfiler 中也提供了用于分析簇突变的 <a href="https://osf.io/qpmzw/wiki/home/">SigProfilerClusters</a>。其运行过程大致如下:</p><p>①、根据突变的上下文(周边序列)等信息,对突变的位置进行打乱重排,以模拟随机情况下的突变分布情况。</p><p>②、根据真实数据与模拟数据的突变分布计算出每个样本的 IMD 阈值。</p><p>③、根据基因组各个窗口内的突变率对 IMD 阈值进行校正。</p><p>④、根据簇突变的 VAF 等信息对其进行分类。</p><p>该团队使用该工具对 PCAWG 项目的数据进行了分析,系统阐述了各癌症类型中的簇突变景观,并发现了 APOBEC3 Kataegis 对 Extrachromosomal DNA (ecDNA) 的作用(<a href="https://www.nature.com/articles/s41586-022-04398-6#Sec8">Bergstrom et al. 2022, Nature</a>)。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol><li>常见的 DSB 特征及生物学成因:<a href="https://cancer.sanger.ac.uk/signatures/dbs/">https://cancer.sanger.ac.uk/signatures/dbs/</a></li><li>Clustered Mutation Signatures Reveal that Error-Prone DNA Repair Targets Mutations to Active Genes, Cell 2017</li><li>Examining clustered somatic mutations with SigProfilerClusters, Bioinformatics 2022</li><li>Mapping clustered mutations in cancer reveals APOBEC3 mutagenesis of ecDNA, Nature 2022</li></ol></body></html>]]></content>
<summary type="html">这篇文章详细介绍了 clustered mutation(簇突变)的各个类型及其划分标准,并描述了用于确定簇突变的方法。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>rsync 中排除文件的规则详解(rsync --exclude)</title>
<link href="https://biojuse.com/2024/08/15/rsync%20%E4%B8%AD%E6%8E%92%E9%99%A4%E6%96%87%E4%BB%B6%E7%9A%84%E8%A7%84%E5%88%99%E8%AF%A6%E8%A7%A3/"/>
<id>https://biojuse.com/2024/08/15/rsync%20%E4%B8%AD%E6%8E%92%E9%99%A4%E6%96%87%E4%BB%B6%E7%9A%84%E8%A7%84%E5%88%99%E8%AF%A6%E8%A7%A3/</id>
<published>2024-08-15T08:50:00.000Z</published>
<updated>2024-08-17T13:13:22.167Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>前段时间在备份服务器时,由于备份策略的变化,对 rsync 的 exclude patterns 进行了一些更加深入的探索。</p><p>这篇文章将介绍如何设计 rsync 的 exclude patterns 以排除特定目录或文件。</p><h2 id="命令介绍"><a href="#命令介绍" class="headerlink" title="命令介绍"></a>命令介绍</h2><p>rsync 是 Linux 和 Unix 中常用的文件复制和同步命令,相比 cp 命令而言更加高效和完善,其优点包括但不限于:</p><ul><li><strong>增量复制</strong>:rsync 只会复制那些自上次同步后更改的文件部分,极大地减少了数据传输量。例如 a 文件已经同步过一次并且没有发生变化,则 rsync 会跳过。同样,rsync 也会进行完整性检查以确定传输的数据无误。</li><li><strong>保留属性</strong>:rsync 可以保持文件的权限、时间戳、软硬链接、所有权等属性。</li><li><strong>灵活的过滤规则</strong>:支持使用包括和排除规则来选择同步的文件或目录。</li></ul><img src="https://tse3-mm.cn.bing.net/th/id/OIP-C.m0CLH5eqL6kvqiFjEVu6VQHaD4?rs=1&pid=ImgDetMain" height="200px"><h2 id="排除规则"><a href="#排除规则" class="headerlink" title="排除规则"></a>排除规则</h2><p>rsync 通过参数 <code>--exclude</code> 或 <code>--exclude-from</code> 指定排除的文件模式。其中 <code>--exclude-from</code> 用于指定包含排除模式的文件(包含多个排除规则)。</p><h3 id="排除指定文件"><a href="#排除指定文件" class="headerlink" title="排除指定文件"></a>排除指定文件</h3><p>输入文件名称(如 <code>123.txt</code>)即可排除所有命名为 <code>123.txt</code> 的文件。</p><h3 id="排除特定尾缀的文件"><a href="#排除特定尾缀的文件" class="headerlink" title="排除特定尾缀的文件"></a><strong>排除特定尾缀的文件</strong></h3><p>通过 <code>*.xxx</code> 排除包含特定尾缀的文件,例如:</p><ul><li><code>*.txt</code> 排除所有以 <code>.txt</code> 结尾的文件。</li><li><code>*vcf</code> 排除所有以 <code>vcf</code> 结尾的文件。注意这里<strong>并不推荐这样做</strong>,因为这<strong>也会排除掉所有以 vcf 结尾的目录</strong>,例如:<ul><li><code>source/dir1/dir_vcf/xxx</code>,这里 <code>dir_vcf</code> 及其中的所有文件也会被排除。</li></ul></li></ul><p>因此在根据尾缀排除文件时,最好添加尾缀前的 <code>.</code> 以避免其他文件夹的意外排除。</p><p>同理也可通过前缀进行文件排除,例如 <code>SRR*</code> 等,但同样地,这会排除符合该条件的所有目录。因此请<strong>谨慎使用</strong>。</p><h3 id="排除隐藏文件及文件夹"><a href="#排除隐藏文件及文件夹" class="headerlink" title="排除隐藏文件及文件夹"></a>排除隐藏文件及文件夹</h3><p>通过 <code>.*</code> <strong>排除所有隐藏文件及隐藏文件夹</strong>。</p><h3 id="排除特定文件夹"><a href="#排除特定文件夹" class="headerlink" title="排除特定文件夹"></a>排除特定文件夹</h3><p>直接使用文件夹名称,例如 <code>anaconda3</code> 等。但注意,<strong>如果有与该文件夹名同名的文件,那么该文件也会被排除</strong>,例如:</p><ul><li>使用 <code>juse</code> 时,<code>source/juse/</code>(目录)会被排除,此外 <code>source/other/juse</code>(文件名为 <code>juse</code>)也会被排除。</li></ul><p>因此如果你想排除特定文件夹但又担心具有同名文件,两个好的权衡方式是:</p><ul><li>添加该目录先前的信息,例如你已经知道其位于其他文件夹中:<code>dir1/dir2</code>(排除位于 dir1 下的 dir2)。</li><li>排除该目录中的所有文件,并保留空文件夹:<code>dir2/**</code>,该做法更加推荐,因为你也可以保留下 '哪些文件夹被排除' 这个信息。</li></ul><p>这里使用两个星号是为了和之后的跨多目录匹配相一致,但其实只用一个 <code>*</code> 的效果是一样的。</p><h3 id="跨目录排除匹配的文件夹"><a href="#跨目录排除匹配的文件夹" class="headerlink" title="跨目录排除匹配的文件夹"></a>跨目录排除匹配的文件夹</h3><p>该方法的应用场景如下:</p><p>用户 <code>juse</code> 的目录里存在多个 <code>work</code> 文件夹,<code>juse</code> 想将它们全部排除掉,但是他认为仅使用 <code>work/**</code> 并不妥当,因为其他人的 <code>work</code> 文件夹中可能存在需要备份的文件,而根据单个目录路径进行排除也存在困难,因为 <code>juse</code> 目录中的 <code>work</code> 文件夹很多且位置不一。</p><ul><li><code>/juse/dir1/work/</code></li><li><code>/juse/work/</code></li><li><code>/juse/dir2/dir3/work/</code></li></ul><p>此时可以通过使用 <code>juse**/work</code> 排除 <code>juse</code> 目录下所有 <code>work</code> 子目录。请注意<strong>仅使用单个 <code>*</code> 时无法实现跨目录匹配</strong>:</p><ul><li><code>juse/*/work</code> 仅能识别到 <code>/juse/dir1/work/</code>,无法识别 <code>/juse/dir2/dir3/work/</code>。</li></ul><p>而使用 <code>juse/**/work</code> 则无法识别到 <code>juse</code> 目录下的 <code>work</code> 文件夹,可以通过去除第一个斜杠避免该问题。</p><p>相比之下 du 命令可以通过单个星号实现跨多个目录匹配,同时两个星号也不影响 du 的使用。</p><h2 id="rsync-使用建议"><a href="#rsync-使用建议" class="headerlink" title="rsync 使用建议"></a>rsync 使用建议</h2><p>首先需要明确一点,rsync <strong>以 source 目录作为根目录进行同步操作,因此并不会因为先前的绝对路径中存在排除模式就导致同步失败</strong>,例如:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rsync -av --exclude 'juse' /juse/dir1/ /pathto/destination/</span><br></pre></td></tr></tbody></table></figure><p>上述命令并不会导致 <code>/juse/dir1/</code> 中的文件全部无法同步,因为其并不会考虑源目录本身的路径信息。</p><p>此外,在使用 rsync 推荐使用以下一些参数:</p><ul><li><code>-av</code>:归档模式 + 输出更多信息,前者保证 rsync 递归复制目录并保留文件的各种属性,后者有助于监控同步过程。</li><li><code>-z</code>:在数据传输过程中进行压缩,在慢速网络中进行同步时建议启用。</li><li><code>--bwlimit</code>:限制带宽(传输速度),避免影响其他进程。</li><li><code>--delete</code> & <code>--delete-excluded</code>:前者删除目标文件夹中源文件夹里不存在的文件,后者删除目标文件夹中被排除模式匹配的文件,适合需要完全同步时使用。</li></ul><p>注意 rsync 可以通过 <code>--exclude-from</code> 使用多个排除模式。</p></body></html>]]></content>
<summary type="html">这篇文章详细介绍了如何设置 rsync 中的 exclude patterns,并说明了在实际应用过程中的一些可选参数。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>Snakemake pipeline 搭建的进阶教程</title>
<link href="https://biojuse.com/2024/07/06/Snakemake%20pipeline%20%E6%90%AD%E5%BB%BA%E7%9A%84%E8%BF%9B%E9%98%B6%E6%95%99%E7%A8%8B/"/>
<id>https://biojuse.com/2024/07/06/Snakemake%20pipeline%20%E6%90%AD%E5%BB%BA%E7%9A%84%E8%BF%9B%E9%98%B6%E6%95%99%E7%A8%8B/</id>
<published>2024-07-06T13:05:00.000Z</published>
<updated>2025-05-27T11:15:18.161Z</updated>
<content type="html"><![CDATA[<html><head></head><body><h2 id="关于-Snakemake"><a href="#关于-Snakemake" class="headerlink" title="关于 Snakemake"></a>关于 Snakemake</h2><p>Snakemake 是一个工作流管理系统,可用于创建易于迁移和复现的数据分析流程(pipeline),因此被广泛应用于生物学分析中。虽然 Snakemake 算是一个独立的编程语言,但其本质是基于 Python 搭建的,因此如果你对 Python 有一定了解,那么上手 Snakemake 就会更容易很多。并且 Python 技能的掌握对于某些情况下 Pipeline 下游分析部分的拓展是有必要的。</p><img src="/pic2/snakemake1.png"><p>本文将结合多个近些年生物论文中公开的 Snakemake Pipeline,介绍如何搭建一个具有一定功能的 Pipeline。</p><p>Snakemake 安装:<a href="https://snakemake.readthedocs.io/en/stable/getting_started/installation.html">https://snakemake.readthedocs.io/en/stable/getting_started/installation.html</a></p><p>Snakemake Pipeline 示例教程:<a href="https://snakemake.readthedocs.io/en/stable/tutorial/basics.html">https://snakemake.readthedocs.io/en/stable/tutorial/basics.html</a></p><p><strong>有利于提升阅读体验的一些条件:</strong></p><ul><li>对于 Python 编程已经具备一定的了解,包括文件的一些基本读写操作和 lambda 表达式的应用。</li><li>具有初步的 Snakemake Pipeline 制作经验。</li></ul><p>阅读后有望获得的一些能力:</p><ul><li>知晓 Snakemake 中一些基本的语法和操作。</li><li>编写具有明确结构的 Snakemake Pipeline。</li></ul><h2 id="搭建-Pipeline"><a href="#搭建-Pipeline" class="headerlink" title="搭建 Pipeline"></a>搭建 Pipeline</h2><p>注意,该 Pipeline 的完整版本在以下 github repository 的 <a href="https://github.com/JuseTiZ/Blog-Snakemake-pipeline/tree/main/Tutorial-Pipeline">Tutorial-Pipeline</a> 可见,建议结合完整 Pipeline 阅读文章以避免不必要的混淆:</p><p><a href="https://github.com/JuseTiZ/Blog-Snakemake-pipeline">https://github.com/JuseTiZ/Blog-Snakemake-pipeline</a></p><p>该 Pipeline 的运行环境也可通过以上 repository 中的指引进行安装和配置。</p><h3 id="Pipeline-要求"><a href="#Pipeline-要求" class="headerlink" title="Pipeline 要求"></a>Pipeline 要求</h3><p><strong>目标:</strong>搭建一个可以根据 SRR accession number 自动下载数据、处理数据和进行表达定量的 Pipeline。</p><p><strong>流程:</strong></p><ol><li><code>prefetch</code> 下载数据,该步骤还需配置 <code>pigz</code>(多线程压缩)及 <code>fasterq-dump</code>(提取 fastq)。</li><li><code>trim_galore</code> 过滤数据</li><li><code>salmon</code> 进行定量</li></ol><p><strong>功能:</strong>①、Pipeline 可以识别来自不同样本的数据,并把同一样本的多个数据进行合并;②、Pipeline 可以分为单端和双端模式进行处理;③、可以自定义各个软件运行时使用的参数;④、下载数据时<strong>如果遇到网络错误应当可以自行重续下载</strong>。</p><p><strong>需要提前准备的:</strong>①、指定自行构建的 salmon 索引;②、已安装好的并放置于环境变量中的软件。</p><p>如果你想将软件的版本控制也考虑进 Pipeline 中,可见本文 <strong><u>Pipeline 拓展</u></strong> 部分。</p><p>你可以通过下述命令进行 hg38 salmon 索引的构建以在后文中使用,或者你可以在阅读本文后将该步骤嵌入 Pipeline 流程中(但并不建议,因为该步骤并不是每一次分析都需要复用,这种情况下最好作为参数指定):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mkdir hg38_salmon_index</span><br><span class="line">wget ftp://ftp.ebi.ac.uk/pub/databases/gencode/Gencode_human/release_44/gencode.v44.pc_transcripts.fa.gz</span><br><span class="line">wget ftp://ftp.ebi.ac.uk/pub/databases/gencode/Gencode_human/release_44/GRCh38.primary_assembly.genome.fa.gz</span><br><span class="line">grep "^>" <(gunzip -c GRCh38.primary_assembly.genome.fa.gz) | cut -d " " -f 1 > decoys.txt</span><br><span class="line">sed -i.bak -e 's/>//g' decoys.txt</span><br><span class="line">cat gencode.v44.pc_transcripts.fa.gz GRCh38.primary_assembly.genome.fa.gz > GRCh38.gentrome.fa.gz</span><br><span class="line">salmon index -t GRCh38.gentrome.fa.gz -d decoys.txt -p 12 -i hg38_salmon_index --gencode > hg38_salmon_index/salmon_index.log 2>&1</span><br></pre></td></tr></tbody></table></figure><h3 id="Pipeline-管理"><a href="#Pipeline-管理" class="headerlink" title="Pipeline 管理"></a>Pipeline 管理</h3><p>首先,一个清晰的文件夹结构对于 Pipeline 的管理和拓展是有帮助的,因此请 <code>cd</code> 到拟存储 Pipeline 的路径,并输入以下命令:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">cd [path]</span><br><span class="line">tutorial_path=$(pwd)</span><br><span class="line">mkdir rules</span><br><span class="line">mkdir config</span><br><span class="line">touch config/config.yaml</span><br><span class="line">mkdir envs</span><br><span class="line">mkdir logs</span><br><span class="line">touch Snakefile</span><br></pre></td></tr></tbody></table></figure><p>这些文件夹的用途将在后续搭建过程中一一介绍。</p><img src="/pic2/snakemake2.png" style="zoom:80%;"><h3 id="Pipeline-搭建"><a href="#Pipeline-搭建" class="headerlink" title="Pipeline 搭建"></a>Pipeline 搭建</h3><p>阅读时请留意每个代码块指定的文件,文件头中的 <code>#</code> 仅作文件名说明用,在实际文件中请将该标头去除。</p><h4 id="1、输出目标文件确定"><a href="#1、输出目标文件确定" class="headerlink" title="1、输出目标文件确定"></a>1、输出目标文件确定</h4><p>搭建一个 Pipeline 具有一定的前置条件,首先<strong>你需要对每一个步骤会产生什么样的文件有具体的了解</strong>,因此<strong>建议完全跑过一套流程以后再进行这一步骤</strong>。</p><p>按照上述目标,我们首先希望 Pipeline 能够做到以下事情:</p><p>①、下载 SRR id 对应的原始数据。</p><p>②、经过一系列处理以后,将这些数据在样本层级进行定量。</p><p>已知第一步我们会得到一些以 <code>.sra</code> 结尾的文件,而完成 ② 后我们会得到所有样本的 <code>quant.sf</code> 文件,这些将决定我们如何<strong>定义 Snakemake 的输出目标</strong>。</p><p>因此我们先创建一个包含我们定量所用 sra id 及其对应的样本名的文件,命名为 <code>group.txt</code>,以下分析将以一个人类海拉细胞示例数据集为例进行: </p><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># group.txt</span><br><span class="line">SRR25601734HeLa_Rep1</span><br><span class="line">SRR25601735HeLa_Rep1</span><br><span class="line">SRR25601736HeLa_Rep2</span><br><span class="line">SRR25601737HeLa_Rep2</span><br></pre></td></tr></tbody></table></figure><p>其中<strong>每一个 sra id 对应一个重复,一个样本可能包含多个重复</strong>,样本的定量结果我们打算放置于 <code>results/[sample]</code> 中,那我们预期得到的文件应该有:</p><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">results/HeLa_Rep1/quant.sf</span><br><span class="line">results/HeLa_Rep2/quant.sf</span><br></pre></td></tr></tbody></table></figure><p>首先,我们需要一个方法指定 <code>group.txt</code> 能让 Snakemake 识别,请在 <code>config/config.yaml</code> 文件中添加以下内容:</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inputfile:</span></span><br><span class="line"> <span class="attr">groupfile:</span> <span class="string">"group.txt"</span></span><br></pre></td></tr></tbody></table></figure><p>这是一个 yaml 数据格式的文件,我们将在 Snakefile 中读取它并确定相关参数。在<strong>之后每次 Pipeline 的复用中一般只需要调整该文件</strong>。</p><p>现在,请打开之前新建的 <code>Snakefile</code> 文件,输入以下内容:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">configfile: <span class="string">"config/config.yaml"</span></span><br><span class="line"></span><br><span class="line">samples = <span class="built_in">set</span>([line.strip().split()[<span class="number">1</span>] <span class="keyword">for</span> line <span class="keyword">in</span> <span class="built_in">open</span>(config[<span class="string">"inputfile"</span>][<span class="string">"groupfile"</span>], <span class="string">'r'</span>)])</span><br><span class="line"></span><br><span class="line">rule <span class="built_in">all</span>:</span><br><span class="line"> <span class="built_in">input</span>:</span><br><span class="line"> expand(<span class="string">"results/{sample}/quant.sf"</span>, sample=samples)</span><br></pre></td></tr></tbody></table></figure><p>逐段解释:</p><ol><li><p>首先我们通过 <code>configfile</code> <strong>指定配置文件</strong>,该命令行会将 <code>config/config.yaml</code> 读取为字典并赋予给 <code>config</code> 变量。</p></li><li><p>此后我们通过 Python 语法读取了先前在 config.yaml 中指定的 <code>group.txt</code>,并将 Sample 的集合传入给了变量 <code>samples</code>。</p></li><li><p>通过 <code>rule all</code> 语句,<strong>确定 Snakemake 最终检查的文件(用于判断该 workflow 是否运行完成)</strong>,其中:</p><ul><li><p><code>expand</code> 为 Snakemake 的特殊语法,本质是遍历并得到一个新列表,即 <code>expand("{x}", x=xlist)</code> 功能上等价于 <code>[f"{x}" for x in xlist]</code>。</p></li><li><p>此处使用 <code>samples</code> 通过 <code>expand</code> 语法将先前预期得到的文件列表传递给了 Snakemake,以使其监控工作流的运行结果。</p></li></ul></li></ol><h4 id="2、工作流框架实现"><a href="#2、工作流框架实现" class="headerlink" title="2、工作流框架实现"></a>2、工作流框架实现</h4><p>到这里,我们已经<strong>为工作流指定了最终的目标文件,现在我们可以开始搭建得到这一系列文件所需要的框架</strong>,接下来的步骤将主要在 <code>rule</code> 文件夹下进行,并涉及到一些 <code>config.yaml</code> 中参数的补充。</p><p>由于我们想要开发的 Pipeline <strong>能够根据单端和双端数据进行调整</strong>,因此我们现在 <code>config.yaml</code> 文件中添加一个新的参数:</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">mode:</span> <span class="string">"paired"</span></span><br></pre></td></tr></tbody></table></figure><p>该 mode 参数也可改为 single,后续我们框架中每个 rule 的行为将根据该 mode 的指定发生变化。</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd $tutorial_path/rules</span><br><span class="line">touch common.smk # 该文件将用于编写 Python 函数以拓展后续 rule 的动态性</span><br></pre></td></tr></tbody></table></figure><p>根据先前拟定的 Pipeline 流程,我们首先创建一个<strong>下载规则</strong>文件:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">touch 01_prefetch.smk</span><br></pre></td></tr></tbody></table></figure><p>下载部分中我们可能需要调整的参数:</p><ol><li>可以<strong>同时并行的下载任务数量</strong>。该点将通过运行 Snakemake 时指定 <code>--resources</code> 实现。</li><li>下载如果失败(因为网络原因)<strong>重试的次数</strong>。该点将通过在 <code>config.yaml</code> 中添加参数实现。</li></ol><p><code>config.yaml</code> 新添参数以让下载失败时重新运行,最多运行三次:</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">download:</span></span><br><span class="line"> <span class="attr">retry:</span> <span class="number">3</span></span><br></pre></td></tr></tbody></table></figure><p><code>01_prefetch.smk</code> 内容:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">rule download:</span><br><span class="line"> output:</span><br><span class="line"> srafile = <span class="string">"rawfastq/{srr}/{srr}.sra"</span></span><br><span class="line"> log:</span><br><span class="line"> <span class="string">"logs/{srr}_download_prefetch.log"</span></span><br><span class="line"> params:</span><br><span class="line"> srrid = <span class="string">"{srr}"</span></span><br><span class="line"> threads: <span class="number">1</span></span><br><span class="line"> retries: config[<span class="string">"download"</span>][<span class="string">"retry"</span>]</span><br><span class="line"> resources:</span><br><span class="line"> download_slots = <span class="number">1</span> </span><br><span class="line"> shell:</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> if [ -e {output.srafile}.lock ]; then</span></span><br><span class="line"><span class="string"> rm {output.srafile}.lock</span></span><br><span class="line"><span class="string"> echo "{output.srafile} lock found but no output file! Deleting..." >> {log}</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> prefetch --max-size 100000000 --progress --output-directory rawfastq {params.srrid} > {log} 2>&1</span></span><br><span class="line"><span class="string"> if [ -e {output.srafile} ]; then</span></span><br><span class="line"><span class="string"> echo "{output.srafile} download finished!" >> {log}</span></span><br><span class="line"><span class="string"> else</span></span><br><span class="line"><span class="string"> mv {output.srafile}* {output.srafile}</span></span><br><span class="line"><span class="string"> echo "{output.srafile} not find! May end with .sralite. Renaming..." >> {log}</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"> """</span></span><br></pre></td></tr></tbody></table></figure><p>逐段解释:</p><ol><li><code>output</code> 段用于<strong>定义该 rule 用于产生什么文件</strong>,Snakemake 会自行创建相应目录(如果不存在)。运行完该 rule 后 Snakemake 会检查是否有产生 <code>output</code> 中指定的输出文件,<strong>如果没有则判断其运行失败</strong>。上例中该段指定了一个输出文件 <code>srafile</code>,其中<strong>通配符 <code>{srr}</code> 的识别方式可见下一个逐段解释</strong>。</li><li><code>log</code> 段用于<strong>定义该 rule 的日志文件</strong>,可以在相应命令行中将屏幕输出记录在该文件中。<code>params</code> 段用于<strong>定义该 rule 使用的参数</strong>。<code>threads</code> 段用于<strong>定义该 rule 使用的线程数(核数)</strong>。</li><li><code>retries</code> 段用于<strong>定义该 rule 失败后重新运行的次数</strong>,如果不指定则该 rule 失败后即会停止 Pipeline 的运行(正在进行的任务不会中断)。可以在 snakemake 运行时通过 <code>--keep-going</code> 使工作流继续运行其他独立的任务。</li><li><code>resources</code> 段用于<strong>定义该 rule 所占用的资源</strong>,可以在 snakemake 运行时通过 <code>--resources</code> 指定资源总量。以该 rule 为例,每个下载任务在 rule 中指定会占用 1 个下载槽(将该资源命名为 download_slots),在运行 snakemake 时可通过 <code>--resources download_slots=4</code> 分配四个下载槽,使下载任务最多同时进行四个。</li><li><code>shell</code> 段用于<strong>定义该 rule 执行的 shell 命令行</strong>,内容解释如下:<ul><li><code>{output.srafile}</code> 将替换为 output 段的 srafile 字段。首先,让其检查是否存在 <code>.lock</code> 文件(<code>prefetch</code> 在下载过程中会产生的文件,如果不移除则无法重续下载),这一步是<strong>为失败以后重试而准备的</strong>。此后通过 <code>prefetch</code> 下载,将日志输出到 <code>{log}</code> 中。下载结束后进行检查,将后缀统一为 <code>.sra</code>(部分 sra 文件以 <code>.sralite</code> 结尾)。</li></ul></li></ol><p>对于一个 rule 而言,最少<strong>仅需要 <code>output</code> 和指令(如 <code>shell</code>、<code>script</code>、<code>run</code>)部分,其他都是可选字段(增强 rule 的功能和可管理性)</strong>。</p><p>下载结束后就是我们还需要进行处理,即将 <code>sra</code> 文件提取为 fastq 文件(使用 <code>fasterq-dump</code>),考虑到这也属于原始数据的处理,因此新的规则可以继续添加在 <code>01_prefetch.smk</code> 中,首先我们确定一些要提前确定的参数:</p><ul><li><code>fasterq-dump</code> 和 <code>pigz</code> 使用的线程数。</li></ul><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># config.yaml</span></span><br><span class="line"><span class="attr">download:</span></span><br><span class="line"> <span class="attr">retry:</span> <span class="number">3</span></span><br><span class="line"> <span class="attr">extract_threads:</span> <span class="number">6</span></span><br></pre></td></tr></tbody></table></figure><p>此外,这一步中<strong>单端时仅产生一个文件,双端时产生两个文件</strong>。因此规则需要根据 <code>config</code> 中指定的 <code>mode</code> 进行行为调整:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 01_prefetch.smk</span></span><br><span class="line">rule extract:</span><br><span class="line"> <span class="built_in">input</span>:</span><br><span class="line"> srafile = <span class="string">"rawfastq/{srr}/{srr}.sra"</span></span><br><span class="line"> output:</span><br><span class="line"> fastq_files = <span class="string">"rawfastq/{srr}.fastq.gz"</span> <span class="keyword">if</span> config[<span class="string">"mode"</span>] == <span class="string">"single"</span> <span class="keyword">else</span> [<span class="string">"rawfastq/{srr}_1.fastq.gz"</span>, <span class="string">"rawfastq/{srr}_2.fastq.gz"</span>],</span><br><span class="line"> log:</span><br><span class="line"> <span class="string">"logs/{srr}_extract.log"</span></span><br><span class="line"> params:</span><br><span class="line"> srrid = <span class="string">"{srr}"</span></span><br><span class="line"> threads: config[<span class="string">"download"</span>][<span class="string">"extract_threads"</span>]</span><br><span class="line"> shell:</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> fasterq-dump {input.srafile} --progress --details --split-files -v --outdir rawfastq --threads {threads} > {log} 2>&1</span></span><br><span class="line"><span class="string"> pigz -p {threads} rawfastq/{params.srrid}*.fastq</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> if [ "{config[mode]}" == "single" ]; then</span></span><br><span class="line"><span class="string"> if [ -e rawfastq/{params.srrid}_1.fastq.gz ]; then</span></span><br><span class="line"><span class="string"> mv rawfastq/{params.srrid}_1.fastq.gz {output.fastq_files}</span></span><br><span class="line"><span class="string"> echo "Single extract finished! (Renamed _1.fastq.gz)" >> {log}</span></span><br><span class="line"><span class="string"> else</span></span><br><span class="line"><span class="string"> echo "Single extract finished!" >> {log}</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"> else</span></span><br><span class="line"><span class="string"> if [ -e rawfastq/{params.srrid}_1.fastq.gz ] && [ -e rawfastq/{params.srrid}_2.fastq.gz ]; then</span></span><br><span class="line"><span class="string"> echo "Paired extract finished!" >> {log}</span></span><br><span class="line"><span class="string"> else</span></span><br><span class="line"><span class="string"> echo "Paired extract failed: one or both FASTQ files missing!" >> {log}</span></span><br><span class="line"><span class="string"> exit 1</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"> """</span></span><br></pre></td></tr></tbody></table></figure><p>逐段解释:</p><ol><li><code>input</code> 段<strong>指定该 rule 需要的输入文件</strong>,snakemake 将进行监测直到这些文件出现后再开始运行该 rule。<u>这里涉及到 snakemake 中最重要的一个概念</u>:<ul><li>Snakemake <strong>将依据各个 rule 的 input 和 output 确认它们彼此之间的依赖关系,构建一个工作流的有向无环图</strong>,此后按照图的拓扑顺序运行,确保某一规则在依赖的其他规则结束后才会执行。同样也是在这个过程中,Snakemake 会自动根据目标文件向各规则的 input 和 output 中进行通配符填补(即上述的 <code>{srr}</code>)。以本文拟构建的 Pipeline 为例(<code>snakemake --dag | dot -Tpng > dag.png</code>):</li></ul></li></ol><img src="/pic2/dag.png" style="zoom:80%;"><center>更详细的 {srr} 确定可见后文 —— 理解 Pipeline 的信息传递</center><ol start="2"><li><p><code>output</code> 段新增了根据 mode 进行的输出文件调整。</p></li><li><p><code>shell</code> 段中,首先通过 <code>fasterq-dump</code> 进行提取,并使用 <code>pigz</code> 进行压缩。此后,如果使用单端模式,则将后缀统一为 <code>[SRR id].fastq.gz</code>;如果使用双端模式,则检查是否两端数据都存在。</p></li></ol><p>到此即产生我们所需要的原始数据,将这些 rule 整合进 <code>Snakefile</code> 中(声明 <code>smk</code> 文件):</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Snakefile</span></span><br><span class="line"><span class="comment"># download and extract SRA files</span></span><br><span class="line">include: <span class="string">"rules/01_prefetch.smk"</span></span><br></pre></td></tr></tbody></table></figure><p>此后 <code>trim_galore</code> 的流程构建也遵循相同的思路(设定参数 —— 编写规则 —— 整合):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">touch 02_trimgalore.smk</span><br></pre></td></tr></tbody></table></figure><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># config.yaml</span></span><br><span class="line"><span class="attr">trim:</span></span><br><span class="line"> <span class="attr">threads:</span> <span class="number">8</span></span><br><span class="line"> <span class="attr">param:</span> <span class="string">""</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 02_trimgalore.smk</span></span><br><span class="line">rule trim_galore:</span><br><span class="line"> <span class="built_in">input</span>:</span><br><span class="line"> fastq_files = <span class="string">"rawfastq/{srr}.fastq.gz"</span> <span class="keyword">if</span> config[<span class="string">"mode"</span>] == <span class="string">"single"</span> <span class="keyword">else</span> [<span class="string">"rawfastq/{srr}_1.fastq.gz"</span>, <span class="string">"rawfastq/{srr}_2.fastq.gz"</span>]</span><br><span class="line"> output:</span><br><span class="line"> trimmed_fastq = <span class="string">"trimgalore_result/{srr}_trimmed.fq.gz"</span> <span class="keyword">if</span> config[<span class="string">"mode"</span>] == <span class="string">"single"</span> <span class="keyword">else</span> [<span class="string">"trimgalore_result/{srr}_1_val_1.fq.gz"</span>, <span class="string">"trimgalore_result/{srr}_2_val_2.fq.gz"</span>]</span><br><span class="line"> params:</span><br><span class="line"> option = config[<span class="string">"trim"</span>][<span class="string">"param"</span>]</span><br><span class="line"> log:</span><br><span class="line"> <span class="string">"logs/{srr}_trimgalore.log"</span></span><br><span class="line"> threads: config[<span class="string">"trim"</span>][<span class="string">"threads"</span>]</span><br><span class="line"> shell:</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> if [ "{config[mode]}" == "single" ]; then</span></span><br><span class="line"><span class="string"> trim_galore {params.option} --cores {threads} {input.fastq_files} -o trimgalore_result/ > {log} 2>&1</span></span><br><span class="line"><span class="string"> else</span></span><br><span class="line"><span class="string"> trim_galore --paired {params.option} --cores {threads} {input.fastq_files[0]} {input.fastq_files[1]} -o trimgalore_result/ > {log} 2>&1</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"> """</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Snakefile</span></span><br><span class="line"><span class="comment"># Trim fastq</span></span><br><span class="line">include: <span class="string">"rules/02_trimgalore.smk"</span></span><br></pre></td></tr></tbody></table></figure><p>这里将监测先前 <code>extract</code> 规则输出的文件,如果 <code>extract</code> 规则运行完成则启动,并根据单端和双端模式自动调节输入的 fastq 文件和过滤后的 fastq 文件。可以设置的参数有两个,一个是每个 <code>trim_galore</code> 使用的线程数,另一个是 <code>trim_galore</code> 的额外参数。通过<strong>设置一个可以灵活调节的参数项可以让使用者依据自身的情况进行参数设定而不至于受 Pipeline 限制</strong>,例如自行设置最短长度和接头序列等。如果不需要则留空即可。</p><p><code>salmon</code> 的构造流程大致相同,但由于这里使用的通配符将变为样本名,因此这里需要进行一定变动:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">touch 03_salmon.smk</span><br></pre></td></tr></tbody></table></figure><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># config.yaml</span></span><br><span class="line"><span class="attr">salmon:</span></span><br><span class="line"> <span class="attr">threads:</span> <span class="number">16</span></span><br><span class="line"> <span class="attr">salmon_index:</span> <span class="string">"/path/to/hg38_salmon_index"</span></span><br><span class="line"> <span class="attr">param:</span> <span class="string">"--seqBias --gcBias --dumpEq"</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 03_salmon.smk</span></span><br><span class="line">rule salmon:</span><br><span class="line"> <span class="built_in">input</span>:</span><br><span class="line"> trimmed_fastq = <span class="keyword">lambda</span> wildcards: get_fastq_list(config[<span class="string">"mode"</span>], config[<span class="string">"inputfile"</span>][<span class="string">"groupfile"</span>], wildcards.sample)</span><br><span class="line"> output:</span><br><span class="line"> count_file = <span class="string">"results/{sample}/quant.sf"</span></span><br><span class="line"> params:</span><br><span class="line"> index = config[<span class="string">"salmon"</span>][<span class="string">"salmon_index"</span>],</span><br><span class="line"> samplename = <span class="string">"{sample}"</span>,</span><br><span class="line"> option = config[<span class="string">"salmon"</span>][<span class="string">"param"</span>],</span><br><span class="line"> fastqpath = <span class="keyword">lambda</span> wildcards: get_fastq_path(config[<span class="string">"mode"</span>], config[<span class="string">"inputfile"</span>][<span class="string">"groupfile"</span>], wildcards.sample)</span><br><span class="line"> log:</span><br><span class="line"> <span class="string">"logs/{sample}_salmon.log"</span></span><br><span class="line"> threads: config[<span class="string">"salmon"</span>][<span class="string">"threads"</span>]</span><br><span class="line"> shell:</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> if [ "{config[mode]}" == "single" ]; then</span></span><br><span class="line"><span class="string"> salmon quant -i {params.index} -l A -r <(zcat {params.fastqpath}) -p {threads} {params.option} -o results/{params.samplename} > {log} 2>&1</span></span><br><span class="line"><span class="string"> else</span></span><br><span class="line"><span class="string"> salmon quant -i {params.index} -l A -1 <(zcat {params.fastqpath[0]}) -2 <(zcat {params.fastqpath[1]}) -p {threads} {params.option} -o results/{params.samplename} > {log} 2>&1</span></span><br><span class="line"><span class="string"> fi</span></span><br><span class="line"><span class="string"> """</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Snakefile</span></span><br><span class="line"><span class="comment"># Salmon quant</span></span><br><span class="line">include: <span class="string">"rules/03_salmon.smk"</span></span><br></pre></td></tr></tbody></table></figure><p>可以看到这里使用的通配符变为了 <code>{sample}</code>,并且使用了两个自行构造的函数 <code>get_fastq_list</code> 及 <code>get_fastq_path</code>,分别用于获取每个样本中所有 fastq 文件的列表和路径。这两个函数的具体实现在 <code>common.smk</code> 中进行:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> csv</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_fastq_list</span>(<span class="params">mode, filepath, group</span>):</span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> Get fastq list for each group.</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> group_fastq = {}</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">'r'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line"> info = line.strip().split()</span><br><span class="line"> <span class="keyword">if</span> info[<span class="number">1</span>] <span class="keyword">not</span> <span class="keyword">in</span> group_fastq:</span><br><span class="line"> group_fastq[info[<span class="number">1</span>]] = [info[<span class="number">0</span>]]</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> group_fastq[info[<span class="number">1</span>]].append(info[<span class="number">0</span>])</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> mode == <span class="string">"single"</span>:</span><br><span class="line"> fastq_path_list = [<span class="string">f'trimgalore_result/<span class="subst">{i}</span>_trimmed.fq.gz'</span> <span class="keyword">for</span> i <span class="keyword">in</span> group_fastq[group]]</span><br><span class="line"> <span class="keyword">return</span> fastq_path_list</span><br><span class="line"> <span class="keyword">elif</span> mode == <span class="string">"paired"</span>:</span><br><span class="line"> fastq_path_list1 = [<span class="string">f'trimgalore_result/<span class="subst">{i}</span>_1_val_1.fq.gz'</span> <span class="keyword">for</span> i <span class="keyword">in</span> group_fastq[group]]</span><br><span class="line"> fastq_path_list2 = [<span class="string">f'trimgalore_result/<span class="subst">{i}</span>_2_val_2.fq.gz'</span> <span class="keyword">for</span> i <span class="keyword">in</span> group_fastq[group]]</span><br><span class="line"> fastq_path_list = fastq_path_list1 + fastq_path_list2</span><br><span class="line"> <span class="keyword">return</span> fastq_path_list</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_fastq_path</span>(<span class="params">mode, filepath, group</span>):</span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> Get fastq path for each group.</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> group_fastq = {}</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">'r'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> <span class="keyword">for</span> line <span class="keyword">in</span> f:</span><br><span class="line"> info = line.strip().split()</span><br><span class="line"> <span class="keyword">if</span> info[<span class="number">1</span>] <span class="keyword">not</span> <span class="keyword">in</span> group_fastq:</span><br><span class="line"> group_fastq[info[<span class="number">1</span>]] = [info[<span class="number">0</span>]]</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> group_fastq[info[<span class="number">1</span>]].append(info[<span class="number">0</span>])</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> mode == <span class="string">"single"</span>:</span><br><span class="line"> fastq_path_list = [<span class="string">f'trimgalore_result/<span class="subst">{i}</span>_trimmed.fq.gz'</span> <span class="keyword">for</span> i <span class="keyword">in</span> group_fastq[group]]</span><br><span class="line"> <span class="keyword">return</span> <span class="string">' '</span>.join(fastq_path_list)</span><br><span class="line"> <span class="keyword">elif</span> mode == <span class="string">"paired"</span>:</span><br><span class="line"> fastq_path_list1 = [<span class="string">f'trimgalore_result/<span class="subst">{i}</span>_1_val_1.fq.gz'</span> <span class="keyword">for</span> i <span class="keyword">in</span> group_fastq[group]]</span><br><span class="line"> fastq_path_list2 = [<span class="string">f'trimgalore_result/<span class="subst">{i}</span>_2_val_2.fq.gz'</span> <span class="keyword">for</span> i <span class="keyword">in</span> group_fastq[group]]</span><br><span class="line"> <span class="keyword">return</span> [<span class="string">' '</span>.join(fastq_path_list1), <span class="string">' '</span>.join(fastq_path_list2)]</span><br></pre></td></tr></tbody></table></figure><p>这两个函数根据接收到的模式及 <code>group.txt</code> 内容,返回特定 sample 对应的 fastq 文件列表和路径,其中<strong>列表传输给 input 使其监视上游依赖 rule 的运行情况,而路径传输给特定参数以在 salmon 定量中对同一样本的测序数据进行合并</strong>。</p><p>这里涉及到的其他一些概念:</p><ul><li><strong>wildcards 是一个命名空间,在函数和 lambda 表达式中用于访问通配符。</strong><code>wildcards.sample</code> 只有在规则的输出、输入或其他字段中定义了 <code>{sample}</code> 占位符时才会存在。<strong>结合 lambda 和 wildcards 并通过自定义的处理函数可以获取需要动态规划的输入和输出。</strong>注意,直接在这些函数中给定 <code>{sample}</code> 是不可行的。</li><li>common.smk 同样需要在 Snakefile 中声明,此后该文件中定义的所有函数都可以被其他规则所识别。</li></ul><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Snakefile</span></span><br><span class="line">include: <span class="string">"rules/common.smk"</span></span><br></pre></td></tr></tbody></table></figure><p>到这里,Pipeline 的整体搭建就已完成。</p><h4 id="3、Pipeline-运行"><a href="#3、Pipeline-运行" class="headerlink" title="3、Pipeline 运行"></a>3、Pipeline 运行</h4><p>使用 snakemake 命令行运行 Pipeline:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd $tutorial_path</span><br><span class="line">snakemake --core 16 --resources download_slots=4</span><br></pre></td></tr></tbody></table></figure><p>这里 <code>--core</code> 指定 snakemake 总共可以管理的 CPU 核数量,snakemake 将根据每个 rule 占用的 threads 自动分配任务,例如:</p><ul><li><p>如果一个 extract 要占用 8 个 threads,一个 salmon 要占用 4 个 threads。那么 snakemake 可能同时进行一个 extract 任务和两个 salmon 任务,<strong>确保提供的核被最大化利用(前提是这些任务的前置依赖都已经满足)</strong>。</p></li><li><p><code>--resources</code> 指定 snakemake 的其他分配资源,这些都是用户规定的。以上述命令为例,Snakemake 共有 4 个 download_slots 资源,而在需要 download_slots 资源运行的 rule 中,同时运行所需的资源总和将被限定在这个值以内。在该 Pipeline 中即最高只可同时并行四个下载任务。</p></li></ul><p>运行完成后,留意以下文件夹:</p><ul><li><code>log</code> 文件夹中包含各个环节运行时的输出日志文件。</li><li><code>rawfastq</code> 文件夹中为原始测序数据。</li><li><code>trimgalore_result </code> 文件夹中为过滤后的测序数据。</li><li><code>results</code> 文件夹中为各样本的定量结果。</li></ul><p>实测以上 Pipeline 能够正确运行,如果存在问题请前往 github repository 页面查看完整 Pipeline 并确定出错点:</p><p><a href="https://github.com/JuseTiZ/Blog-Snakemake-pipeline/tree/main/Tutorial-Pipeline">https://github.com/JuseTiZ/Blog-Snakemake-pipeline/tree/main/Tutorial-Pipeline</a></p><h4 id="4、理解-Pipeline-的信息传递"><a href="#4、理解-Pipeline-的信息传递" class="headerlink" title="4、理解 Pipeline 的信息传递"></a>4、理解 Pipeline 的信息传递</h4><p>或许到现在你对各个部分中通配符的确认还是有些云里雾里,不过只要认识了整体框架,再回过头来去理解就并不困难。</p><p>在最后一个 smk 文件中,<strong>规则 salmon 的 output 是唯一吻合 Snakemake 文件里 rule all input 的输出</strong>:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># rule salmon 的 output</span></span><br><span class="line">rule salmon:</span><br><span class="line"> output:</span><br><span class="line"> count_file = <span class="string">"results/{sample}/quant.sf"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Snakemake 文件</span></span><br><span class="line">samples = <span class="built_in">set</span>([line.strip().split()[<span class="number">1</span>] <span class="keyword">for</span> line <span class="keyword">in</span> <span class="built_in">open</span>(config[<span class="string">"inputfile"</span>][<span class="string">"groupfile"</span>], <span class="string">'r'</span>)])</span><br><span class="line">rule <span class="built_in">all</span>:</span><br><span class="line"> <span class="built_in">input</span>:</span><br><span class="line"> expand(<span class="string">"results/{sample}/quant.sf"</span>, sample=samples)</span><br></pre></td></tr></tbody></table></figure><p>基于此,<strong>rule all input 的列表将传递给 rule salmon output 中的 count_file</strong>,所以最后一个规则里 {sample} 的内容也就对应 samples 里的元素。</p><p>而规则 salmon 中,我们在 input 里基于 <code>wildcards.sample</code> 获取了该规则所需的 fastq 输入(通过 <code>get_fastq_list</code> 函数),我们不妨直接运行该函数试试看:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># HeLa_Rep1 & HeLa_Rep2 任选一个</span></span><br><span class="line"><span class="meta">>>> </span>get_fastq_list(<span class="string">"paired"</span>, <span class="string">"group.txt"</span>, <span class="string">"HeLa_Rep2"</span>)</span><br><span class="line">[<span class="string">'trimgalore_result/SRR25601736_1_val_1.fq.gz'</span>, <span class="string">'trimgalore_result/SRR25601737_1_val_1.fq.gz'</span>, <span class="string">'trimgalore_result/SRR25601736_2_val_2.fq.gz'</span>, <span class="string">'trimgalore_result/SRR25601737_2_val_2.fq.gz'</span>]</span><br></pre></td></tr></tbody></table></figure><p><strong>这里所需的 fastq 输入,进一步转变为对上一规则 trim_galore 的依赖</strong>,因此<strong>此处的 SRR ID 被 trim_galore 的 output 识别并替换其中的通配符</strong> {srr}:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">rule trim_galore:</span><br><span class="line"> <span class="built_in">input</span>:</span><br><span class="line"> fastq_files = <span class="string">"rawfastq/{srr}.fastq.gz"</span> <span class="keyword">if</span> config[<span class="string">"mode"</span>] == <span class="string">"single"</span> <span class="keyword">else</span> [<span class="string">"rawfastq/{srr}_1.fastq.gz"</span>, <span class="string">"rawfastq/{srr}_2.fastq.gz"</span>]</span><br><span class="line"> output:</span><br><span class="line"> trimmed_fastq = <span class="string">"trimgalore_result/{srr}_trimmed.fq.gz"</span> <span class="keyword">if</span> config[<span class="string">"mode"</span>] == <span class="string">"single"</span> <span class="keyword">else</span> [<span class="string">"trimgalore_result/{srr}_1_val_1.fq.gz"</span>, <span class="string">"trimgalore_result/{srr}_2_val_2.fq.gz"</span>]</span><br></pre></td></tr></tbody></table></figure><p>同理,trim_galore 的输入要求向上传递,直至到 rule download 为止。</p><h2 id="拓展-Pipeline"><a href="#拓展-Pipeline" class="headerlink" title="拓展 Pipeline"></a>拓展 Pipeline</h2><p>以下内容不再详细解释 Pipeline 中如何具体实现,但原理较简单且具有一定实用性,因此这里做一些简单介绍。</p><h3 id="版本控制"><a href="#版本控制" class="headerlink" title="版本控制"></a>版本控制</h3><p>每个 rule 在运行时都可以指定一个 conda 环境,例如:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">rule download:</span><br><span class="line">......</span><br><span class="line"> conda:</span><br><span class="line"> <span class="string">"../envs/sratool.yaml"</span></span><br><span class="line"> ......</span><br></pre></td></tr></tbody></table></figure><p><strong>这里的相对路径是以 rule 所在 smk 文件开始寻找的</strong>,你可以在 <code>../envs/sratool.yaml</code> 中填写以下内容:</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">sra</span></span><br><span class="line"><span class="attr">dependencies:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">bioconda::sra-tools==3.1.0</span></span><br></pre></td></tr></tbody></table></figure><p>在运行该规则时,Snakemake 会寻找名为 sra 的环境,如果不存在则新建该环境并安装指定依赖(sra-tools)。</p><p>请注意,Snakemake 所有环境操作都是默认使用 <code>mamba</code> 进行的,如果你想使用 <code>conda</code> 进行需要在命令行中指定 <code>--conda-frontend conda</code>(不推荐)。</p><p>另外你也可以在 snakefile 中指定运行这套 pipeline 所需的最低版本,例如:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> snakemake.utils <span class="keyword">import</span> min_version</span><br><span class="line">min_version(<span class="string">"6.0"</span>)</span><br></pre></td></tr></tbody></table></figure><h3 id="性能监测"><a href="#性能监测" class="headerlink" title="性能监测"></a>性能监测</h3><p>每个 rule 在运行时都可以指定一个 benchmark 文件,例如:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">rule download:</span><br><span class="line">......</span><br><span class="line"> benchmark:</span><br><span class="line"> <span class="string">"logs/{srr}_download_prefetch.benchmark.txt"</span>,</span><br><span class="line"> ......</span><br></pre></td></tr></tbody></table></figure><p>Snakemake 会自动测量并记录该 rule 的执行时间、CPU 使用率、内存使用情况等信息并储存到 benchmark 文件中。<strong>这对于需要优化工作流和分析性能瓶颈的情况非常有用</strong>。</p><h2 id="再次改进的可能"><a href="#再次改进的可能" class="headerlink" title="再次改进的可能"></a>再次改进的可能</h2><p>不难看出,上述 Pipeline 依然存在很多可以改进的地方以增强 Pipeline 的可读性和可拓展性,但要做到这些可能会在一定程度上提升理解难度,因此如果你认为上述内容不算困难且想要进一步检验自己当前的能力,可以考虑对该 Pipeline 做以下改进:</p><ol><li>将 <code>group.txt</code> 换为 <code>sample.yaml</code>,在 <code>Snakefile</code> 中提取到 <code>config</code> 变量里,据此进行 rule all input 的指定(可通过动态函数进行),并在 salmon 规则中的 input 和 output 里使用更具效率的函数进行动态规划,<code>sample.yaml</code> 示例:</li></ol><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">samples:</span></span><br><span class="line"> <span class="attr">HeLa_Rep1:</span></span><br><span class="line"> <span class="attr">SRR:</span> <span class="string">"SRR25601734|SRR25601735"</span></span><br><span class="line"> <span class="attr">HeLa_Rep2:</span></span><br><span class="line"> <span class="attr">SRR:</span> <span class="string">"SRR25601736|SRR25601737"</span></span><br></pre></td></tr></tbody></table></figure><ol start="2"><li>将涉及单端双端判断的 rule 拆分成两个 rule(一个用于 single-end,一个用于 paired-end),并将 <code>mode</code> 参数从 <code>config.yaml</code> 中移除,使 Pipeline 能够同时处理单端和双端的数据。例如可以通过在上面提到的 <code>sample.yaml</code> 中添加每个样本的数据类型来进行单双端数据的混合处理:</li></ol><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">samples:</span></span><br><span class="line"> <span class="attr">HeLa_Rep1:</span></span><br><span class="line"> <span class="attr">SRR:</span> <span class="string">"SRR25601734|SRR25601735"</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">"paired"</span></span><br><span class="line"> <span class="attr">xxxx:</span></span><br><span class="line"> <span class="attr">SRR:</span> <span class="string">"SRRxxx"</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">"single"</span></span><br></pre></td></tr></tbody></table></figure><ol start="3"><li>添加下游分析部分,例如新建 <code>scripts</code> 文件夹存放自定义的用于提取表达矩阵的脚本,并在 Pipeline 中添加新的规则以对其进行实现(别忘了 rule all 的 input 部分也要进行对应更新)。</li></ol><p>前两者的具体实现可参考 repository 下的 <a href="https://github.com/JuseTiZ/Blog-Snakemake-pipeline/tree/main/ChIP-Pipeline">ChIP Pipeline</a>。</p></body></html>]]></content>
<summary type="html">本文为使用 Snakemake 搭建 pipeline 的详细教程,其中包括一个合格 Pipeline 的基本要求、管理方式及具体搭建步骤。也涉及到版本控制和性能监测等方面的内容。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
<category term="生物信息学" scheme="https://biojuse.com/tags/%E7%94%9F%E7%89%A9%E4%BF%A1%E6%81%AF%E5%AD%A6/"/>
<category term="生信" scheme="https://biojuse.com/tags/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>对 bedgraph 文件进行 lowess 平滑操作</title>
<link href="https://biojuse.com/2024/06/29/%E5%AF%B9%20bedgraph%20%E6%96%87%E4%BB%B6%E8%BF%9B%E8%A1%8C%20lowess%20%E5%B9%B3%E6%BB%91%E6%93%8D%E4%BD%9C/"/>
<id>https://biojuse.com/2024/06/29/%E5%AF%B9%20bedgraph%20%E6%96%87%E4%BB%B6%E8%BF%9B%E8%A1%8C%20lowess%20%E5%B9%B3%E6%BB%91%E6%93%8D%E4%BD%9C/</id>
<published>2024-06-29T03:30:00.000Z</published>
<updated>2024-06-29T03:35:25.317Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>在进行一些特定的功能基因组学数据分析时,我们可能需要对 bedgraph 文件中每个 bin 的值进行一定的平滑操作,以降低随机噪声的影响并提供更好的可视化效果。例如:</p><ul><li>Repli-seq / BrdU-seq 中量化得到的 Replication Timing</li><li>OK-seq / Pu-seq 中量化得到的 Replication Fork Directionality</li></ul><p>以下是一个用于对 bedgraph 进行 lowess 平滑操作的 python script:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> statsmodels.api <span class="keyword">as</span> sm</span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_args</span>():</span><br><span class="line"></span><br><span class="line"> parser = argparse.ArgumentParser(description=<span class="string">'Perform lowess smooth on bedgraph.'</span>)</span><br><span class="line"> </span><br><span class="line"> parser.add_argument(<span class="string">'--input'</span>, <span class="string">'-i'</span>, <span class="built_in">help</span>=<span class="string">'Input bedgraph file.'</span>, required=<span class="literal">True</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'--output'</span>, <span class="string">'-o'</span>, <span class="built_in">help</span>=<span class="string">'Output smoothed bedgraph file.'</span>, required=<span class="literal">True</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'--span'</span>, <span class="built_in">help</span>=<span class="string">'Span size of loess smoothing.'</span>, <span class="built_in">type</span>=<span class="built_in">int</span>, required=<span class="literal">True</span>)</span><br><span class="line"> parser.add_argument(<span class="string">"--chr"</span>, required=<span class="literal">True</span>,</span><br><span class="line"> <span class="built_in">help</span>=<span class="string">"The chrom to input. e.g. 1-22,X,Y"</span>)</span><br><span class="line"> </span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> args</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_range</span>(<span class="params">value</span>):</span><br><span class="line"> result = []</span><br><span class="line"> <span class="keyword">for</span> part <span class="keyword">in</span> value.split(<span class="string">','</span>):</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'-'</span> <span class="keyword">in</span> part:</span><br><span class="line"> start, end = part.split(<span class="string">'-'</span>)</span><br><span class="line"> result.extend(<span class="built_in">range</span>(<span class="built_in">int</span>(start), <span class="built_in">int</span>(end) + <span class="number">1</span>))</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> result.append(<span class="built_in">int</span>(part))</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"></span><br><span class="line"> args = get_args()</span><br><span class="line"></span><br><span class="line"> data = pd.read_csv(args.<span class="built_in">input</span>, sep=<span class="string">'\t'</span>, header=<span class="literal">None</span>, names=[<span class="string">'chrom'</span>, <span class="string">'start'</span>, <span class="string">'end'</span>, <span class="string">'value'</span>])</span><br><span class="line"> data[<span class="string">'midpoint'</span>] = (data[<span class="string">'start'</span>] + data[<span class="string">'end'</span>]) / <span class="number">2</span></span><br><span class="line"></span><br><span class="line"> span_size = args.span</span><br><span class="line"> smoothed_data = []</span><br><span class="line"> chr_list = [<span class="string">f'chr<span class="subst">{i}</span>'</span> <span class="keyword">for</span> i <span class="keyword">in</span> parse_range(args.<span class="built_in">chr</span>)]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> chrom <span class="keyword">in</span> data[<span class="string">'chrom'</span>].unique():</span><br><span class="line"> chrom_data = data[data[<span class="string">'chrom'</span>] == chrom]</span><br><span class="line"> <span class="keyword">if</span> chrom <span class="keyword">not</span> <span class="keyword">in</span> chr_list:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line"> chrom_length = chrom_data[<span class="string">'end'</span>].iloc[-<span class="number">1</span>] - chrom_data[<span class="string">'start'</span>].iloc[<span class="number">0</span>]</span><br><span class="line"> span_frac = span_size / chrom_length</span><br><span class="line"></span><br><span class="line"> lowess = sm.nonparametric.lowess(chrom_data[<span class="string">'value'</span>], chrom_data[<span class="string">'midpoint'</span>], frac=span_frac)</span><br><span class="line"> smoothed_df = pd.DataFrame(lowess, columns=[<span class="string">'midpoint'</span>, <span class="string">'smoothed_value'</span>])</span><br><span class="line"> chrom_data = chrom_data.reset_index(drop=<span class="literal">True</span>)</span><br><span class="line"> chrom_data[<span class="string">'smoothed_value'</span>] = smoothed_df[<span class="string">'smoothed_value'</span>]</span><br><span class="line"> smoothed_data.append(chrom_data)</span><br><span class="line"></span><br><span class="line"> smoothed_data = pd.concat(smoothed_data)</span><br><span class="line"> smoothed_data[[<span class="string">'chrom'</span>, <span class="string">'start'</span>, <span class="string">'end'</span>, <span class="string">'smoothed_value'</span>]].to_csv(args.output, sep=<span class="string">'\t'</span>, header=<span class="literal">False</span>, index=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> main()</span><br></pre></td></tr></tbody></table></figure><p><strong>下载地址:</strong></p><p><a href="https://github.com/JuseTiZ/PyScript-for-CT/blob/main/bedgraph_lowess.py">https://github.com/JuseTiZ/PyScript-for-CT/blob/main/bedgraph_lowess.py</a></p><p><strong>依赖 module 安装:</strong></p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install pandas</span><br><span class="line">pip install statsmodels</span><br></pre></td></tr></tbody></table></figure><p><strong>参数详情:</strong></p><ul><li><code>--input</code> / <code>-i</code>,指定需要进行 lowess 平滑操作的 bedgraph 文件,该文件应当仅具有四列,且第一列染色体编号应以 <code>chr</code> 开头。</li><li><code>--output</code> / <code>-o</code>,指定输出的平滑后 bedgraph 文件,输出的新 bedgraph 中第四列为 LOWESS smooth 后的值。</li><li><code>--span</code>,指定平滑操作时使用的长度,脚本将根据每条染色体的总长确定用于平滑的数据比例。</li><li><code>--chr</code>,指定进行平滑操作的染色体编号,可使用 <code>-</code> 指定数字范围,也可使用逗号分隔,例如 <code>1-22,X,Y</code>。</li></ul><p><strong>应用示例:</strong></p><p>假设目前有一个通过 Repli-seq 计算得到的小鼠 RT(Replication Timing) bedgraph 文件 <code>RT.bedgraph</code>,通过以下命令进行 30w bp 的平滑:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python bedgraph_lowess.py -i RT.bedgraph -o RT.lowess.bedgraph --span 300000 --chr 1-19</span><br></pre></td></tr></tbody></table></figure><p>平滑前后 IGV track 示例:</p><p><img src="/pic2/bedgraphlowess.png"></p><p>请注意,平滑操作<strong>在减少噪音的同时,也损失了部分信息量</strong>,因此请根据自己当前使用的数据进行权衡,合理设置 <code>--span</code> 参数,一般情况下:</p><ul><li>数据的分辨率越高,该参数指定的值应当越低。反之亦然。</li><li>对信息精细程度的要求越高,该参数指定的值应当越低。反之亦然。</li></ul><p>例如对于某些 OK-seq 数据而言只需要 60kb 左右的 span size 即可。请根据研究需求或者数据来源文章指定恰当的值。</p></body></html>]]></content>
<summary type="html">在某些特别的 seq 数据处理中,我们可能要对得到的 bedgraph 文件进行平滑以消除噪音影响。本文介绍了一种基于 python 的平滑实现方法。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="杂项" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E6%9D%82%E9%A1%B9/"/>
</entry>
<entry>
<title>获取基因组上 intron 区域及对应链信息的方法</title>
<link href="https://biojuse.com/2024/06/09/%E8%8E%B7%E5%8F%96%E5%9F%BA%E5%9B%A0%E7%BB%84%E4%B8%8A%20intron%20%E5%8C%BA%E5%9F%9F%E5%8F%8A%E5%AF%B9%E5%BA%94%E9%93%BE%E4%BF%A1%E6%81%AF%E7%9A%84%E6%96%B9%E6%B3%95/"/>
<id>https://biojuse.com/2024/06/09/%E8%8E%B7%E5%8F%96%E5%9F%BA%E5%9B%A0%E7%BB%84%E4%B8%8A%20intron%20%E5%8C%BA%E5%9F%9F%E5%8F%8A%E5%AF%B9%E5%BA%94%E9%93%BE%E4%BF%A1%E6%81%AF%E7%9A%84%E6%96%B9%E6%B3%95/</id>
<published>2024-06-09T12:30:00.000Z</published>
<updated>2024-06-09T12:46:13.128Z</updated>
<content type="html"><![CDATA[<html><head></head><body><h2 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h2><p>前置条件:</p><ul><li>在环境变量中可调用的 <code>bedtools</code></li></ul><p>本文参考:</p><blockquote><p><strong>Get intronic and intergenic sequences based on gff file</strong> from Biostars</p><p><a href="https://www.biostars.org/p/112251/">https://www.biostars.org/p/112251/</a></p></blockquote><p>本文可满足的需求:</p><ul><li>得到特定区域的 bed 文件(例如 exon / intron 等)。</li><li>在得到区域信息的同时进行链信息的区分。</li></ul><h3 id="下载基因组注释文件(gtf)"><a href="#下载基因组注释文件(gtf)" class="headerlink" title="下载基因组注释文件(gtf)"></a>下载基因组注释文件(gtf)</h3><p>以人类最新版本的 gencode 注释为例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://ftp.ebi.ac.uk/pub/databases/gencode/Gencode_human/release_46/gencode.v46.basic.annotation.gtf.gz</span><br></pre></td></tr></tbody></table></figure><p>也可以选择已有的 gtf 文件进行。</p><h3 id="得到-transcript-和-exon-区域信息"><a href="#得到-transcript-和-exon-区域信息" class="headerlink" title="得到 transcript 和 exon 区域信息"></a>得到 transcript 和 exon 区域信息</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">GTF="/path/to/gencode.v46.basic.annotation.gtf.gz" # 改为自己实际的注释文件路径,建议使用压缩格式以兼容以下命令</span><br><span class="line">BASENAME=$(basename "$GTF" .gtf.gz)</span><br><span class="line"></span><br><span class="line">TRANSCRIPT_BED="${BASENAME}.transcript.bed"</span><br><span class="line">FORWARD_TRANS_BED="${BASENAME}.transcript.fors.bed"</span><br><span class="line">BACKWARD_TRANS_BED="${BASENAME}.transcript.bacs.bed"</span><br><span class="line">DOUBLE_TRANS_BED="${BASENAME}.transcript.doubletrans.bed"</span><br><span class="line">EXON_BED="${BASENAME}.exon.bed"</span><br><span class="line">INTRON_BED="${BASENAME}.intron.bed"</span><br><span class="line">CDS_BED="${BASENAME}.cds.bed"</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">得到 transcript 区域</span></span><br><span class="line">awk '$3=="transcript" {print $1, $4-1, $5, $7}' OFS='\t' <(zcat $GTF) > $TRANSCRIPT_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">得到 exon 区域</span></span><br><span class="line">awk '$3=="exon" {print $1, $4-1, $5, $7}' OFS='\t' <(zcat $GTF) > $EXON_BED</span><br></pre></td></tr></tbody></table></figure><p>以上命令将得到 <code>.transcript.bed</code> 和 <code>.exon.bed</code> 结尾的文件,里面包含基因组上所有 transcript 和 exon 的信息。</p><h3 id="不关注链信息时"><a href="#不关注链信息时" class="headerlink" title="不关注链信息时"></a>不关注链信息时</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">合并排序 transcript 区域</span></span><br><span class="line">bedtools sort -i $TRANSCRIPT_BED | bedtools merge -i - > $TRANSCRIPT_BED.tmp </span><br><span class="line">mv $TRANSCRIPT_BED.tmp $TRANSCRIPT_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">合并排序 exon 区域</span></span><br><span class="line">bedtools sort -i $EXON_BED | bedtools merge -i - > $EXON_BED.tmp </span><br><span class="line">mv $EXON_BED.tmp $EXON_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 transcript 中排除 exon 得到 intron</span></span><br><span class="line">bedtools subtract -a $TRANSCRIPT_BED -b $EXON_BED > $INTRON_BED</span><br></pre></td></tr></tbody></table></figure><h3 id="关注链信息时"><a href="#关注链信息时" class="headerlink" title="关注链信息时"></a>关注链信息时</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">得到正负链信息</span></span><br><span class="line">awk '$4=="+"' $TRANSCRIPT_BED | bedtools sort -i - | bedtools merge -i - | awk 'OFS="\t"{print $0, "+"}' > $FORWARD_TRANS_BED</span><br><span class="line">awk '$4=="-"' $TRANSCRIPT_BED | bedtools sort -i - | bedtools merge -i - | awk 'OFS="\t"{print $0, "-"}' > $BACKWARD_TRANS_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">找出冲突区域(正负链上都存在 transcript)</span></span><br><span class="line">bedtools intersect -a $FORWARD_TRANS_BED -b $BACKWARD_TRANS_BED > $DOUBLE_TRANS_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">排除 transcript 冲突区域</span></span><br><span class="line">bedtools subtract -a <(cat $FORWARD_TRANS_BED $BACKWARD_TRANS_BED | bedtools sort -i -) -b $DOUBLE_TRANS_BED > $TRANSCRIPT_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">排除 exon 冲突区域</span></span><br><span class="line">awk '$3=="exon" {print $1, $4-1, $5, $7}' OFS='\t' <(zcat $GTF) | bedtools sort -i - | bedtools merge -i - > $EXON_BED</span><br><span class="line">bedtools intersect -a $TRANSCRIPT_BED -b $EXON_BED > $EXON_BED.tmp</span><br><span class="line">mv $EXON_BED.tmp $EXON_BED</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">得到包含链信息的 intron</span></span><br><span class="line">bedtools subtract -a $TRANSCRIPT_BED -b $EXON_BED > $INTRON_BED</span><br></pre></td></tr></tbody></table></figure><h3 id="相关的用途"><a href="#相关的用途" class="headerlink" title="相关的用途"></a>相关的用途</h3><p>通过上述命令,同理也可以获得例如 CDS 或 UTR 的 bed 文件。下游分析中,我们可以通过这些 bed 文件筛选在特定区域中的突变进行相关探索:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bedtools intersect -a input.vcf -b CDS.bed > input.cds.vcf</span><br></pre></td></tr></tbody></table></figure><p>将 <code>input.vcf</code> 和 <code>CDS.bed</code> 请换为实际路径即可得到所有在 CDS 区域中的 variants。</p></body></html>]]></content>
<summary type="html">通过基因组注释文件得到基因组 intron 区域的 bed 文件,同时标注链信息。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
<category term="生物信息学" scheme="https://biojuse.com/tags/%E7%94%9F%E7%89%A9%E4%BF%A1%E6%81%AF%E5%AD%A6/"/>
<category term="生信" scheme="https://biojuse.com/tags/%E7%94%9F%E4%BF%A1/"/>
<category term="基因组" scheme="https://biojuse.com/tags/%E5%9F%BA%E5%9B%A0%E7%BB%84/"/>
</entry>
<entry>
<title>Ensembl VEP plugins 的使用方法(Alphamissense、dbNSFP 等)</title>
<link href="https://biojuse.com/2024/06/09/Ensembl%20VEP%20plugins%20%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%EF%BC%88Alphamissense%E3%80%81dbNSFP%20%E7%AD%89%EF%BC%89/"/>
<id>https://biojuse.com/2024/06/09/Ensembl%20VEP%20plugins%20%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%EF%BC%88Alphamissense%E3%80%81dbNSFP%20%E7%AD%89%EF%BC%89/</id>
<published>2024-06-09T11:30:00.000Z</published>
<updated>2025-04-02T01:50:31.978Z</updated>
<content type="html"><![CDATA[<html><head></head><body><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>以下是相关的官网链接,如果想要得到除本文内容以外更全面深入的了解,建议跳转相关链接进行查阅:</p><blockquote><p>VEP plugins documentation:</p><p><a href="https://grch37.ensembl.org/info/docs/tools/vep/script/vep_plugins.html">https://grch37.ensembl.org/info/docs/tools/vep/script/vep_plugins.html</a></p><p>VEP install documentation:</p><p><a href="https://grch37.ensembl.org/info/docs/tools/vep/script/vep_download.html">https://grch37.ensembl.org/info/docs/tools/vep/script/vep_download.html</a></p></blockquote><h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><h3 id="安装-VEP"><a href="#安装-VEP" class="headerlink" title="安装 VEP"></a>安装 VEP</h3><p>此处强烈<strong>建议使用 Docker 或者 Singularity 直接拉取 VEP 镜像进行分析,因为在 VEP image 中已经内置好所有的 plugin</strong>,无需自己手动下载。</p><p>考虑到 Docker 对权限的要求更高,因此这里以普适性更强的 Singularity 为例(Docker 用户有需求可通过文章开头链接前往官网进行参考):</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拉取 VEP image</span></span><br><span class="line">singularity pull --name vep.sif docker://ensemblorg/ensembl-vep</span><br></pre></td></tr></tbody></table></figure><p>之后就可以通过 <code>singularity</code> 运行 VEP:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">singularity exec vep.sif vep --help</span><br></pre></td></tr></tbody></table></figure><p>直接安装的方法:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/Ensembl/ensembl-vep.git</span><br><span class="line">cd ensembl-vep</span><br><span class="line">perl INSTALL.pl</span><br></pre></td></tr></tbody></table></figure><p><code>INSTALL.pl</code> 可以通过指定一系列参数来进行自定义安装,这里介绍其中主要需要注意的几个:</p><ul><li><code>--CACHE_VERSION</code> 选择特定的 Ensembl 版本,目前最新为 112,请根据自己正在使用的注释版本确定。</li><li><code>--CACHEDIR</code> 下载数据库的存储路径,默认为 <code>$HOME/.vep</code>,可自行修改。</li><li><code>--PLUGINS</code> 指定下载的插件(plugins),通过逗号分隔。也可通过 <code>--PLUGINS all</code> 使其下载全部插件。</li></ul><p>由于 <code>INSTALL.pl</code> 需要一定的依赖项,因此遇到安装报错时可前往其 <a href="https://grch37.ensembl.org/info/docs/tools/vep/script/vep_download.html">documentation</a> 查看是否存在依赖项缺失,此外 API 和数据库版本不同时可能存在兼容问题。但目前我使用最新版本的 VEP 进行旧版本数据库的注释是不存在任何问题的。</p><h3 id="下载数据库"><a href="#下载数据库" class="headerlink" title="下载数据库"></a>下载数据库</h3><p>通过 <code>INSTALL.pl</code> 安装的另一个弊端就是可能下载速度非常慢,因此这里介绍一个自行安装数据库的方法,对于使用 <code>singularity</code> 或者已经配置好 <code>vep</code> 的情况,可以使用以下命令:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mkdir vep_data</span><br><span class="line">cd vep_data</span><br><span class="line">wget -c ftp://ftp.ensembl.org/pub/release-112/variation/indexed_vep_cache/homo_sapiens_vep_112_GRCh38.tar.gz</span><br><span class="line">tar zxf homo_sapiens_vep_112_GRCh38.tar.gz</span><br></pre></td></tr></tbody></table></figure><p>使用其他版本时,可将上述网址中的 <code>112</code> 替换为其他版本号例如 <code>110</code> 等。</p><p>之后在运行 <code>vep</code> 时可以通过 <code>--cache_version</code> 指定需要使用的版本号。</p><h3 id="下载-plugins-需要的文件"><a href="#下载-plugins-需要的文件" class="headerlink" title="下载 plugins 需要的文件"></a>下载 plugins 需要的文件</h3><p>这里仅介绍 AlphaMissense 和 dbNSFP 两个插件的数据库构建方法,原因如下:</p><ul><li>AlphaMissense 对于非同义突变的影响预测具有目前最先进的性能。但由于其开发团队仅提供了 GENCODE V33(对应 Ensembl V98),因此很多更新版本注释中的突变可能没法找到其对应的 AlphaMissense score。且其 github 上仅提供模型架构而未提供训练后的权重信息,因此无法自行预测。但其结果依然是极具参考价值的。</li><li>dbNSFP 是一个<strong>集成数据库</strong>,里面包含了众多 Variant Effect Predictor 的结果,包括但不限于 CADD、LINSIGHT、ESM1b、EVE、AlphaMissense 等各种 score,安装了该插件的效果等同于安装许多其他插件,从效率上讲极具价值。如果选择安装该数据库则可以考虑跳过 AlphaMissense,注意该数据库文件较大请注意剩余存储。</li></ul><img src="/pic2/dbNSFP.png" style="zoom:80%;"><p>一些需要注意的事项:</p><ul><li>插件下载好不代表可以直接使用,因为它需要基于对应数据库才能运行。</li><li>AlphaMissense 是 vep 112 版本新发布的插件,不清楚旧版本 API 是否与其兼容。</li><li>dbNSFP 中<strong>仅包含非同义突变的注释信息</strong>,因此其仅会对那些存在 missense_variant 的位点进行注释。如果你还需要统计同义突变或者内含子突变的 score(例如 CADD 等软件包含其他类型突变的影响),那么使用 dbNSFP 可能不是最好的选择。</li><li>dbNSFP 本身使用的注释版本可能与 VEP 具有冲突,这可能导致相关的预测结果在不同版本间存在冲突,详见 <a href="https://github.com/Ensembl/VEP_plugins/issues/626">issue#626</a>。</li><li>建议将所有数据库下载到同一目录下的不同子目录中,方便进行管理和可能需要的路径绑定。</li></ul><h4 id="AlphaMissense-安装"><a href="#AlphaMissense-安装" class="headerlink" title="AlphaMissense 安装"></a>AlphaMissense 安装</h4><p>更多细节见:<a href="https://grch37.ensembl.org/info/docs/tools/vep/script/vep_plugins.html#alphamissense">https://grch37.ensembl.org/info/docs/tools/vep/script/vep_plugins.html#alphamissense</a></p><p>请自行选择安装该数据库的路径 <code>[PATH]</code>,然后运行以下命令:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cd [PATH]</span><br><span class="line">gsutil -m cp \</span><br><span class="line"> "gs://dm_alphamissense/AlphaMissense_hg19.tsv.gz" \</span><br><span class="line"> "gs://dm_alphamissense/AlphaMissense_hg38.tsv.gz" \</span><br><span class="line"> .</span><br></pre></td></tr></tbody></table></figure><p>此后对数据库进行构建,以使用 hg38 版本为例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tabix -s 1 -b 2 -e 2 -f -S 1 AlphaMissense_hg38.tsv.gz</span><br></pre></td></tr></tbody></table></figure><p>建立好索引后,在运行 VEP 时可以通过以下命令进行 Alphamissense score 注释:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">singularity exec /path/to/vep.sif -i variations.vcf --plugin AlphaMissense,file=/full/path/to/AlphaMissense_hg38.tsv.gz</span><br></pre></td></tr></tbody></table></figure><p>将 <code>vep.sif</code> 和 <code>AlphaMissense_hg38.tsv.gz</code> 的路径替换为自己的路径(详细运行示例可见下文)。</p><h4 id="dbNSFP-安装"><a href="#dbNSFP-安装" class="headerlink" title="dbNSFP 安装"></a>dbNSFP 安装</h4><p>更多细节见:<a href="https://grch37.ensembl.org/info/docs/tools/vep/script/vep_plugins.html#dbnsfp">https://grch37.ensembl.org/info/docs/tools/vep/script/vep_plugins.html#dbnsfp</a></p><p>dbNSFP 每个版本分为 <code>a</code> <code>c</code> 两个类型,其中 <code>a</code> 适用于 <code>academic use</code>,<code>c</code> 适用于 <code>commercial use</code>。后者中不包含以下 effect score:</p><blockquote><p>Polyphen2, VEST, REVEL, ClinPred, CADD, LINSIGHT, GenoCanyon</p></blockquote><p>以下部分以 <code>a</code> 类型为例,该文章编写时 dbNSFP 的最新版本为 v4.8,若有变动请见其 <a href="https://sites.google.com/site/jpopgen/dbNSFP">documentation</a> 页面。</p><p>请自行选择安装该数据库的路径 <code>[PATH]</code>,然后运行以下命令:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd [PATH]</span><br><span class="line">wget https://dbnsfp.s3.amazonaws.com/dbNSFP4.8a.zip</span><br></pre></td></tr></tbody></table></figure><p>下载后,通过以下命令进行数据库的构建:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">version=4.8a</span><br><span class="line">unzip dbNSFP${version}.zip</span><br><span class="line">zcat dbNSFP${version}_variant.chr1.gz | head -n1 > h</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">For hg38</span></span><br><span class="line">zgrep -h -v ^#chr dbNSFP${version}_variant.chr* | sort -k1,1 -k2,2n - | cat h - | bgzip -c > dbNSFP${version}_grch38.gz</span><br><span class="line">tabix -s 1 -b 2 -e 2 dbNSFP${version}_grch38.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">For hg19</span></span><br><span class="line">zgrep -h -v ^#chr dbNSFP${version}_variant.chr* | awk '$8 != "." ' | sort -k8,8 -k9,9n - | cat h - | bgzip -c > dbNSFP${version}_grch37.gz</span><br><span class="line">tabix -s 8 -b 9 -e 9 dbNSFP${version}_grch37.gz</span><br></pre></td></tr></tbody></table></figure><p>通过以下命令查看 dbNSFP 可以进行哪些 score 的注释:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat h | tr '\t' '\n'</span><br></pre></td></tr></tbody></table></figure><p>以上命令会打印出所有可以进行注释的列,通过在参数中指定这些列进行相应的注释,以 AlphaMissense 为例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">cat</span> h | <span class="built_in">tr</span> <span class="string">'\t'</span> <span class="string">'\n'</span> | grep AlphaMissense</span></span><br><span class="line">AlphaMissense_score</span><br><span class="line">AlphaMissense_rankscore</span><br><span class="line">AlphaMissense_pred</span><br></pre></td></tr></tbody></table></figure><p>在运行 VEP 时可以通过以下命令进行 Alphamissense score 注释:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">singularity exec /path/to/vep.sif -i variations.vcf --plugin dbNSFP,file=/path/to/dbNSFP${version}_grch38.gz,AlphaMissense_score</span><br></pre></td></tr></tbody></table></figure><p>如果要进行其他注释,则将 <code>AlphaMissense_score</code> 替换为对应的列名(即文件 <code>h</code> 中包含的那些名称)。如果要使用所有列,则指定 <code>ALL</code> 即可。</p><p>将 <code>vep.sif</code> 和 <code>dbNSFP${version}_grch38.gz</code> 的路径替换为自己的路径。</p><h3 id="进行注释"><a href="#进行注释" class="headerlink" title="进行注释"></a>进行注释</h3><p>以下是一个 dbNSFP 注释的示例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">singularity exec -B /path/to/database:/path/to/database /path/to/vep.sif \</span><br><span class="line"> vep --dir /path/to/vep_data \</span><br><span class="line"> --cache --cache_version [version] --offline --format vcf --vcf --force_overwrite --assembly GRCh38 \</span><br><span class="line"> --input_file [input vcf] \</span><br><span class="line"> --output_file [output vcf] \</span><br><span class="line"> --plugin dbNSFP,/path/to/database/dbNSFP${version}_grch38.gz,AlphaMissense_score,CADD_raw,phyloP100way_vertebrate</span><br></pre></td></tr></tbody></table></figure><p>注意事项:</p><ul><li><code>-B</code>:该参数用于将目录挂载到 singularity 中,不是必选项。但是如果在运行中,各个插件文件的<u>路径指定正确却依然返回找不到文件</u>时,则需要通过 -B 将插件文件的目录挂载到 singularity 容器中进行访问。比如如果 dbNSFP 的目录在 <code>/database/dbNSFP</code> 中,则可以通过 <code>-B /database:/database</code> 将其目录挂载到容器的相同位置上,从而进行访问。</li><li>将 <code>/path/to/vep.sif</code> / <code>/path/to/vep_data</code> / <code>[input vcf]</code> / <code>[output vcf]</code> 更改为自己的实际路径。</li><li><code>[version]</code> 指定下载的注释版本,如 <code>112</code>。</li><li>dbNSFP 插件中的文件路径改为自己的实际路径。</li><li>要指定更多 dbNSFP 的列,只需要通过逗号作为分隔符添加即可。</li></ul><p>或者你仅想进行 AlphaMissense 的注释:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">singularity exec -B /path/to/database:/path/to/database /path/to/vep.sif \</span><br><span class="line"> vep --dir /path/to/vep_data \</span><br><span class="line"> --cache --cache_version [version] --offline --format vcf --vcf --force_overwrite --assembly GRCh38 \</span><br><span class="line"> --input_file [input vcf] \</span><br><span class="line"> --output_file [output vcf] \</span><br><span class="line"> --plugin AlphaMissense,file=/path/to/database/AlphaMissense_hg38.tsv.gz</span><br></pre></td></tr></tbody></table></figure><p>请根据自己的实际需求选择 <code>hg38</code> 或 <code>hg19</code> 版本。</p><h3 id="多进程并行"><a href="#多进程并行" class="headerlink" title="多进程并行"></a>多进程并行</h3><p>不难察觉到,VEP 的运行速度是非常慢的,如果想对基因组的所有 variants 进行注释可能需要花费大量的时间,因此可以通过以下几种方法降低所需时间:</p><ol><li>仅选择特定区域的 variants,例如只取出那些落在 CDS 区域的 variants 等。</li><li>通过多进程进行并行注释。</li></ol><p>关于如何选择仅在 CDS 区域的 variants 可见博客另一篇文章,这里仅提如何实现多进程并行。</p><p>首先,假设所有 variants 都在同一个文件里,那么一个简单的方法是将其拆分为不同染色体的 variants,然后对每一条染色体的 variants 进行并行注释:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">vcffile="example.vcf" # 请将该处的 vcf 替换为真实的 vcf 文件名称</span><br><span class="line">outname=$(basename "$vcffile" .vcf)</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">保存 vcf header</span></span><br><span class="line">grep '^#' "$vcffile" > header.vcf</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">输出每条染色体的 vcf</span></span><br><span class="line">awk -v outname="$outname" 'BEGIN {OFS="\t"} !/^#/ {print > outname"."$1".vcf.tmp"}' "$vcffile"</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">整合 header 和 vcf</span></span><br><span class="line">mkdir -p subvcf</span><br><span class="line">for i in *.vcf.tmp;</span><br><span class="line">do</span><br><span class="line"> cat header.vcf "$i" > ./subvcf/"$(basename "$i" .tmp)"</span><br><span class="line"> rm "$i"</span><br><span class="line">done</span><br></pre></td></tr></tbody></table></figure><p>拆分步骤如上所示,并行请根据实际情况决定方案,例如通过 slurm 调度系统或者 parallel 命令等实现。此处以 parallel 为例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ls ./subvcf/*.vcf | parallel -j [num] \</span><br><span class="line">'singularity exec -B /path/to/database:/path/to/database /path/to/vep.sif \</span><br><span class="line"> vep --dir /path/to/vep_data \</span><br><span class="line"> --cache --cache_version [version] --offline --format vcf --vcf --force_overwrite --assembly GRCh38 \</span><br><span class="line"> --input_file {} \</span><br><span class="line"> --output_file {.}.anno.vcf \</span><br><span class="line"> --plugin AlphaMissense,file=/path/to/database/AlphaMissense_hg38.tsv.gz'</span><br></pre></td></tr></tbody></table></figure><p>请将 <code>-j</code> 后的 <code>[num]</code> 替换为希望的并行作业数,最终的注释 vcf 文件将在 <code>subvcf</code> 中以 <code>.anno.vcf</code> 结尾。</p><img src="/pic2/vep_example1.png" style="zoom:80%;"><p>以上方案依然存在两个问题:</p><p>①、不同染色体上的 variants 数量差异很大。</p><p>②、这样仅能做到最高同时并行 <code>染色体数</code> 个任务。</p><p>因此,也可以通过将文件拆分为 variants 数量相等的若干个文件进行并行,以下是一个示例:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">vcffile="example.vcf"</span><br><span class="line">outname=$(basename "$vcffile" .vcf)</span><br><span class="line">num_splits=10 # 希望的拆分数量</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">保存 vcf header</span></span><br><span class="line">grep '^#' "$vcffile" > header.vcf</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">计算突变数量</span></span><br><span class="line">total_variants=$(grep -v '^#' "$vcffile" | wc -l)</span><br><span class="line">variants_per_file=$(( (total_variants + num_splits - 1) / num_splits ))</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">输出子 vcf 文件</span></span><br><span class="line">mkdir -p subvcf</span><br><span class="line">split_count=1</span><br><span class="line">variant_count=0</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">使用 awk 按突变数量进行拆分(By GPT4.0)</span></span><br><span class="line">awk -v header="header.vcf" -v outname="$outname" -v variants_per_file="$variants_per_file" -v split_count="$split_count" -v variant_count="$variant_count" '</span><br><span class="line">BEGIN {</span><br><span class="line"> while ((getline < header) > 0) {</span><br><span class="line"> header_lines[++header_line_count] = $0</span><br><span class="line"> }</span><br><span class="line"> close(header)</span><br><span class="line">}</span><br><span class="line">!/^#/ {</span><br><span class="line"> if (variant_count % variants_per_file == 0) {</span><br><span class="line"> if (split_count > 1) close(output_file)</span><br><span class="line"> output_file = sprintf("./subvcf/%s.split%d.vcf", outname, split_count)</span><br><span class="line"> for (i = 1; i <= header_line_count; i++) {</span><br><span class="line"> print header_lines[i] > output_file</span><br><span class="line"> }</span><br><span class="line"> split_count++</span><br><span class="line"> }</span><br><span class="line"> print >> output_file</span><br><span class="line"> variant_count++</span><br><span class="line">}</span><br><span class="line">' "$vcffile"</span><br></pre></td></tr></tbody></table></figure><p>以上命令将把文件分为拆分为 10 个子 vcf 文件,并行的方法同之前所述。如果需要拆分为更多的子文件以设置更高的并行数量,仅需调整 <code>num_splits</code> 即可。</p><img src="/pic2/vep_example2.png" style="zoom:80%;"><h3 id="注释结果解释"><a href="#注释结果解释" class="headerlink" title="注释结果解释"></a>注释结果解释</h3><p>注释后,header 中会出现相应的说明字段,以 AlphaMissence 注释为例:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">##INFO=<ID=CSQ,Number=.,Type=String,Description="Consequence annotations from Ensembl VEP. Format: Allele|Consequence|IMPACT|SYMBOL|Gene|Feature_type|Feature|BIOTYPE|EXON|INTRON|HGVSc|HGVSp|cDNA_position|CDS_position|Protein_position|Amino_acids|Codons|Existing_variation|DISTANCE|STRAND|FLAGS|SYMBOL_SOURCE|HGNC_ID|am_class|am_pathogenicity"></span><br><span class="line">##am_class=The AlphaMissense thresholds are: 'Likely benign' if score < 0.34, 'Likely pathogenic' if score > 0.564, 'ambiguous' otherwise -- see doi.org/10.1126/science.adg7492 for details; column from /data/alphamissense/AlphaMissense_hg38.tsv.gz</span><br><span class="line">##am_pathogenicity=Continuous AlphaMissense score between 0 and 1 which can be interpreted as the predicted probability of the variant being pathogenic; column from /data/alphamissense/AlphaMissense_hg38.tsv.gz</span><br></pre></td></tr></tbody></table></figure><p>这里 <code>INFO</code> 中多出的 <code>CSQ</code> 为 Ensembl VEP 的注释结果,其中以 <code>|</code> 分隔所有注释信息,相应位置上对应的注释说明可见 header 说明。此外:</p><ul><li>一个 variant 可能落在多个转录本中,因此对应的 <code>CSQ</code> 会出现多条结果(以 <code>,</code> 分隔)。</li><li>也有可能该 variant 并不在某个 score 的注释区域内(例如 AlphaMissense 仅注释非同义突变),此时对应位置将为空。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>之前在 <a href="https://www.nature.com/articles/s41588-023-01465-0">esm1b</a> 的文章里看到他们用的就是 dbNSFP 来评估各个 VEP method 的表现,想一想先前我还傻楞地去一个一个下载,不禁感慨世界上有很多节省时间的方式,只是需要多花些时间、多长点见识才能了解到。</p><p>也希望这篇文章能帮其他人少走点弯路。</p></body></html>]]></content>
<summary type="html">本文将介绍如何使用 VEP 的各个插件进行全面的 variant effect 注释,同时也涉及到数据下载、相关版本注意事项和多进程并行方法等。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
<category term="生物信息学" scheme="https://biojuse.com/tags/%E7%94%9F%E7%89%A9%E4%BF%A1%E6%81%AF%E5%AD%A6/"/>
<category term="生信" scheme="https://biojuse.com/tags/%E7%94%9F%E4%BF%A1/"/>
</entry>
<entry>
<title>fasterq-dump 下载 SRA 文件时报错的解决方法(err cmn_iter)</title>
<link href="https://biojuse.com/2024/05/10/fasterq-dump%20%E4%B8%8B%E8%BD%BD%20SRA%20%E6%96%87%E4%BB%B6%E6%97%B6%E6%8A%A5%E9%94%99%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%EF%BC%88err%20cmn_iter%EF%BC%89/"/>
<id>https://biojuse.com/2024/05/10/fasterq-dump%20%E4%B8%8B%E8%BD%BD%20SRA%20%E6%96%87%E4%BB%B6%E6%97%B6%E6%8A%A5%E9%94%99%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%EF%BC%88err%20cmn_iter%EF%BC%89/</id>
<published>2024-05-10T03:00:00.000Z</published>
<updated>2024-06-27T07:35:12.744Z</updated>
<content type="html"><![CDATA[<html><head></head><body><h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p>这几天下载 SRA,遇到的错误有:</p><ul><li><p><code>fasterq-dump.3.1.0 err: cmn_iter.c cmn_read_uint8_array</code></p></li><li><p><code>fasterq-dump.3.1.0 err: cmn_iter.c cmn_read_String</code></p></li></ul><p>以前使用 <code>fasterq-dump</code> 时只是偶然出现这些问题,重新下载也都能解决,但最近有些 SRA 一直下载失败,经过调查发现这种错误在大文件下载时出现异常频繁,且多次下载并没法有效解决问题,因此需要一个替代的方法。</p><h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><h4 id="Prefetch"><a href="#Prefetch" class="headerlink" title="Prefetch"></a>Prefetch</h4><p>由于 <code>fasterq-dump</code> 直接通过 HTTP 下载得到 fastq 文件,该过程很可能由于一些问题中断从而导致下载失败。因此可以通过更稳定的 <code>prefetch</code> 先得到 sra 文件,再通过 <code>fasterq-dump</code> 提取 fastq 文件。</p><blockquote><p>fasterq-dump fetches SRR on the fly via HTTP and there could be fatal errors during the transfer.<br>prefetch eliminates transfer problems.</p></blockquote><p>具体操作:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">prefetch --max-size 100000000 [SRR <span class="built_in">id</span>]</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">fasterq-dump [SRR <span class="built_in">id</span>]/[SRR <span class="built_in">id</span>].sra --progress --details --split-files -v --threads [number of threads]</span></span><br></pre></td></tr></tbody></table></figure><p>使用 <code>prefetch</code> 下载的好处有两点:</p><ol><li>sra 文件占用的空间较小,因此下载速度更快。</li><li>下载如果因为某种原因中断,仍可以通过相同的命令进行断点重连。</li></ol><p>以下是一个使用 slurm 调度系统进行批量下载和读取的示例,没有调度系统的朋友也可以直接参考命令进行多下载任务并行:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment">#SBATCH -J download</span></span><br><span class="line"><span class="comment">#SBATCH -o download.out</span></span><br><span class="line"><span class="comment">#SBATCH -e download.err</span></span><br><span class="line"><span class="comment">#SBATCH -N 1</span></span><br><span class="line"><span class="comment">#SBATCH -n 4</span></span><br><span class="line"></span><br><span class="line">output_dir=<span class="string">"./rawfastq"</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="variable">$output_dir</span></span><br><span class="line"><span class="built_in">mkdir</span> -p tmp</span><br><span class="line"></span><br><span class="line"><span class="comment"># Download</span></span><br><span class="line"><span class="built_in">cat</span> download.list | parallel -j 4 --tmpdir ./tmp <span class="string">"prefetch --max-size 100000000 --progress --output-directory <span class="variable">$output_dir</span> {} ><span class="variable">$output_dir</span>/prefetch_{}.log 2>&1"</span></span><br></pre></td></tr></tbody></table></figure><p>其中 <code>download.list</code> 为一行一个 SRR id 的文件。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment">#SBATCH -J extract</span></span><br><span class="line"><span class="comment">#SBATCH -o extract.out</span></span><br><span class="line"><span class="comment">#SBATCH -e extract.err</span></span><br><span class="line"><span class="comment">#SBATCH -N 1</span></span><br><span class="line"><span class="comment">#SBATCH -n 16</span></span><br><span class="line"></span><br><span class="line">output_dir=<span class="string">"./rawfastq"</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="variable">$output_dir</span></span><br><span class="line"><span class="built_in">mkdir</span> -p tmp</span><br><span class="line"></span><br><span class="line"><span class="comment"># Extract</span></span><br><span class="line"><span class="built_in">cat</span> download.list | parallel -j 4 --tmpdir ./tmp <span class="string">"fasterq-dump <span class="variable">$output_dir</span>/{}/{}.sra* --progress --details --split-files -v --outdir <span class="variable">$output_dir</span> --threads 4 ><span class="variable">$output_dir</span>/fasterq-dump_{}.log 2>&1"</span></span><br><span class="line"><span class="comment"># gzip Compress</span></span><br><span class="line"><span class="built_in">ls</span> <span class="variable">$output_dir</span>/*fastq | parallel -j 16 gzip {}</span><br></pre></td></tr></tbody></table></figure><p>这里使用 <code>.sra*</code> 作为后缀的原因是有时下载的 sra 文件其尾缀可能为 <code>.sralite</code>,具体差别可见 <a href="https://www.ncbi.nlm.nih.gov/sra/docs/sra-data-formats/">SRA Data Formats</a>。</p><p>若想加速提取过程,可以根据自身情况调整并行数量和 <code>fasterq-dump</code> 使用的线程数量。以上方法<strong>经实测非常稳定,对于大文件而言也不会出现报错</strong>。</p><h4 id="ascp"><a href="#ascp" class="headerlink" title="ascp"></a>ascp</h4><p>有时 <code>prefetch</code> 下载速度极缓慢,因此选择速度更快很多的 <code>ascp</code> 也是很好的替代方案。</p><p>conda 下载:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">有 mamba 则用 mamba</span></span><br><span class="line">conda install -c hcc aspera-cli -y</span><br></pre></td></tr></tbody></table></figure><p>在 <a href="https://www.ebi.ac.uk/ena/browser/home">ENA Browser</a> 搜索对应的 Accession Number,</p><img src="/pic2/ENAbroser.png" style="zoom:80%;"><p>勾选 <code>fastq_aspera</code> 后下载 TSV:</p><img src="/pic2/ENAbroser2.png" style="zoom:50%;"><p>根据 <code>fastq_ftp</code> 列,制成以下类似文件:</p><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">/vol1/fastq/ERR418/003/ERR4181783/ERR4181783_1.fastq.gz</span><br><span class="line">/vol1/fastq/ERR418/003/ERR4181783/ERR4181783_2.fastq.gz</span><br></pre></td></tr></tbody></table></figure><p>假设其命名为 <code>download.list</code>,使用以下命令下载:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ascp -QT -k 1 -l 100m -P33001 -i /path/to/asperaweb_id_dsa.openssh --mode recv --user era-fasp --host fasp.sra.ebi.ac.uk --file-list ./download.list [output path]</span><br></pre></td></tr></tbody></table></figure><p>参数详解:</p><ul><li><code>-Q</code>:启用较少详细信息的输出模式(quiet mode)。</li><li><code>-T</code>:启用文件时间戳保留。文件传输完成后,目标文件的时间戳将与源文件相同。</li><li><code>-k 1</code>:断点续传。</li><li><code>-l 100m</code>:限制传输速率,最大传输速率为 100 Mbps。</li><li><code>-P 33001</code>:用于连接的端口号,<code>33001</code> 是 Aspera 使用的默认端口号。</li><li><code>-i /path/to/asperaweb_id_dsa.openssh</code>:指定私钥文件的路径,用于身份验证。位于<strong>安装 ascp 的环境</strong>目录中的 <strong>etc</strong> 下。</li></ul><img src="/pic2/ENAbroser3.png" style="zoom:80%;"><ul><li><code>--mode recv</code>:指定传输模式。<code>recv</code> 表示接收文件(下载)。</li><li><code>--user era-fasp</code>:指定连接使用的用户名。</li><li><code>--host fasp.sra.ebi.ac.uk</code>:指定连接的主机名或 IP 地址。</li><li><code>--file-list ./download.list</code>:指定包含待传输文件列表的文件。</li><li><code>[output path]</code>:存储下载文件的路径,改为自己的实际路径。</li></ul><p>不过需要注意,<code>ascp</code> 下载的数据有时会出现问题,进而导致下游分析如 <code>trim_galore</code> 在过滤 reads 时出错,以下是一些报错示例:</p><blockquote><p>cutadapt: error: Line xxx in FASTQ file is expected to start with 'xxx', but found 'xxx'</p></blockquote><p>可以通过 <code>gunzip</code> 命令检查是否是由于 fastq 文件存在问题:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">gunzip -t SRRxxx.fastq.gz</span></span><br><span class="line"></span><br><span class="line">gzip: SRRxxx.fastq.gz: invalid compressed data--crc error</span><br><span class="line"></span><br><span class="line">gzip: SRRxxx.fastq.gz: invalid compressed data--length error</span><br></pre></td></tr></tbody></table></figure><p>以上情况<strong>可能并不是因为下载过程中的网络问题</strong>,而是 <code>ascp</code> 下载的文件本身存在问题,经实测某些文件下载不存在问题的话,不管下载多少次都不会出错。而某些文件如果下载后存在问题,那么不管下载多少次都会存在问题,这同时也体现了 <code>prefetch</code> 的特点 —— 慢但稳定。</p><h3 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h3><p>本文解决方案皆来自 sra-tools Github issue:</p><blockquote><ul><li><p><a href="https://github.com/ncbi/sra-tools/issues/214">fasterq-dump err #214</a></p></li><li><p><a href="https://github.com/ncbi/sra-tools/issues/545">Fasterq-dump failing consistently #545</a></p></li></ul></blockquote></body></html>]]></content>
<summary type="html">本文将介绍当使用 fasterq-dump 下载序列不成功时其他的可行替代方法(prefetch 和 ascp) ,同时也提到 ascp 下载 fastq 文件时可能出现的问题和报错。</summary>
<category term="学习" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/"/>
<category term="生信" scheme="https://biojuse.com/categories/%E5%AD%A6%E4%B9%A0/%E7%94%9F%E4%BF%A1/"/>
</entry>
</feed>