-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
2239 lines (2181 loc) · 275 KB
/
index.xml
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Yorksite</title>
<link>https://yorks0n.github.io/</link>
<description>Recent content on Yorksite</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language>
<lastBuildDate>Thu, 20 Feb 2025 16:00:00 +0800</lastBuildDate><atom:link href="https://yorks0n.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Rstudio也能用GitHub Copilot了</title>
<link>https://yorks0n.github.io/posts/rstudio_with_copilot/</link>
<pubDate>Fri, 27 Dec 2024 12:00:11 +0800</pubDate>
<guid>https://yorks0n.github.io/posts/rstudio_with_copilot/</guid>
<description>希望我不是最后一个知道的。</description>
<content:encoded><![CDATA[<p>之前在VS Code中体验过Copilot的自动补全,大为震撼,但是其他IDE写R的体验都不是很好,就没有日常用起来。最近偶然一搜,发现说Rtudio里也能用Copilot的自动补全,赶紧配置了试了一下。</p>
<p>注:此过程适用于Mac上英文版Rstudio,并且需要保证有良好的网络环境。</p>
<h2 id="配置过程">配置过程</h2>
<p>首先将Rstudio更新到最新版,打开Rsutio,由于登录GitHub可能需要良好的网络环境,因此需要先给Rtudio设置http代理。首先在Rtudio中运行以下命令,打开<code>~/.Renviron</code>处的一个配置文件。</p>
<pre tabindex="0"><code>file.edit('~/.Renviron')
</code></pre><p>在打开的文件中加入http代理设置如下,设置完后记得保存。</p>
<pre tabindex="0"><code>http_proxy=http://proxy.dom.com/ #代理服务器地址,或者为http_proxy=http://ip地址:端口号
http_proxy_user=user:passwd #用户名和密码,没有用户名和密码时可省略此行
</code></pre><p>随后在顶部菜单栏选择Tools - Global Options</p>
<p><img loading="lazy" src="images//image-20241227103247146.png" alt="image-20241227103247146" />
</p>
<p>最下面一栏直接就有Copilot的选项,选中并在顶部启用,再登录自己的GitHub账号。</p>
<p><img loading="lazy" src="images/image-20241227103503312-5266905.png" alt="image-20241227103503312" />
</p>
<p>如果网络条件不佳,可能会遇到GitHub登录页面打不开的情况。如果是网页上显示<strong>Congratulations, you’re all set!</strong>,但是Rsutio中还是一直在加载,说明给GitHub配置的http代理有问题。</p>
<p>如果步骤正确,此时会显示登录的账号名称:</p>
<p><img loading="lazy" src="images/image-20241227104214859-5267336.png" alt="image-20241227104214859" />
</p>
<p>这时按说就能正常使用Copilot了。</p>
<h2 id="试用">试用</h2>
<p>我试着写了两行注释,其他部分都让Copilot帮我补全,运行正常。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span><span style="color:#75715e"># generate some data to draw heatmap <- 这行是我写的</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">set.seed</span>(<span style="color:#ae81ff">123</span>)
</span></span><span style="display:flex;"><span>df <span style="color:#f92672"><-</span> <span style="color:#a6e22e">expand.grid</span>(x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span><span style="color:#f92672">:</span><span style="color:#ae81ff">10</span>, y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span><span style="color:#f92672">:</span><span style="color:#ae81ff">10</span>)
</span></span><span style="display:flex;"><span>df<span style="color:#f92672">$</span>z <span style="color:#f92672"><-</span> <span style="color:#a6e22e">rnorm</span>(<span style="color:#a6e22e">nrow</span>(df))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># load the ggplot2 package</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">library</span>(ggplot2)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># draw a heat map with ggplot <- 这行也是我写的</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ggplot</span>(data <span style="color:#f92672">=</span> df, <span style="color:#a6e22e">aes</span>(x <span style="color:#f92672">=</span> x, y <span style="color:#f92672">=</span> y, fill <span style="color:#f92672">=</span> z)) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">geom_tile</span>() <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">scale_fill_viridis_c</span>() <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">theme_minimal</span>() <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">theme</span>(legend.position <span style="color:#f92672">=</span> <span style="color:#e6db74">"bottom"</span>) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">labs</span>(title <span style="color:#f92672">=</span> <span style="color:#e6db74">"Heatmap of z values"</span>,
</span></span><span style="display:flex;"><span> x <span style="color:#f92672">=</span> <span style="color:#e6db74">"x values"</span>,
</span></span><span style="display:flex;"><span> y <span style="color:#f92672">=</span> <span style="color:#e6db74">"y values"</span>,
</span></span><span style="display:flex;"><span> fill <span style="color:#f92672">=</span> <span style="color:#e6db74">"z values"</span>)
</span></span></code></pre></div><p>画出的图</p>
<p><img loading="lazy" src="images/image-20241227104417598-5267458.png" alt="image-20241227104417598" />
</p>
<p>赶快体验一下吧。</p>
]]></content:encoded>
</item>
<item>
<title>双十一买了啥</title>
<link>https://yorks0n.github.io/posts/buy_11/</link>
<pubDate>Thu, 05 Dec 2024 18:16:00 +0800</pubDate>
<guid>https://yorks0n.github.io/posts/buy_11/</guid>
<description><p>最近太忙了,半个多月前就打完了草稿,但一直没空更新,趁偷闲更新一下。</p>
<h2 id="ipad-mini-a17-pro">iPad mini A17 Pro</h2>
<p>赶上国补的85折买的,首发破发的苹果还得是国补。虽然是不用加400就能上air的价格,但早就想买个mini日常娱乐用。到手后又配了大康的膜和咸鱼淘的微瑕紫色双面夹,发现不如橙色的亮眼,有些后悔,还是今年新出的紫色更好看。贴膜后屏幕摸起来不再涩涩的,也不担心丢在桌子柜子上刮到机器了。非常满意,几乎每天回到家就捧着它。现在每天白天看屏幕的时间太久了,回家看手机的时候总会觉得屏幕太小,很疲惫,mini的大小就刚刚好,既够大,又不至于大到不想捧着它。大部分时候我拿它看看b站的视频,或是看会儿直播(之前只用手机就不太会看直播,看屏幕久了太累了)。同时,用mini看微信读书也非常舒适,之前曾经在手机上用过,但那个屏幕太小,读书总不太得劲,宁可去用kindle看。我一直觉得看书这种事,偶尔导入一下书,其他时候只用kindle也不大麻烦,但现在发现用起来方便真的太重要了,能极大促进我拿起来读书的欲望,甚至让我第一次去充了个微信读书会员。</p>
<h2 id="caldigit-ts3-plus雷电拓展坞">caldigit TS3 plus雷电拓展坞</h2>
<p>不知道是最近压力大还是双十一前后,购物欲望老强了,新的Mac mini M4也让人看着就心动。然而我的M2 16+512才买一年多,用着也没啥不妥,纠结之下就没买新的Mac mini。于是寻思着买点其他的给自己一点快乐,某天突然想到我的Mac mini的HDMI接口可能是有点松垮,加上经常会去机器上插拔优盘等,于是老会碰到HDMI线,让屏幕黑屏,这时候就要重新去动一动那个线,并且此趋势近期有愈演愈烈的倾向,便打算买个扩展坞。既然要买,准备买个雷电接口的,可那时去看群友挂在闲鱼上的品相价格俱佳的TS4已经被卖掉了,搜了搜有洋垃圾plugable实在不咋好看,就降了降六百多收了个闲鱼上的TS3 plus。Caldigit的这几个扩展坞还都是DP的接口,相比HDMI接口也更稳固一些。虽然旧的这款是DP 1.2的,好在接我4K 60 hz的旧LG显示器也完全足够了。顺便收拾了一下桌子,把Mac mini放在升降台下,扩展坞放在升降台上,插拔优盘很方便,也不再会黑屏了。把我之前的一个爱国者固态也长期插在扩展坞上,休眠或重启后也不会断连,美中不足是扩展坞老是温温的,不知道夏天会不会太热。</p></description>
<content:encoded><![CDATA[<p>最近太忙了,半个多月前就打完了草稿,但一直没空更新,趁偷闲更新一下。</p>
<h2 id="ipad-mini-a17-pro">iPad mini A17 Pro</h2>
<p>赶上国补的85折买的,首发破发的苹果还得是国补。虽然是不用加400就能上air的价格,但早就想买个mini日常娱乐用。到手后又配了大康的膜和咸鱼淘的微瑕紫色双面夹,发现不如橙色的亮眼,有些后悔,还是今年新出的紫色更好看。贴膜后屏幕摸起来不再涩涩的,也不担心丢在桌子柜子上刮到机器了。非常满意,几乎每天回到家就捧着它。现在每天白天看屏幕的时间太久了,回家看手机的时候总会觉得屏幕太小,很疲惫,mini的大小就刚刚好,既够大,又不至于大到不想捧着它。大部分时候我拿它看看b站的视频,或是看会儿直播(之前只用手机就不太会看直播,看屏幕久了太累了)。同时,用mini看微信读书也非常舒适,之前曾经在手机上用过,但那个屏幕太小,读书总不太得劲,宁可去用kindle看。我一直觉得看书这种事,偶尔导入一下书,其他时候只用kindle也不大麻烦,但现在发现用起来方便真的太重要了,能极大促进我拿起来读书的欲望,甚至让我第一次去充了个微信读书会员。</p>
<h2 id="caldigit-ts3-plus雷电拓展坞">caldigit TS3 plus雷电拓展坞</h2>
<p>不知道是最近压力大还是双十一前后,购物欲望老强了,新的Mac mini M4也让人看着就心动。然而我的M2 16+512才买一年多,用着也没啥不妥,纠结之下就没买新的Mac mini。于是寻思着买点其他的给自己一点快乐,某天突然想到我的Mac mini的HDMI接口可能是有点松垮,加上经常会去机器上插拔优盘等,于是老会碰到HDMI线,让屏幕黑屏,这时候就要重新去动一动那个线,并且此趋势近期有愈演愈烈的倾向,便打算买个扩展坞。既然要买,准备买个雷电接口的,可那时去看群友挂在闲鱼上的品相价格俱佳的TS4已经被卖掉了,搜了搜有洋垃圾plugable实在不咋好看,就降了降六百多收了个闲鱼上的TS3 plus。Caldigit的这几个扩展坞还都是DP的接口,相比HDMI接口也更稳固一些。虽然旧的这款是DP 1.2的,好在接我4K 60 hz的旧LG显示器也完全足够了。顺便收拾了一下桌子,把Mac mini放在升降台下,扩展坞放在升降台上,插拔优盘很方便,也不再会黑屏了。把我之前的一个爱国者固态也长期插在扩展坞上,休眠或重启后也不会断连,美中不足是扩展坞老是温温的,不知道夏天会不会太热。</p>
]]></content:encoded>
</item>
<item>
<title>文献一团乱麻?试试PARA管理方法</title>
<link>https://yorks0n.github.io/posts/para_and_zotero/</link>
<pubDate>Fri, 29 Nov 2024 13:30:00 +0800</pubDate>
<guid>https://yorks0n.github.io/posts/para_and_zotero/</guid>
<description><h2 id="文献积累学术生涯的外部记忆">文献积累——学术生涯的外部记忆</h2>
<blockquote>
<p>在科研之路上,你想要积累哪些知识?</p></blockquote>
<p>有没有过这样的经历?你正在准备一个重要的研究报告或论文,需要找到那篇关键的文献,但却在堆积如山的文件和资料中找不到它。或者你读过一篇很好的文献,但当你想引用它的时候,你却忘记了它的具体内容。</p>
<p>在一些科幻作品中,存在“外部记忆装置”的设定,其中的人物往往不将全部信息记在脑中,而是在需要时从外部记忆中调取内容。如今我们的现实已经与之颇有些相似:我们不会记住每一个具体的知识细节,浩如烟海的内容爆炸也不允许我们这么做,但我们通常知道去哪里找到这部分知识。当下,电脑和手机已经成为我们肢体的延伸,搜索引擎强大的检索功能,赋予我们按照需求取用知识的能力。</p>
<p><img loading="lazy" src="images/image.png" alt="image.png" />
</p>
<p>而在科研道路上,经年累月的学习不但锻炼着我们的实验技能,积累下来的知识同样是我们学习道路上宝贵的财富。在此过程中,我们读过的每一篇文献,被我们记住的不是其中具体的语句,而是各项工作中的主要结果和结论。但在有些时候,比方说想参考某个方法做个相似的实验,或是写作背景综述时,我们会回忆起曾经看过的文章,会去找出最原始的文章查看更多细节。这时,曾经整理过的文献便摇身一变,担当起我们学术生涯外部记忆的职责。</p>
<p><img loading="lazy" src="images/image%201.png" alt="image.png" />
</p>
<h2 id="为什么文献常常乱作一团">为什么文献常常乱作一团?</h2>
<p>那么,为什么明明每一篇文献都有明确的主题和关键词,整理起来却依旧存在非常多困难呢?举个例子,说有一篇文章,“如何做出一盘无敌好吃的小炒肉”,里面从肉的选购、处理、调味到烹饪方法一应俱全。你看完后,觉得非常好,这么棒的技巧我一定要学会,你会把它收在哪?</p>
<ul>
<li>菜谱 —— 没错,再怎么说这也是个菜谱,放这准没错</li>
<li>年夜饭菜单 —— 今年的年夜饭由你掌勺,正巧家里有人最爱吃小炒肉,放这备着</li>
<li>如何买菜 —— 本文的肉类选购部分讲得太清晰了,常常温习准能以后买菜不踩坑</li>
<li>常用调味料 —— 刚搬了新家,需要买些调料,你正在想买什么合适</li>
<li>……</li>
</ul>
<p>如果从上面几点出发,这些归类方式似乎都有各自的道理,这也是信息管理有时会比较困难的原因:在不同的场景下,相同的内容可以有不同的分类方式,并且往往没有对错之分。</p>
<p>因此我觉得,好的文献管理思路比工具更重要。现在能用来做文献管理的工具层出不穷,Endnote也好,Zotero也好,网上都能找到非常详细的用法教程。但是假如只有工具,没有恰当的内容组织思路,那工具里的文献也会堆成一团乱麻。我更想提供一个框架,让每个人可以根据自己的需要,将内容以一种便于寻找的方式组织起来。</p>
<blockquote>
<p>我们的目的:让内容便于被找到。</p></blockquote>
<h2 id="什么是para方法">什么是PARA方法?</h2>
<p>在之前的很长时间里,我一直隐约觉得,自己整理的文献资料库,其中的各个文件夹彼此之间没有明确的分隔关系,以至于想存一篇新文章的时候,要纠结半天放在哪。纠结完存进去后,要找来用的时候又找不出来这篇文章了。后来偶然间了解到了一种称为PARA的信息整理方法,它将内容分类为项目、领域与资源几个大类。或许它不是最好最完美的,但是相比于没有框架的时候,已经有了质的飞跃。也因此,我今天将其分享给大家,如果你们也遇到过相似的困扰,可以尝试一下这个方法。</p>
<p>所谓PARA,其实是Tiago Forte提出的一种组织信息的思路。其实,PARA分别代表Projects(项目)、Areas(领域)、Resources(资源)和Archives(归档)。</p>
<p><img loading="lazy" src="images/image%202.png" alt="image.png" />
</p>
<p>这样讲可能比较抽象,举个我自己的例子,比如我关注基因组学和生物信息学,我所做的课题也与之有关,相关分析做得好可以帮助我产出成果,做不好则毕业都很费劲,我<strong>需要为此负责</strong>,因此<strong>基因组学</strong>就可称作我关注的一个<strong>领域</strong>。同时,我的课题是一个<strong>物种的基因组测序项目</strong>,从我开始着手找资料开始,历经测序、组装和分析,直到文章发表结束,因为能划分出非常<strong>明确的开始和结束时间</strong>,因此这个课题属于一个<strong>项目</strong>。同时,我又对<strong>平面设计</strong>比较<strong>感兴趣</strong>,会去看一些相关的书籍和学习资料,这些东西我看着会觉得很有意思,但如果不看也不太影响我完成课题,于是可以算作<strong>资源</strong>。另外,假如我之前有个项目已经做完了,已经完成项目的内容我当然不需要经常去关注,那它就可以被划到归档。</p>
<p><img loading="lazy" src="images/image%203.png" alt="image.png" />
</p>
<p>然而,领域与项目、资源之间并不是独立的,而是可以互相滋养</p>
<p><strong>通过项目来打磨提升领域</strong>——比方说,我关注<strong>基因组学</strong>,但是只靠看一堆基因组相关的文章提升实在有限,必须要自己去实际做一些项目来锻炼,在项目中熟悉各部分的流程,并实际掌握操作的技能。</p>
<p><strong>通过资源支撑领域域</strong> ——我们日常看的各种文章、资料,大部分都属于资源而非领域,正如上面讲到,我们没法只靠读来掌握某些内容。但是每天的精力是有限的,假如分配给太多方面,面面俱到也很困难。于是更好的方法是<strong>基于领域 (或者将来潜在的 Area)作为精进的方向,然后把资源当做领域的基石和养分</strong>。</p>
<p><img loading="lazy" src="images/image%204.png" alt="image.png" />
</p>
<h2 id="如何将para方法应用在组织文献中">如何将PARA方法应用在组织文献中</h2>
<p>不过,要想用这种方式来管理自己的文献,首先需要花点时间想一想,对自己来说,究竟想要积累哪些知识?</p>
<p>这里先来讲讲我的,我在项目中放入了刚刚提到的物种测序项目,因为这是我的课题;在领域中包含了基因组学,数据可视化,生物信息学等,因为这是我的专业方向,同时与我的课题息息相关;在资源中放入了平面设计,认知与思维方式,效率,以及语言,因为我对这些方面非常感兴趣;最后,归档中有我之前做过的一个项目,某个物种的耐盐基因筛选,它已经结束了,于是进了归档。</p>
<p>这个清单的长度当然没有明确的限制,但是当你打算往自己关注的领域里放入二三十个主题时,也得掂量掂量有没有足够的精力。创建一个清晰的清单,能让你对自己想要积累知识的边界更加明确,就可以据此有意识地、战略性地决定看或者不看某篇文章,决定某个拖了很久的工作究竟是赶快弥补还是尽快放弃它。 相反,如果你不为自己定义清单,就会被不断拉着、推到别人的项目中去。比如,CNS新出了一堆顶刊文章,你看不看,看哪些?</p>
<p><img loading="lazy" src="images/image%205.png" alt="image.png" />
</p>
<p>可以看到,其中有几部分是和科研有关的,另一部分则是兴趣使然。和科研有关的部分,我就时常需要读些论文,并将对应的知识积累起来。</p>
<p>然后,你需要选择一个用来管理文献的工具。我选择了Zotero作为文献管理的工具,它大概长这样。左边的区域包括以文件夹形式组织的目录,下方是给文献打上的标签,中间区域是文献的标题、作者、期刊、年份等信息,右边则是选中某条文章后的详细信息。</p>
<p><img loading="lazy" src="images/image%206.png" alt="image.png" />
</p>
<p>你也完全可以选择别的工具,比如更为老牌的Endnote,可能会是更多人包括大家的导师的第一选择,生命科学图书馆之前是有Endnote可以下载的。我讲的与选择特定工具并不关联,你可以任意选择喜欢的工具,并在那个工具中实现相似的效果。</p>
<p>接下来需要在文献管理软件中根据选择好的项目、领域、资源创建大类。</p>
<p>假如你已经在使用这样的文献管理工具,已经积累了一些文章,但是它们有些混乱。可以这样重新开始:将所有现有的文章移动到一个新的文件夹,命名为归档和当前日期,然后开始接下来的步骤。因为与其花费大量时间整理它们,将来也不一定用得到,反而可能让你失去开始的契机。因此不如和它们暂时告别,不必担心丢失任何东西,它们都还在,当需要的时候还可以通过搜索找到它们。</p>
<p>让我们继续,在管理软件的目录下,按照项目、领域、资源和归档创建四个大类,并将大类下面为你选定的主题再分别创建文件夹。记住,一开始只需要创建到每个大类下的主题就可以了,只有当在你在这个主题下积累了一定的内容,感觉需要进一步细分时,才创建新的文件夹,可以最大程度避免文件夹的冗余和混乱。</p>
<p>这里还有两个小技巧,其一是默认的文件夹可能会按字母排序,你可以在前面加上数字,让它们按你想要的顺序排序。其二是我还为其添加了一个收集箱(Inbox),放在所有类别最前面,这样当遇到某篇可能感兴趣的文章时,可以先将其收进收集箱,等看完后判断一下,是将它储存起来以备将来查阅,还是和我需要的知识不太相关,因此直接删除。</p>
<p><img loading="lazy" src="images/image%207.png" alt="image.png" />
</p>
<p>随后,可以进一步按照内容将某个项目拆分为更细的子类别。比如这里以我比较熟悉的基因组测序项目为例,它由前期的基因组调研、组装注释,以及下游的一系列分析组成。</p>
<p>我可以按照项目推进的阶段,将其拆分为一个个子项目,比如在调研阶段,我看的文章主要是基因组大小、染色体信息、近缘物种等资料,在组装注释阶段,需要看各种软件的使用方法,而在分析阶段,则需要阅读许多现有文章,总结其分析流程和方法。</p>
<p>这里强调两个实用的功能,一个是标签,我给一部分比较常用的标签设置了颜色,让其显示在文章处,比如标记文章是否已读,读完觉得文章是否重要。还可以按照文章类型标记标签,比如综述类、方法类,或是将文章归类为特定主题,一切都以自己将来找某个文章时是否方便为依据。</p>
<p>另一个实用的功能是Zotero里的Short Title功能,可以看向右图中中间这一列。这一列默认是不显示的,需要在调整显示的列中将其显示出来。可以看到我给不少文章加上了短标题,这样瞄一眼就能找到我想找的文章。</p>
<p>虽然这里只写了项目,但是领域、资源类别下同理进行划分。最后记得将已经完成的项目,放入归档中。</p></description>
<content:encoded><![CDATA[<h2 id="文献积累学术生涯的外部记忆">文献积累——学术生涯的外部记忆</h2>
<blockquote>
<p>在科研之路上,你想要积累哪些知识?</p></blockquote>
<p>有没有过这样的经历?你正在准备一个重要的研究报告或论文,需要找到那篇关键的文献,但却在堆积如山的文件和资料中找不到它。或者你读过一篇很好的文献,但当你想引用它的时候,你却忘记了它的具体内容。</p>
<p>在一些科幻作品中,存在“外部记忆装置”的设定,其中的人物往往不将全部信息记在脑中,而是在需要时从外部记忆中调取内容。如今我们的现实已经与之颇有些相似:我们不会记住每一个具体的知识细节,浩如烟海的内容爆炸也不允许我们这么做,但我们通常知道去哪里找到这部分知识。当下,电脑和手机已经成为我们肢体的延伸,搜索引擎强大的检索功能,赋予我们按照需求取用知识的能力。</p>
<p><img loading="lazy" src="images/image.png" alt="image.png" />
</p>
<p>而在科研道路上,经年累月的学习不但锻炼着我们的实验技能,积累下来的知识同样是我们学习道路上宝贵的财富。在此过程中,我们读过的每一篇文献,被我们记住的不是其中具体的语句,而是各项工作中的主要结果和结论。但在有些时候,比方说想参考某个方法做个相似的实验,或是写作背景综述时,我们会回忆起曾经看过的文章,会去找出最原始的文章查看更多细节。这时,曾经整理过的文献便摇身一变,担当起我们学术生涯外部记忆的职责。</p>
<p><img loading="lazy" src="images/image%201.png" alt="image.png" />
</p>
<h2 id="为什么文献常常乱作一团">为什么文献常常乱作一团?</h2>
<p>那么,为什么明明每一篇文献都有明确的主题和关键词,整理起来却依旧存在非常多困难呢?举个例子,说有一篇文章,“如何做出一盘无敌好吃的小炒肉”,里面从肉的选购、处理、调味到烹饪方法一应俱全。你看完后,觉得非常好,这么棒的技巧我一定要学会,你会把它收在哪?</p>
<ul>
<li>菜谱 —— 没错,再怎么说这也是个菜谱,放这准没错</li>
<li>年夜饭菜单 —— 今年的年夜饭由你掌勺,正巧家里有人最爱吃小炒肉,放这备着</li>
<li>如何买菜 —— 本文的肉类选购部分讲得太清晰了,常常温习准能以后买菜不踩坑</li>
<li>常用调味料 —— 刚搬了新家,需要买些调料,你正在想买什么合适</li>
<li>……</li>
</ul>
<p>如果从上面几点出发,这些归类方式似乎都有各自的道理,这也是信息管理有时会比较困难的原因:在不同的场景下,相同的内容可以有不同的分类方式,并且往往没有对错之分。</p>
<p>因此我觉得,好的文献管理思路比工具更重要。现在能用来做文献管理的工具层出不穷,Endnote也好,Zotero也好,网上都能找到非常详细的用法教程。但是假如只有工具,没有恰当的内容组织思路,那工具里的文献也会堆成一团乱麻。我更想提供一个框架,让每个人可以根据自己的需要,将内容以一种便于寻找的方式组织起来。</p>
<blockquote>
<p>我们的目的:让内容便于被找到。</p></blockquote>
<h2 id="什么是para方法">什么是PARA方法?</h2>
<p>在之前的很长时间里,我一直隐约觉得,自己整理的文献资料库,其中的各个文件夹彼此之间没有明确的分隔关系,以至于想存一篇新文章的时候,要纠结半天放在哪。纠结完存进去后,要找来用的时候又找不出来这篇文章了。后来偶然间了解到了一种称为PARA的信息整理方法,它将内容分类为项目、领域与资源几个大类。或许它不是最好最完美的,但是相比于没有框架的时候,已经有了质的飞跃。也因此,我今天将其分享给大家,如果你们也遇到过相似的困扰,可以尝试一下这个方法。</p>
<p>所谓PARA,其实是Tiago Forte提出的一种组织信息的思路。其实,PARA分别代表Projects(项目)、Areas(领域)、Resources(资源)和Archives(归档)。</p>
<p><img loading="lazy" src="images/image%202.png" alt="image.png" />
</p>
<p>这样讲可能比较抽象,举个我自己的例子,比如我关注基因组学和生物信息学,我所做的课题也与之有关,相关分析做得好可以帮助我产出成果,做不好则毕业都很费劲,我<strong>需要为此负责</strong>,因此<strong>基因组学</strong>就可称作我关注的一个<strong>领域</strong>。同时,我的课题是一个<strong>物种的基因组测序项目</strong>,从我开始着手找资料开始,历经测序、组装和分析,直到文章发表结束,因为能划分出非常<strong>明确的开始和结束时间</strong>,因此这个课题属于一个<strong>项目</strong>。同时,我又对<strong>平面设计</strong>比较<strong>感兴趣</strong>,会去看一些相关的书籍和学习资料,这些东西我看着会觉得很有意思,但如果不看也不太影响我完成课题,于是可以算作<strong>资源</strong>。另外,假如我之前有个项目已经做完了,已经完成项目的内容我当然不需要经常去关注,那它就可以被划到归档。</p>
<p><img loading="lazy" src="images/image%203.png" alt="image.png" />
</p>
<p>然而,领域与项目、资源之间并不是独立的,而是可以互相滋养</p>
<p><strong>通过项目来打磨提升领域</strong>——比方说,我关注<strong>基因组学</strong>,但是只靠看一堆基因组相关的文章提升实在有限,必须要自己去实际做一些项目来锻炼,在项目中熟悉各部分的流程,并实际掌握操作的技能。</p>
<p><strong>通过资源支撑领域域</strong> ——我们日常看的各种文章、资料,大部分都属于资源而非领域,正如上面讲到,我们没法只靠读来掌握某些内容。但是每天的精力是有限的,假如分配给太多方面,面面俱到也很困难。于是更好的方法是<strong>基于领域 (或者将来潜在的 Area)作为精进的方向,然后把资源当做领域的基石和养分</strong>。</p>
<p><img loading="lazy" src="images/image%204.png" alt="image.png" />
</p>
<h2 id="如何将para方法应用在组织文献中">如何将PARA方法应用在组织文献中</h2>
<p>不过,要想用这种方式来管理自己的文献,首先需要花点时间想一想,对自己来说,究竟想要积累哪些知识?</p>
<p>这里先来讲讲我的,我在项目中放入了刚刚提到的物种测序项目,因为这是我的课题;在领域中包含了基因组学,数据可视化,生物信息学等,因为这是我的专业方向,同时与我的课题息息相关;在资源中放入了平面设计,认知与思维方式,效率,以及语言,因为我对这些方面非常感兴趣;最后,归档中有我之前做过的一个项目,某个物种的耐盐基因筛选,它已经结束了,于是进了归档。</p>
<p>这个清单的长度当然没有明确的限制,但是当你打算往自己关注的领域里放入二三十个主题时,也得掂量掂量有没有足够的精力。创建一个清晰的清单,能让你对自己想要积累知识的边界更加明确,就可以据此有意识地、战略性地决定看或者不看某篇文章,决定某个拖了很久的工作究竟是赶快弥补还是尽快放弃它。 相反,如果你不为自己定义清单,就会被不断拉着、推到别人的项目中去。比如,CNS新出了一堆顶刊文章,你看不看,看哪些?</p>
<p><img loading="lazy" src="images/image%205.png" alt="image.png" />
</p>
<p>可以看到,其中有几部分是和科研有关的,另一部分则是兴趣使然。和科研有关的部分,我就时常需要读些论文,并将对应的知识积累起来。</p>
<p>然后,你需要选择一个用来管理文献的工具。我选择了Zotero作为文献管理的工具,它大概长这样。左边的区域包括以文件夹形式组织的目录,下方是给文献打上的标签,中间区域是文献的标题、作者、期刊、年份等信息,右边则是选中某条文章后的详细信息。</p>
<p><img loading="lazy" src="images/image%206.png" alt="image.png" />
</p>
<p>你也完全可以选择别的工具,比如更为老牌的Endnote,可能会是更多人包括大家的导师的第一选择,生命科学图书馆之前是有Endnote可以下载的。我讲的与选择特定工具并不关联,你可以任意选择喜欢的工具,并在那个工具中实现相似的效果。</p>
<p>接下来需要在文献管理软件中根据选择好的项目、领域、资源创建大类。</p>
<p>假如你已经在使用这样的文献管理工具,已经积累了一些文章,但是它们有些混乱。可以这样重新开始:将所有现有的文章移动到一个新的文件夹,命名为归档和当前日期,然后开始接下来的步骤。因为与其花费大量时间整理它们,将来也不一定用得到,反而可能让你失去开始的契机。因此不如和它们暂时告别,不必担心丢失任何东西,它们都还在,当需要的时候还可以通过搜索找到它们。</p>
<p>让我们继续,在管理软件的目录下,按照项目、领域、资源和归档创建四个大类,并将大类下面为你选定的主题再分别创建文件夹。记住,一开始只需要创建到每个大类下的主题就可以了,只有当在你在这个主题下积累了一定的内容,感觉需要进一步细分时,才创建新的文件夹,可以最大程度避免文件夹的冗余和混乱。</p>
<p>这里还有两个小技巧,其一是默认的文件夹可能会按字母排序,你可以在前面加上数字,让它们按你想要的顺序排序。其二是我还为其添加了一个收集箱(Inbox),放在所有类别最前面,这样当遇到某篇可能感兴趣的文章时,可以先将其收进收集箱,等看完后判断一下,是将它储存起来以备将来查阅,还是和我需要的知识不太相关,因此直接删除。</p>
<p><img loading="lazy" src="images/image%207.png" alt="image.png" />
</p>
<p>随后,可以进一步按照内容将某个项目拆分为更细的子类别。比如这里以我比较熟悉的基因组测序项目为例,它由前期的基因组调研、组装注释,以及下游的一系列分析组成。</p>
<p>我可以按照项目推进的阶段,将其拆分为一个个子项目,比如在调研阶段,我看的文章主要是基因组大小、染色体信息、近缘物种等资料,在组装注释阶段,需要看各种软件的使用方法,而在分析阶段,则需要阅读许多现有文章,总结其分析流程和方法。</p>
<p>这里强调两个实用的功能,一个是标签,我给一部分比较常用的标签设置了颜色,让其显示在文章处,比如标记文章是否已读,读完觉得文章是否重要。还可以按照文章类型标记标签,比如综述类、方法类,或是将文章归类为特定主题,一切都以自己将来找某个文章时是否方便为依据。</p>
<p>另一个实用的功能是Zotero里的Short Title功能,可以看向右图中中间这一列。这一列默认是不显示的,需要在调整显示的列中将其显示出来。可以看到我给不少文章加上了短标题,这样瞄一眼就能找到我想找的文章。</p>
<p>虽然这里只写了项目,但是领域、资源类别下同理进行划分。最后记得将已经完成的项目,放入归档中。</p>
<p><img loading="lazy" src="images/image%208.png" alt="image.png" />
</p>
<p>总结一下,PARA方法实际是提供了一种归类思路,将所有的内容以某种分类规则,构建成了树状的结构。树状结构层次清晰,利于我们记忆,检索起来也非常方便。而文献管理软件中标签的存在,让文章间有了更复杂的联系,组建成网状结构,允许我们以更复杂的方式进行筛选:比如我想选择全部没有读过的、综述类型的、和细胞壁相关的文章,只需要同时选中这几个标签就能找到。</p>
<p><img loading="lazy" src="images/image%209.png" alt="image.png" />
</p>
<h2 id="一些小建议">一些小建议</h2>
<p>再提几个可能能让文章不会过度混乱的小建议。</p>
<blockquote>
<p>保持克制,不当仓鼠</p></blockquote>
<p>我们首先需要的,是考虑清楚自己打算积累哪些知识。我最开始就有过一阵这样子的阶段,每次看到一篇文章都想先将其保存下来,想着“万一用的上呢”。 仿佛保存过文章,我就有拥有了这部分知识。加上Zotero中保存文章实在是非常方便,点一下浏览器的收藏插件就可以将文章存下来,因此保存的文章越来越多,但其实大部分文章都没怎么仔细看过,文章越来越多,文档库也越来越混乱,从中找文章的过程也变得复杂。直到某个时候,我发现保存下来完全没看过的文章很多,我既看不完它们,也没法将它们用起来。后来才意识到,一篇文章如果只是被下载下来,存在电脑里,并不意味着我就拥有了这部分知识。因此盲目地收藏文章并不可取,反而很容易因为囤积了太多,使计划稍后仔细读一读的文章变成再也不读。</p>
<blockquote>
<p>定期整理</p></blockquote>
<p>其次,不要想一次性设计出一个一劳永逸的资料管理系统。这也是我有过的困扰,将文献保存下来后,每次随便地想一个合适的文件夹储存它们。这样过了一段时间,我有好多好多并列的文件夹,但是我甚至不记得每个文件夹里存了些什么,不少文件夹的内容还互相有所交叉。终于某一天我下定决心,把所有的文件夹整理了一遍,能合并的合并,很多没看过、用不上的文章都删掉,这才感觉清爽了不少。并且意识到,很多最开始文件夹组织上的混乱,来源于课题早期对于整体脉络的不清晰,同时处在课题的探索阶段,什么都看一点,才弄出了一大堆各种内容的文件夹;到了课题中后期,主要内容已经明确,因为脉络不清晰造成的混乱也就自然而然地解决了。也正因如此,我觉得只有定期整理一下,让文献之间的结构伴随着你的成长,像植物那样逐渐生长出来,才能得到最适合你的文献管理结构。</p>
<blockquote>
<p>不必面面俱到。</p></blockquote>
<p>有些时候某一篇文章,已经放在项目下的一个文件夹里了,你可能会纠结,用不用再去其他符合这篇文章气质的文件夹再放一份?我也曾纠结过这一点,感觉不把每个可能相关的文件夹都放一份,将来找的时候万一找不到就很麻烦。
而现在的我,对此建议是保持克制,只把文章放在你当前需要用到的类别中。因为后来想明白了,收集是为了之后的使用,并不为了打造一个事无巨细的图书馆。等将来的某一天真要找这篇文章的时候,回想到曾经在做某个项目的过程中看过,然后就会去项目下面根据所属的类别找到这篇文章,到那时如果有在别处用到它的需求,比如说你有了一个新的项目,在写一篇新的综述,再将其放在对应的合集中就可以了。也得益于使用了文献管理软件,在多个文件夹下存在的同一篇文章,并不会分散成多个文件,而是共用相同的条目信息,因此只要是同一篇文章,哪怕在每个文件夹中都放一份,最终在数量上也只算是有一篇,它们的笔记也都是共通的。</p>
<h2 id="补充阅读">补充阅读</h2>
<ul>
<li>
<p><a href="https://sspai.com/post/63830">我的科研软件流分享 - 少数派</a></p>
</li>
<li>
<p><a href="https://sspai.com/post/72163">解决科研人痛点的大突破——Zotero 6.0 亮点速览 - 少数派</a></p>
</li>
</ul>
]]></content:encoded>
</item>
<item>
<title>点名的风还是吹到了Blog</title>
<link>https://yorks0n.github.io/posts/nine_questions/</link>
<pubDate>Wed, 23 Oct 2024 00:00:00 +0000</pubDate>
<guid>https://yorks0n.github.io/posts/nine_questions/</guid>
<description>历史真是个圈。</description>
<content:encoded><![CDATA[<p>以前还在玩QQ空间时,曾流行过一阵点名风潮。也不知最早是从何处兴起,点名者会罗列出一系列诸如姓名、生日、谁传给你的、未来想做什么这样的问题,写上自己的答案并@一些朋友们,被点者要复制这些问题,填上自己的回复,再重复这一过程。上次见到这种玩法估计得是十五六年前了,历史竟能如此相像?</p>
<p><img loading="lazy" src="images/unnamed.jpg" alt="unnamed" />
</p>
<p>再说回来,感谢Dayu的邀请,这也让我停更了一年多的博客重新出现了些生机。好久没写,Hugo的语法都有了不少更新,以至于和之前的主题不太兼容。为了能继续用之前的主题,还给它提了issue,研究了半天才给修好。</p>
<h3 id="1-简单介绍下自己或者你的博客">1. 简单介绍下自己或者你的博客?</h3>
<p>如果你和我不太熟,可以叫我Yorkson。这些年来,我的名字少说还有十来种不同的叫法,甚至到了看到对应的叫法就能判断是哪个时期的哪些朋友们的地步。目前我还在生物信息学专业读博,因此博客中主要的文章主要和一些生信分析、或是数据可视化(我的兴趣之一)有关。有很多杂七杂八的爱好,每天高强度上网冲浪,但很多内容不便在此展开,虽然是 i 人但更推崇线下面对面的交流。</p>
<h3 id="2-什么契机让你开始写博客">2. 什么契机让你开始写博客?</h3>
<p>早在大学时就曾弄了自己的博客,甚至为域名去折腾了半天备案,后来发现花在折腾建立起博客上的时间远多于写东西的时间。最近的一次开始于22年上海疫情封控期间,被关在家里无事可做,于是尝试开始在少数派上写文章,也因此为契机能认识Dayu。后来为了能把一些杂七杂八的主题和零散的文章规整在一起,于是又一次买了域名弄了博客。另一方面的原因,则是因为这几年生信基本全靠自学,需要高强度上网检索各种奇怪问题,其中许多疑难杂症都是靠着世界另一处不认识的网友在博客等处写的文章解决的。这种「写下的东西能在未来某一天帮助素不相识之人」的感觉异常有趣,因此我希望能够以博客的形式分享知识,兴许能微微降低知识传播的门槛,生产出一些比AIGC质量稍高一点的内容。</p>
<h3 id="3-你是如何完成创作的">3. 你是如何完成创作的?</h3>
<p>哎,这个问题大抵是问写作的工作流。我可能会偶然间想到某个选题,将其记录下来,偶尔有什么点子就稍微往里补充一下。过了一阵子想写文章了,就在那期间高强度围绕这个选题进行一定的检索,或是写一些片段,并尽量以某个逻辑组织起来,然后觉得还差点意思,就丢在那不管了。直到某个时候,感觉写出来的东西没必要也无法做到完全完美,这才会发出来。还有些想整理成文章的是这些年各种摸爬滚打所记录下的笔记,但是一直拖着没有写,可能将来的某天会整理出来吧。</p>
<h3 id="4-运营博客的过程中是否有失去过动力如果有是为什么恢复的">4. 运营博客的过程中是否有失去过动力?如果有,是为什么恢复的?</h3>
<p>那可太有了,一旦停下就会停好久,不然也不至于一年半不写新东西。我觉得问题不大,至少我定期会有写作的愿望,过一阵又会犯懒不想写,尤其是忙的时候。不想写就不写,想写就写,就这么简单!不过这次大概是Dayu邀请我填这个点名才会继续更新一阵子吧,LOL。</p>
<h3 id="5-如何搭建博客以及运营博客每年需要投入的资金">5. 如何搭建博客,以及运营博客每年需要投入的资金?</h3>
<p>之前是买了国内的服务器,做了域名备案放在上面,但众所周知会有奇怪的麻烦之处。现在直接用Hugo生成静态网站,挂在GitHub Page里,体验也没差,成本也只有域名所需的一年那几十块钱。GitHub页面在某些时空访问可能费点劲儿,但大家都是天天上网冲浪的人,这点问题应该是会有办法的。</p>
<h3 id="6-推荐-1-篇你博客中的文章并推荐一个你喜欢读的博客聊聊原因">6. 推荐 1 篇你博客中的文章,并推荐一个你喜欢读的博客,聊聊原因。</h3>
<p>我会推荐这篇<a href="https://blog.yorks0n.com/posts/color_guide/">论文可视化配色简易指南</a>,是我根据自己感兴趣的选题去整理出来的,我觉得会比较实用。最初发在少数派上,但因为一些图片懒得重新做从中删去了一部分,这里的是我写这篇文章的原始版本。要推荐一个博客,我会推荐<a href="https://1q43.blog/">虹线</a>的博客,虽然部分文章可能会有人喜欢有人讨厌,但他的好几篇长文我读下来还是很喜欢并受到了不少启发的。</p>
<h3 id="7-推荐-1-个近期喜欢的事物">7. 推荐 1 个近期喜欢的事物?</h3>
<p>推荐个最近刚买的书签管理工具,Anybox,几乎完美满足了我的书签管理要求,终于摆脱了缓慢的Raindrop,原因我也刚写了文章细说了一下,见<a href="https://blog.yorks0n.com/posts/change_to_anybox/">从Raindrop迁移到了Anybox</a>。</p>
<h3 id="8-想做还没有做的事或想尝试还没有尝试的主题">8. 想做还没有做的事,或想尝试还没有尝试的主题?</h3>
<p>作为一个有八百个兴趣的人,想做的事太多了,除去已经尝试过然后搁置的那些,大概近期反复想起的是学不同的语言或是想学画画吧,都是需要很多时间精力投入才能有所回报的爱好,现在越来越没耐心长时间地集中注意力了,这可不行。</p>
<h3 id="9-写到这里闭上你的眼睛深呼吸几分钟或是出去溜达一圈然后回来写任何你想写的东西">9. 写到这里,闭上你的眼睛,深呼吸几分钟,或是出去溜达一圈,然后回来写任何你想写的东西。</h3>
<p>好久不看书,这两天又拿出Kindle,发现集中注意力越来越困难了,让人分心的东西实在太多,这不太行。读书的过程让我有种安心的感觉,似乎是其他活动给不了的。但这几年似乎总在翻来覆去读之前已经看过的书,哪怕已经看了很多遍了。想要一些新鲜的,有意思的阅读内容。假如你已经读到了这里,不妨听我一个请求:如果让你只推荐一本书,你会推荐什么呀,请一定要告诉我!</p>
<h3 id="写在最后">写在最后</h3>
<p>一鼓作气写完了这篇点名,似乎真有种和素未谋面的人交流的感受,不知道会有多少人能看到这篇文章呢?假如有些错别字还请谅解,应该会有的。</p>
<p>不论如何,如果你看到了这儿,向你问好!</p>
]]></content:encoded>
</item>
<item>
<title>从Raindrop迁移到了Anybox</title>
<link>https://yorks0n.github.io/posts/change_to_anybox/</link>
<pubDate>Tue, 22 Oct 2024 00:00:00 +0000</pubDate>
<guid>https://yorks0n.github.io/posts/change_to_anybox/</guid>
<description>苦raindrop久矣,终于找到趁手的书签管理工具了</description>
<content:encoded><![CDATA[<p>长期以来,我一直使用Raindrop作为我的书签管理服务,苦raindrop久矣。从2017年左右,一直用着免费版本的Raindrop,主要因为它那些付费版本的功能我也不太用得上,这些年也在其中积累下来大几百个书签,其中不少还标注了标签和备注方便查找。它倒是也能用,既支持多平台同步,又有网页版,最初免费版不支持创建嵌套的文件夹,后来也支持了,可以说功能上没啥大毛病。</p>
<p><img loading="lazy" src="images/Snipaste_2024-10-22_13-04-42.jpg" alt="Snipaste_2024-10-22_13-04-42" />
</p>
<p>然而这几年里,总想找机会把它换掉,无他,Raindrop的性能真是太差了。不仅各个平台的App都宛如网页版套壳,各种操作的响应也都非常慢。每次想添加新书签,还要在添加页面等一会儿,确定它同步完了再关闭窗口;搜索的时候,也经常弹不出搜索结果。为此,我一直在尝试各种书签管理工具,想找个满意的替代品,大致需求也不复杂:</p>
<ul>
<li>性能良好,添加书签时的反应迅速</li>
<li>同时支持文件夹和标签两种分类方式,便于分大类和进行筛选</li>
<li>检索性能优秀</li>
<li>支持同步功能,需要至少有桌面与手机平台</li>
<li>最好能有独立App,而非依靠浏览器插件等形式实现</li>
<li>非订阅制,开源或可以买断</li>
</ul>
<p>然而找了好久都没遇到合适的,许多开源项目都没有多端,或是同步起来相当费劲,亦或是书签多了后管理不便。一些付费服务页面fancy,但是性能巨差,或是性能好并且拥抱AI,于是每个月订阅费用感人。我还是希望能将书签管理与稍后读等内容管理分开的,避免混在一起检索起来麻烦,因此那些对内容的快速AI检索我也用不上。</p>
<p>前几年,也曾多次按月订阅试过Anybox,但最初不支持文件夹,支持Tag但不能手动调顺序,以至按类别检索效率略低。后来虽然更新出了标签嵌套,但截至目前还是不支持手动调整标签顺序,没法像文件夹那样方便。不过在最近的尝试中发现它支持了文件夹,甚至可以直接把Raindrop导出中的文件夹结构保留,解决了我主要的痛点。虽然软件为订阅制,但支持198元永久版买断,总比按月支出来得舒服。于是在又订阅了一个月把书签都导进来试用几天后,果断买断了,删除Raindrop,使用至今非常舒适。不管是软件本身提供的快速检索栏,还是结合了Raycast的插件,都异常丝滑,好好好!</p>
<p><img loading="lazy" src="images/search.gif" alt="search" />
</p>
<p>甚至现在Anybox还可以将网页版归档下载下来保存,也许之后会把读过想储存的文章也迁移过来?</p>
<p>要说Anybox现在的缺点,估计就是没有网页版也不支持win吧,但我日常主要用Mac和iOS,倒也完全够用。</p>
]]></content:encoded>
</item>
<item>
<title>数据可视化——基本图形元素及其应用</title>
<link>https://yorks0n.github.io/posts/basic_elements_data_vis/</link>
<pubDate>Wed, 19 Jul 2023 00:00:00 +0000</pubDate>
<guid>https://yorks0n.github.io/posts/basic_elements_data_vis/</guid>
<description>介绍可视化作图过程中使用的基本元素,简单分析不同数据类型与适合使用的展示方式。</description>
<content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>本文旨在介绍可视化作图过程中使用的基本元素,简单分析不同数据类型适合使用何种图形进行展示。通过了解这些基本元素,我们可以实现以下两个目的:</p>
<ul>
<li>更好地解读图片传递的信息</li>
<li>判断图表质量的好坏</li>
</ul>
<p>在理解了基本元素的基础上,我们能够更好地设计图表,从而更高效直观地向读者传递我们的结论。</p>
<h2 id="用于数据可视化的基本图形元素">用于数据可视化的基本图形元素</h2>
<h3 id="marks-and-channels">Marks and Channels</h3>
<p>一张数据可视化图(特指将数据用图的形式展示出来)中的元素,基本可以分成下面提到的两大类。即使是较为复杂的图,也能拆分成诸多基本元素的组合。</p>
<p>其中第一类称为 <strong>Mark(标记),是一张图中的基本图形元素。</strong> Mark根据其维度可以细分为三种:</p>
<p><img loading="lazy" src="images/vis_elements.png" alt="Untitled" />
</p>
<p>第二类元素是 <strong>Channel(通道),指用于控制 Mark 如何呈现在图中的变量。</strong> 通道可以分为以下几种:</p>
<ul>
<li>位置</li>
<li>颜色</li>
<li>形状</li>
<li>斜率</li>
<li>大小(长度、面积、体积)</li>
</ul>
<p><img loading="lazy" src="images/vis_elements1.png" alt="Untitled" />
</p>
<p>这样讲非常抽象,来几个具体的例子:</p>
<ul>
<li>a 图,使用线段作为 Mark(柱状图就是些粗了点的线段),Channel 是长度,所以当我们在看柱状图时,比较的实际是不同线段(柱子)的长度</li>
<li>b 图,用点作为 Mark,Channel 是点在图上的位置,每个点都有其坐标 (x,y)</li>
<li>c 图,在 b 图的基础上,增加了一个颜色 Channel,用这个通道将点分成了红蓝两组</li>
<li>d 图,在 c 图的基础上,又增加了面积作为 Channel 控制点的大小,额外编码了一个变量</li>
</ul>
<p><img loading="lazy" src="images/vis_elements2.png" alt="Untitled" />
</p>
<p><strong>以此可见,无论复杂还是简单的图,都是通过 Marks 与 Channels 的适当组合得到的。</strong></p>
<h3 id="channel-rankings">Channel Rankings</h3>
<p>在画图的时候,一次必然不会用到全部的上述元素。由于各种 Channel 传递信息的效果并不一致,要想让你的图更为直观,需要选择恰当的元素表现方式。</p>
<p>下图展示了不同类型的数据,用不同 Channel 进行表达时,其效果的优劣,越靠上的 Channel 越便于读者理解。</p>
<p>有顺序的数据(比如一组有大有小的测量值)应当以左边这组形式呈现,类别数据(比如野生型和突变体这两组材料)应当以右边的形式呈现。</p>
<p><img loading="lazy" src="images/vis_elements3.png" alt="Untitled" />
</p>
<p>对于两类数据,效果最好的都是空间位置。我们来看一组例子,体会一下不同数值编码方式传递信息的效率差异。</p>
<p>浏览下面的每一张图,快速判断图中的数据能分为几组。</p>
<p><img loading="lazy" src="images/vis_elements4.png" alt="Untitled" />
</p>
<ul>
<li>第一的图,用位置+颜色编码,很容易将点进行分组。</li>
<li>第二张图,用大小+颜色编码,稍微混乱了一些,但还算可以分辨。</li>
<li>第三张图,该分成三组还是四组?这张图采用宽度+高度编码,还算可以分辨,但由于我们通常感知面积快于形状,所以矮宽/高瘦的椭圆形可能在直觉上会被归为一组,于是图中四组容易被认作大中小的三组。</li>
<li>第四张图,只用颜色进行编码,非常混乱,实际也是四组。</li>
</ul>
<h2 id="不同数据类型与其适合的展示方式">不同数据类型与其适合的展示方式</h2>
<h3 id="one-quantitative-value">One quantitative value</h3>
<p>首先是一维数据,只有一组连续值,我们希望展示这组数据的分布情况。如果只用平均值、标准差来描述,可能会损失很多信息,便可以考虑用直方图或密度图来展示。</p>
<p><img loading="lazy" src="images/vis_elements5.png" alt="Untitled" />
</p>
<h4 id="直方图histogram">直方图(Histogram)</h4>
<p>直方图(Histogram)是展示 <strong>连续数据分布情况</strong> 最常用的工具,它划分好一组区间,将落到每个区间中值的个数进行统计并绘图,使用线条的长度来编码数据。由于直方图需要对连续型数据做离散分组,因此它有一个明显的缺点,就是它的形状依赖于分组的端点,例如若有好几个相同的数值正好处在分组端点上,那么我们只要稍微向左或向右移动一下分组端点,这些数据点就会被划分入不同的区间,导致矩形条的高度变化。</p>
<ul>
<li>Mark: line</li>
<li>Channel: length</li>
</ul>
<p><img loading="lazy" src="images/vis_elements6.png" alt="Untitled" />
</p>
<h4 id="密度图density-plot">密度图(Density plot)</h4>
<p>密度图利用核密度估计,对原始数据估计其密度函数,相比直方图对数据的描述更加准确。然而其边界处可能由于边界效应与实际情况不符。</p>
<ul>
<li>Mark: line</li>
<li>Channel: position</li>
</ul>
<p><img loading="lazy" src="images/vis_elements7.png" alt="Untitled" />
</p>
<h3 id="two-quantitative-values">Two quantitative values</h3>
<p>当面对二维连续值的情况,比一维稍微复杂一点,可以用散点图展示它们之间的关系。</p>
<p><img loading="lazy" src="images/vis_elements8.png" alt="Untitled" />
</p>
<h4 id="散点图scatter-plot">散点图(Scatter plot)</h4>
<p>散点图展示 <strong>两个变量之间的关系</strong> ,这种关系可能是线性或非线性的。图中每一个点的横纵坐标都分别对应两个变量各自的观测值,因此散点所反映出来的趋势也就是两个变量之间的关系。</p>
<ul>
<li>Mark: point</li>
<li>Channel: position</li>
</ul>
<p><img loading="lazy" src="images/vis_elements9.png" alt="Untitled" />
</p>
<h3 id="one-key-one-value">One key, one value</h3>
<p>假如一张表是这样的:</p>
<ul>
<li><strong>能够定位到某个表格位置的变量称作 Key</strong>,这通常是我们统计对象的某个属性,比如某个材料的名称;</li>
<li><strong>表格中某个格中具体的值被称为 Value</strong></li>
</ul>
<p><img loading="lazy" src="images/vis_elements10.png" alt="Untitled" />
</p>
<h4 id="柱状图bar-chart">柱状图(Bar chart)</h4>
<p>柱状图可能是我们最熟悉的图形之一,目前是各种统计图形中应用最广泛的,但柱状图使用统计后的数据(平均值、标准差)进行作图,所能展示的统计量比较贫乏:只能以矩形条的长度展示原始数值,对数据的分布几乎没有任何展示。</p>
<p>比如在下图中,因为展示的是每个动物的平均质量,如果不加误差线,我们既不能看出它的变异程度,也看不到这些数据的分布。</p>
<ul>
<li>Mark: lines</li>
<li>Channel: position, length</li>
</ul>
<p><img loading="lazy" src="images/vis_elements11.png" alt="Untitled" />
</p>
<h4 id="箱线图box-plot">箱线图(Box plot)</h4>
<p>统计(平均值、标准差)后画出的柱状图损失了太多信息,相比之下,直接用原始数据画出的箱线图呈现了 <strong>更详细的数据分布情况</strong> ,并允许数据中包含异常值。</p>
<ul>
<li>Mark: line, point</li>
<li>Channel: position, length</li>
</ul>
<p><img loading="lazy" src="images/vis_elements12.png" alt="Untitled" />
</p>
<h4 id="小提琴图violin-plot">小提琴图(Violin plot)</h4>
<p>小提琴图是密度曲线图的变体,有时也与箱线图一同呈现,能够更清晰地展示数据的分布情况。由于其外形与小提琴的形状相似(尤其展示双峰数据时),所以称为小提琴图。通常要较多的数据量,才能呈现出比较好看的小提琴图。</p>
<ul>
<li>Mark: line, point</li>
<li>Channel: position, length</li>
</ul>
<p><img loading="lazy" src="images/vis_elements13.png" alt="Untitled" />
</p>
<h3 id="one-ordered-key-one-value">One ordered key, one value</h3>
<p>当我们的数据,其中的 Key 有内在顺序时,其呈现方式又可以有所不同。</p>
<p><img loading="lazy" src="images/vis_elements14.png" alt="Untitled" />
</p>
<h4 id="折线图line-chart">折线图(Line chart)</h4>
<p>比方说展示一组随时间(比如年份)变化的数据,一般会用折线图的形式,更容易体现出数据的变化趋势。</p>
<ul>
<li>Mark: line, point</li>
<li>Channel: position</li>
</ul>
<p><img loading="lazy" src="images/vis_elements15.png" alt="Untitled" />
</p>
<p>各种常见的基本图片类型就讲到这,我在每张图处都表明了作图利用到的 Mark 与 Channel。比较后就会发现,各种图确实都可以抽象为作图元素的适当组合。</p>
<h2 id="常见问题">常见问题</h2>
<p>以下介绍一些常见的问题与争议,同时包含一些注意事项。篇幅有限,没法将各种问题逐一分析,如果感兴趣也可以与我深入进行交流。</p>
<h3 id="选择柱状图还是折线图">选择柱状图还是折线图</h3>
<p>这个问题一般取决于数据的类型,尤其是其中 Key 的类型。</p>
<ul>
<li>如果是<strong>类别数据</strong>,用柱状图</li>
<li>如果是<strong>有序数据</strong>,用折线图</li>
</ul>
<p>需注意,类别数据不应使用折线图来展示,而有序数据也能用柱状图表示。</p>
<p>下面两张图展示了性别与身高的关系,在这里 Key 为性别,属于类别数据,因此柱状图可以,但是折线图不合适。因为折线图天生带有一种展示趋势的倾向,会错误地传递出一种「从 Female 到 Male 身高存在上升趋势」,但一般来说性别不会逐渐变化,通常也不会出现身高随着性别变化而改变的情况。</p>
<p><img loading="lazy" src="images/vis_elements16.png" alt="Untitled" />
</p>
<p>再看下面的图,年龄与身高的关系。柱状图展示了不同年龄段的身高差别,折线图展示出从十岁到十二岁儿童身高有上升的趋势,这就非常合理了。</p>
<p><img loading="lazy" src="images/vis_elements17.png" alt="Untitled" />
</p>
<h3 id="原点是否从0开始">原点是否从0开始</h3>
<p>一个作图过程中需要注意的问题:原点是否从0开始?在不同图中这个问题存在一些争议,但是 <strong>在柱状图中,原点一定要从0开始。</strong> 我们有时也会见到,为了刻意传递出某种结论而在柱状图中不从零开始展示数据的情况。</p>
<p>回想一下前面提到柱状图的时候,我们提到<strong>柱状图利用长度来编码数值</strong>。正因如此,长度需要与数值以合适的比例关系进行对应,100 是 50 的两倍,因此在线性坐标轴上,对应 100 的柱子长度就该是 50 的两倍长。</p>
<p>而<strong>折线图利用位置来编码数值</strong>,所以在一些情况下,为了展示出更清晰的局部变化,可以不从0开始。</p>
<p><img loading="lazy" src="images/vis_elements18.png" alt="Untitled" />
</p>
<h3 id="雷达图与饼图">雷达图与饼图</h3>
<p>雷达图实际上是极坐标系中的折线图,<strong>以到中心的距离编码数值</strong>,然而其<strong>可读性非常差</strong>,绝大多数情况下避免使用。</p>
<ul>
<li>纵坐标对应的数值不易阅读</li>
<li>折线的趋势变化不明显</li>
<li>折线图不应用来表示没有顺序的分组数据</li>
<li>除非周期性数据,否则一组数据结尾和开始处的连接没有实际含义</li>
</ul>
<p><img loading="lazy" src="images/vis_elements19.png" alt="Untitled" />
</p>
<p>饼图<strong>用角度编码数值</strong>,给人的感知是角度/面积,<strong>可读性也不好</strong>。因为人眼对角度或面积辨识能力不强,尤其是相似的角度非常多的时候。</p>
<p><img loading="lazy" src="images/vis_elements20.png" alt="Untitled" />
</p>
<p>回看一下最开始放的排序图,角度与面积都排在了最后,因此请谨慎使用饼图。如果用,对数据进行排序后再画饼图,可以稍稍让读图变得轻松一点。</p>
<p><img loading="lazy" src="images/vis_elements21.png" alt="Untitled" />
</p>
<h3 id="一些奇怪的图">一些奇怪的图</h3>
<p>我翻了一些CNS文章,除了热图和树状/网状图,基本都是上面提到的图片类型。其中有些变体形式的图也非常不错,比如下面这张直接展示出原始值及其平均值,又省空间又直观。</p>
<p><img loading="lazy" src="images/vis_elements22.png" alt="Untitled" />
</p>
<p>或是下面这种(我画了个示意图),相比于柱状图省去了柱子的部分,同样呈现出平均值和误差线,又比柱状图省下不少空间(柱状图的柱子宽度几乎毫无意义)。</p>
<p><img loading="lazy" src="images/vis_elements23.png" alt="Untitled" />
</p>
<p>再举个例子,说说如何用图片元素的基本知识,帮助我们读一张图,尤其是如今日益花哨的各种组学图。下面是张呈现出转录组数据的火山图(Volcano plot),一眼看上去还挺炫,让我将其中的各种元素拆分一下:</p>
<ul>
<li>横坐标与纵坐标都是取 log 后的连续型数值,坐标轴不包括离散(分组)数据,因此这是将(x,y)映射到位置的散点图,每个点代表一个基因</li>
<li>再看横纵坐标表示的含意,横坐标是 R-NR 数据的比值,因此越靠右表明比值越大,越靠左则比值越小</li>
<li>图中的两条曲线划定了一定范围,因此作为比较数据的阈值</li>
<li>数据点还映射到了不同的颜色,灰色是没有达到阈值的数据,黑色点达到阈值,其中又有些选出的基因,被标注以不同颜色,应该是依照某个其他因素进行了分组</li>
</ul>
<p>那么,再接下去看看上下文有没有提到这些颜色代表了什么分组,就清楚它是要讲什么了。</p>
<p><img loading="lazy" src="images/vis_elements24.png" alt="Untitled" />
</p>
<p>好了,这篇文章就写到这,我们下次再见。</p>
<h2 id="参考资料与延伸阅读">参考资料与延伸阅读</h2>
<ul>
<li><a href="https://socviz.co/index.html#preface">Data Visualization - A practical introduction</a></li>
<li><a href="https://www.cs.ubc.ca/~tmm/talks.html#vad15sydney">Visualization Analysis & Design</a></li>
</ul>
]]></content:encoded>
</item>
<item>
<title>单变量异常值检测方法</title>
<link>https://yorks0n.github.io/posts/outlier_detection/outlier_detection/</link>
<pubDate>Mon, 17 Jul 2023 14:01:55 +0800</pubDate>
<guid>https://yorks0n.github.io/posts/outlier_detection/outlier_detection/</guid>
<description>整理一下检测单变量异常值的方法。</description>
<content:encoded><![CDATA[<h2 id="异常值">异常值?</h2>
<p>首先,什么是异常值,某个特别的值真的是异常值吗?</p>
<p>在统计学中,异常值(outlier)是与其他观察值显著不同的数据点。它们可能来源于测量方法的变异性,可能指示出新的数据情况,或者也可能是实验误差的产物。然而,判断某个值是否为异常值并不轻松,也没有一劳永逸的方法或是唯一可信的标准,因此这里整理几种常见的单变量异常值检测方法,以供自己或他人将来参考。</p>
<p>至于如何处理异常值,是原样保留还是剔除它们,往往会随着分析目的等因素发生变化,但请一定三思,avoid <a href="https://en.wikipedia.org/wiki/Cherry_picking">cherry-picking</a>。</p>
<h2 id="异常值检测">异常值检测</h2>
<h3 id="three-sigma-rule">three-sigma rule</h3>
<p>首先计算数据的平均值mu,与标准差std</p>
<p>three-sigma 法则将任何 $abs(x - µ) > 3 * std$ 的值作为异常值,这里的3也可以根据情况进行调整</p>
<p>这种方式与另一种Z-score方法实际是一致的:对数据进行Z-score变换,就是在计算数据的Z统计量</p>
<p>$$
z = (x-µ)/σ
$$</p>
<p>对应于three-sigma方法中,就是这里的 $abs(z) > 3$会被视为异常值。</p>
<p>这里划定异常值的系数取3,实际已经是一个非常极端的值了,我们假设Z-score变换后的数据符合标准正态分布,此时置信区间内的概率$p(|z| > 3) = 0.9973$,即数据落在这个区间外的概率只有0.0027。</p>
<p>我们假设随机来一组数据:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span><span style="color:#a6e22e">set.seed</span>(<span style="color:#ae81ff">2501</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 生成一组10个符合正态分布的随机数,µ = 100,σ = 10</span>
</span></span><span style="display:flex;"><span>random_numbers_1 <span style="color:#f92672"><-</span> <span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">rnorm</span>(<span style="color:#ae81ff">10</span>, mean <span style="color:#f92672">=</span> <span style="color:#ae81ff">100</span>, sd <span style="color:#f92672">=</span> <span style="color:#ae81ff">10</span>))
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 99 104 103 91 106 100 96 90 110 99</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 再添加几个值,分别为68,220</span>
</span></span><span style="display:flex;"><span>data_1 <span style="color:#f92672"><-</span> <span style="color:#a6e22e">c</span>(random_numbers_1, <span style="color:#ae81ff">68</span>, <span style="color:#ae81ff">220</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 99 104 103 91 106 100 96 90 110 99 68 220</span>
</span></span></code></pre></div><p>此时样本均值和标准差分别为</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span>mu_1 <span style="color:#f92672">=</span> <span style="color:#a6e22e">mean</span>(data_1)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 107.17</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sigma_1 <span style="color:#f92672">=</span> <span style="color:#a6e22e">sd</span>(data_1)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 37.13</span>
</span></span></code></pre></div><p>此时 three-sigma的区间为$mu \pm 3*sigma = (-4.23, 218.57)$,是个大到离谱的区间了。上面的数据中只有220会被这个方式定义为异常值。这个范围对应初始的 $N(100,10^2)$ 中的概率为 $1 - 9.7^{-26}$,即有真实值落在区间外的概率为$9.7^{-26}$,因此是一个区间范围非常大,非常宽松的方法。其原因一方面是选择 3 作为系数,因此可以视情况将其调整为其他值,比如 2.5 等。</p>
<p>但是,计算置信区间的方式相对比较繁琐,一种更直观的是对所有数据直接计算 z 统计量</p>
<p>$$
|z| = \left|\frac{X - \bar{X}}{s}\right|
$$</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span><span style="color:#a6e22e">abs</span>(<span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">scale</span>(data_1)[,<span style="color:#ae81ff">1</span>],<span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 0.22 0.09 0.11 0.44 0.03 0.19 0.30 0.46 0.08 0.22 1.05 3.04</span>
</span></span></code></pre></div><p>其中68的z变量为1.05,220的z变量为3.04,如果以3为标准就可以将其作为异常值。</p>
<p>另一方面,该方法本身涉及到计算平均值和标准偏差,这两个值都受异常值影响很大,尤其是标准偏差,假设上面额外增加的 220 改成 200,这种方法的区间就会成为 $(10.54, 200.45)$,这时 200 落在了边界附近,却刚好不会被视为异常值。</p>
<h3 id="iqr-方法">IQR 方法</h3>
<p>有时也称为箱线图方法,通过计算数据的分位数判断异常值,大概方式如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span>Q25 <span style="color:#f92672">=</span> <span style="color:#ae81ff">25</span>th_percentile
</span></span><span style="display:flex;"><span>Q75 <span style="color:#f92672">=</span> <span style="color:#ae81ff">75</span>th_percentile
</span></span><span style="display:flex;"><span>IQR <span style="color:#f92672">=</span> Q75 <span style="color:#f92672">-</span> Q25 <span style="color:#f92672">//</span> inter<span style="color:#f92672">-</span>quartile range
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">IF </span>(x <span style="color:#f92672"><</span> Q25 <span style="color:#f92672">-</span> <span style="color:#ae81ff">1.5</span><span style="color:#f92672">*</span>IQR) <span style="color:#a6e22e">OR </span>(Q75 <span style="color:#f92672">+</span> <span style="color:#ae81ff">1.5</span><span style="color:#f92672">*</span>IQR <span style="color:#f92672"><</span> x) THEN x is a mild outlier
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">IF </span>(x <span style="color:#f92672"><</span> Q25 <span style="color:#f92672">-</span> <span style="color:#ae81ff">3.0</span><span style="color:#f92672">*</span>IQR) <span style="color:#a6e22e">OR </span>(Q75 <span style="color:#f92672">+</span> <span style="color:#ae81ff">3.0</span><span style="color:#f92672">*</span>IQR <span style="color:#f92672"><</span> x) THEN x is an extreme outlier
</span></span></code></pre></div><p>IQR 方法的好处在于,去掉异常值前后,其他值的中位数和 IQR 基本是不变的。</p>
<p>同样用上面的数据,计算一下这种方式的区间:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span><span style="color:#a6e22e">quantile</span>(data_1, probs <span style="color:#f92672">=</span> <span style="color:#a6e22e">c</span>(<span style="color:#ae81ff">0.25</span>,<span style="color:#ae81ff">0.75</span>))
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Q25 = 94.75</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Q75 = 104.50</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># IQR = 9.75</span>
</span></span></code></pre></div><p>此时1.5倍IQR的区间为,相对还是个比较窄的范围,后添加进去的 68 和 220 在此情况下都会被视为异常值。</p>
<p>$$
(Q25-1.5\cdot IQR, Q75+1.5\cdot IQR) = (80.12, 119.12)
$$</p>
<p>3 倍 IQR 的范围则为 $(65.5, 133.75)$ 相对会宽一些,此时只有220会被作为异常值。</p>
<p>$$
(Q25-3\cdot IQR, Q75+3\cdot IQR) = (65.5, 133.75)
$$</p>
<p><strong>这种方式的一个好处就是分位数对异常值不太敏感</strong>,如果把220变为200,这个区间依然是不会变的。</p>
<p>1.5 倍 IQR 的区间$(80.12, 119.12)$放到原始的分布中去大致对应于概率 95% 的正态区间;3 倍 IQR 的区间对应 99.94% 的概率,真实值落入这个区间外的概率也足够低了;当然相比于 three-sigma,还是严格了一些。</p>
<p>因此,IQR 方法更容易检测出异常值,当然也更有可能把真实值视为异常值,因此需要谨慎选择 IQR 区间的倍数。</p>
<h2 id="中位数绝对偏差-mad">中位数绝对偏差 MAD</h2>
<p>上面的方法可能会遇到一些问题,包括</p>
<ul>
<li>均值和标准差的计算依赖于所有数值,因此离群值可能会对均值和标准差产生较大影响</li>
<li>three-sigma、IQR等方法对于小样本不一定合适(?</li>
</ul>
<p>还有另一种可以衡量变异程度的统计量,中位数绝对偏差(Median absolute deviation,MAD)</p>
<p>其计算也相当简便:</p>
<p>对于一组数据$X_1, X_2, X_3, …, X_n$ ,其中位数为$\tilde{X} = median(X)$</p>
<p>MAD是各个数据与其中位数绝对偏差的中位数:</p>
<p>$$
MAD = median(|X_i - \tilde{X}|)
$$</p>
<p>MAD 相比方差更能适应数据集中的异常值:在标准偏差中,与均值的距离是平方的,因此大偏差的权重更大,因此离群值会严重影响它;在 MAD 中,由于中位数天生受异常值影响较小,少数异常值的偏差几乎不影响MAD。</p>
<p>上面 three-sigma 使用标准方差 σ 衡量变异程度,IQR 方法使用 IQR 衡量变异,类似的,在这里使用 MAD 衡量数据的变异幅度。MAD 的使用方式类似Z-score变换中将表标准差应用于平均值的过程,为了将 MAD 作为与标准偏差 σ 一致的估计量,需要有</p>
<p>$$
\hat{\sigma} = k \cdot MAD
$$</p>
<p>其中 k 是一个比例系数,取决于分布。对于正态分布的数据,k 被认为是</p>
<p>$$
k = 1/\Phi^{-1}(3/4) \approx 1.4826
$$</p>
<p>即标准正态分布$Z = (X-\mu)/\sigma$的分位数函数$\Phi^{-1}$(也称累积分布函数,R中计算使用 qnorm)的倒数。</p>
<p>用一开始的数据计算</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-R" data-lang="R"><span style="display:flex;"><span><span style="color:#75715e"># 99 104 103 91 106 100 96 90 110 99 68 220</span>
</span></span><span style="display:flex;"><span>mad_1 <span style="color:#f92672"><-</span> <span style="color:#a6e22e">median</span>(<span style="color:#a6e22e">abs</span>(data_1 <span style="color:#f92672">-</span> <span style="color:#a6e22e">median</span>(data_1)))
</span></span><span style="display:flex;"><span><span style="color:#75715e"># mad_1 = 5.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">abs</span>((data_1 <span style="color:#f92672">-</span> <span style="color:#a6e22e">median</span>(data_1)) <span style="color:#f92672">/</span> (mad_1<span style="color:#f92672">*</span><span style="color:#ae81ff">1.4826</span>)), <span style="color:#ae81ff">2</span>)
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">0.06</span> <span style="color:#ae81ff">0.55</span> <span style="color:#ae81ff">0.43</span> <span style="color:#ae81ff">1.04</span> <span style="color:#ae81ff">0.80</span> <span style="color:#ae81ff">0.06</span> <span style="color:#ae81ff">0.43</span> <span style="color:#ae81ff">1.17</span> <span style="color:#ae81ff">1.29</span> <span style="color:#ae81ff">0.06</span> <span style="color:#ae81ff">3.86</span> <span style="color:#ae81ff">14.78</span>
</span></span></code></pre></div><p>类似,视情况选择合适的值作为阈值,即可检出异常值。如果选择 3,对应的区间为 (75.04, 123.96),概率范围为98.5%;如果选择 4,对应的区间为 (58.73, 140.27),概率范围为99.89%。</p>
<p>k 的推导过程如下</p>
<p>为了使 $\pm k*MAD$ 的区间落在概率为 $p_0$ 的标准正态分布中区间内,有以下公式</p>
<p>$$
p_0 = P(|X - µ| \le k \cdot MAD) = P\left( \left|\frac{X - µ}{σ}\right| \le \frac{k \cdot MAD}{σ}\right) = P\left( |Z| \le \frac{k \cdot MAD}{σ} \right)
$$</p>
<p>因此就有</p>
<p>$$
\Phi(k \cdot MAD/\sigma) - \Phi(-k \cdot MAD/\sigma) = p_0
$$</p>
<p>又因为</p>
<p>$$
\Phi(-k\cdot MAD/\sigma)=1-\Phi(k \cdot MAD/\sigma)
$$</p>
<p>所以</p>
<p>$$
\Phi(k \cdot MAD/\sigma)= \frac{1 + p_0}{2}
$$</p>
<p>令 $p_0 = 0.5$,即 50% 正态分布区间时,</p>
<p>$$
k = \frac{\sigma}{MAD} = \frac{1}{\Phi^{-1}(3/4)} \approx 1.4826
$$</p>
<p>这里选择 0.5 是因为,对于均值为零的对称分布,总体 MAD 是分布的第 75 个百分位数,因此标准正态分布的总体 MAD 刚好等于$\Phi^{-1}(3/4)$。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>
<p><a href="https://stackoverflow.com/questions/2303510/recommended-anomaly-detection-technique-for-simple-one-dimensional-scenario">Recommended anomaly detection technique for simple, one-dimensional scenario?</a></p>
</li>
<li>
<p><a href="https://towardsdatascience.com/5-ways-to-detect-outliers-that-every-data-scientist-should-know-python-code-70a54335a623">5 Ways to Detect Outliers/Anomalies That Every Data Scientist Should Know </a></p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Median_absolute_deviation">Median absolute deviation</a></p>
</li>
</ul>
]]></content:encoded>
</item>
<item>
<title>复现一张相关性图</title>
<link>https://yorks0n.github.io/posts/corplot_case/</link>
<pubDate>Tue, 30 May 2023 09:24:00 +0800</pubDate>
<guid>https://yorks0n.github.io/posts/corplot_case/</guid>
<description><p>前几天看到群里有人问如何画一张类似下图的、带拟合线与误差的相关性图,这里找点数据来演示一下怎么画。</p>
<p><img loading="lazy" src="images/corplot1.png" alt="demo" />
找了点数字画了个例子,假设数据读进来是这样的两列:</p>
<p><img loading="lazy" src="images/corplot2.png" alt="Untitled" />
</p>
<p>绘图的部分我用了这些</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-r" data-lang="r"><span style="display:flex;"><span>leg <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">theme</span>(title<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">15</span>),
</span></span><span style="display:flex;"><span> axis.text.x<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> axis.text.y<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> legend.text<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>))
</span></span><span style="display:flex;"><span>label_text <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">paste0</span>(<span style="color:#e6db74">&#34;r = &#34;</span>, <span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">cor</span>(subdata<span style="color:#f92672">$</span>Na,subdata<span style="color:#f92672">$</span>K),<span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">library</span>(ggplot2)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ggplot</span>(subdata, <span style="color:#a6e22e">aes</span>(x <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(Na), y <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(K))) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">geom_point</span>(size <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>,color <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;#31705a&#34;</span>) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">stat_smooth</span>(method <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;lm&#34;</span>, color <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;#31705a&#34;</span>)<span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">theme_classic</span>() <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">annotate</span>(<span style="color:#e6db74">&#34;text&#34;</span>, x<span style="color:#f92672">=</span><span style="color:#ae81ff">5.8</span>, y<span style="color:#f92672">=</span><span style="color:#ae81ff">9</span>, label<span style="color:#f92672">=</span> label_text) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> leg
</span></span></code></pre></div><p>以上代码画出来的图如下:
<img loading="lazy" src="images/corplot3.png" alt="Untitled" />
</p>
<p>其中各部分的解释如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-r" data-lang="r"><span style="display:flex;"><span>leg <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">theme</span>(title<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">15</span>),
</span></span><span style="display:flex;"><span> axis.text.x<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> axis.text.y<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> legend.text<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>)) <span style="color:#75715e"># 用于指定字体大小等,可以先忽略</span>
</span></span><span style="display:flex;"><span>label_text <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">paste0</span>(<span style="color:#e6db74">&#34;r = &#34;</span>, <span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">cor</span>(subdata<span style="color:#f92672">$</span>Na,subdata<span style="color:#f92672">$</span>K),<span style="color:#ae81ff">2</span>)) <span style="color:#75715e"># 用于计算相关系数r的值,并处理成标在图上的「r = xxx」的文本</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">library</span>(ggplot2) <span style="color:#75715e"># 用ggplot2包画图</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ggplot</span>(subdata, <span style="color:#a6e22e">aes</span>(x <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(Na), y <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(K))) <span style="color:#f92672">+</span> <span style="color:#75715e">#指定画图的数据subdata,在aes(x = ,y = )中指定横纵坐标轴</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">geom_point</span>(size <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>,color <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;#31705a&#34;</span>) <span style="color:#f92672">+</span> <span style="color:#75715e">#绘制散点部分,点的大小为2,颜色为浅绿色</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">stat_smooth</span>(method <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;lm&#34;</span>, color <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;#31705a&#34;</span>)<span style="color:#f92672">+</span> <span style="color:#75715e">#绘制拟合线部分,method = &#34;lm&#34; 使用线性拟合,颜色同前</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">theme_classic</span>() <span style="color:#f92672">+</span> <span style="color:#75715e"># 使用ggplot的classic画图主题,是个只显示横纵坐标轴的主题</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">annotate</span>(<span style="color:#e6db74">&#34;text&#34;</span>, x<span style="color:#f92672">=</span><span style="color:#ae81ff">5.8</span>, y<span style="color:#f92672">=</span><span style="color:#ae81ff">9</span>, label<span style="color:#f92672">=</span> label_text) <span style="color:#f92672">+</span> <span style="color:#75715e"># 添加上文字标注,类型是&#34;text&#34;,位置在x=5.8,y=9</span>
</span></span><span style="display:flex;"><span> leg <span style="color:#75715e"># 添加之前指定的字体大小部分,不关键</span>
</span></span></code></pre></div></description>
<content:encoded><![CDATA[<p>前几天看到群里有人问如何画一张类似下图的、带拟合线与误差的相关性图,这里找点数据来演示一下怎么画。</p>
<p><img loading="lazy" src="images/corplot1.png" alt="demo" />
找了点数字画了个例子,假设数据读进来是这样的两列:</p>
<p><img loading="lazy" src="images/corplot2.png" alt="Untitled" />
</p>
<p>绘图的部分我用了这些</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-r" data-lang="r"><span style="display:flex;"><span>leg <span style="color:#f92672"><-</span> <span style="color:#a6e22e">theme</span>(title<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">15</span>),
</span></span><span style="display:flex;"><span> axis.text.x<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> axis.text.y<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> legend.text<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>))
</span></span><span style="display:flex;"><span>label_text <span style="color:#f92672"><-</span> <span style="color:#a6e22e">paste0</span>(<span style="color:#e6db74">"r = "</span>, <span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">cor</span>(subdata<span style="color:#f92672">$</span>Na,subdata<span style="color:#f92672">$</span>K),<span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">library</span>(ggplot2)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ggplot</span>(subdata, <span style="color:#a6e22e">aes</span>(x <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(Na), y <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(K))) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">geom_point</span>(size <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>,color <span style="color:#f92672">=</span> <span style="color:#e6db74">"#31705a"</span>) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">stat_smooth</span>(method <span style="color:#f92672">=</span> <span style="color:#e6db74">"lm"</span>, color <span style="color:#f92672">=</span> <span style="color:#e6db74">"#31705a"</span>)<span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">theme_classic</span>() <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">annotate</span>(<span style="color:#e6db74">"text"</span>, x<span style="color:#f92672">=</span><span style="color:#ae81ff">5.8</span>, y<span style="color:#f92672">=</span><span style="color:#ae81ff">9</span>, label<span style="color:#f92672">=</span> label_text) <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span> leg
</span></span></code></pre></div><p>以上代码画出来的图如下:
<img loading="lazy" src="images/corplot3.png" alt="Untitled" />
</p>
<p>其中各部分的解释如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-r" data-lang="r"><span style="display:flex;"><span>leg <span style="color:#f92672"><-</span> <span style="color:#a6e22e">theme</span>(title<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">15</span>),
</span></span><span style="display:flex;"><span> axis.text.x<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> axis.text.y<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>),
</span></span><span style="display:flex;"><span> legend.text<span style="color:#f92672">=</span><span style="color:#a6e22e">element_text</span>(size<span style="color:#f92672">=</span><span style="color:#ae81ff">14</span>)) <span style="color:#75715e"># 用于指定字体大小等,可以先忽略</span>
</span></span><span style="display:flex;"><span>label_text <span style="color:#f92672"><-</span> <span style="color:#a6e22e">paste0</span>(<span style="color:#e6db74">"r = "</span>, <span style="color:#a6e22e">round</span>(<span style="color:#a6e22e">cor</span>(subdata<span style="color:#f92672">$</span>Na,subdata<span style="color:#f92672">$</span>K),<span style="color:#ae81ff">2</span>)) <span style="color:#75715e"># 用于计算相关系数r的值,并处理成标在图上的「r = xxx」的文本</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">library</span>(ggplot2) <span style="color:#75715e"># 用ggplot2包画图</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ggplot</span>(subdata, <span style="color:#a6e22e">aes</span>(x <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(Na), y <span style="color:#f92672">=</span> <span style="color:#a6e22e">log</span>(K))) <span style="color:#f92672">+</span> <span style="color:#75715e">#指定画图的数据subdata,在aes(x = ,y = )中指定横纵坐标轴</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">geom_point</span>(size <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>,color <span style="color:#f92672">=</span> <span style="color:#e6db74">"#31705a"</span>) <span style="color:#f92672">+</span> <span style="color:#75715e">#绘制散点部分,点的大小为2,颜色为浅绿色</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">stat_smooth</span>(method <span style="color:#f92672">=</span> <span style="color:#e6db74">"lm"</span>, color <span style="color:#f92672">=</span> <span style="color:#e6db74">"#31705a"</span>)<span style="color:#f92672">+</span> <span style="color:#75715e">#绘制拟合线部分,method = "lm" 使用线性拟合,颜色同前</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">theme_classic</span>() <span style="color:#f92672">+</span> <span style="color:#75715e"># 使用ggplot的classic画图主题,是个只显示横纵坐标轴的主题</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">annotate</span>(<span style="color:#e6db74">"text"</span>, x<span style="color:#f92672">=</span><span style="color:#ae81ff">5.8</span>, y<span style="color:#f92672">=</span><span style="color:#ae81ff">9</span>, label<span style="color:#f92672">=</span> label_text) <span style="color:#f92672">+</span> <span style="color:#75715e"># 添加上文字标注,类型是"text",位置在x=5.8,y=9</span>
</span></span><span style="display:flex;"><span> leg <span style="color:#75715e"># 添加之前指定的字体大小部分,不关键</span>
</span></span></code></pre></div>]]></content:encoded>
</item>
<item>
<title>【读文献】单细胞分析最佳实践</title>
<link>https://yorks0n.github.io/posts/read-single-cell-best-practice/</link>
<pubDate>Fri, 07 Apr 2023 00:00:00 +0000</pubDate>
<guid>https://yorks0n.github.io/posts/read-single-cell-best-practice/</guid>
<description>Best practices for single-cell analysis across modalities</description>
<content:encoded><![CDATA[<h1 id="best-practices-for-single-cell-analysis-across-modalities">Best practices for single-cell analysis across modalities</h1>
<p>跨模式单细胞分析的最佳实践,于 2023 年 3 月 31 日发表于 nature reviews genetics。最近在研究单细胞分析相关的资料,因此记下这篇笔记。文章中包含了 Transcriptome、Chromatin accessibility、Surface protein expression、空间转录组等多个方面,但就近期关注点而言,只精简记录单细胞转录组部分的笔记。如果有需要,可以<a href="https://www.nature.com/articles/s41576-023-00586-w">阅读原文</a>。</p>
<hr>
<p><img loading="lazy" src="images/1680845994088.png" alt="" />
</p>
<h2 id="从原始计数矩阵到高质量单细胞数据">从原始计数矩阵到高质量单细胞数据</h2>
<p>原始计数矩阵是直接从测序数据定量得来的矩阵,一般根据每个细胞 barcode 与 UMI 确定细胞中各种 mRNA 的数量,然而原始数据中包含着各种系统噪声和误差(比如来自建库操作、样本质量、测序批次等),因此在这一预处理过程中试图去除误差。这一过程涉及到<strong>质量控制</strong>、<strong>归一化</strong>、<strong>数据矫正</strong>和<strong>特征选择</strong>等过程,还可能会涉及到消除批次效应以<strong>整合多批次数据</strong>的过程。</p>
<h3 id="滤低质量细胞和噪声校正-filtering-low-quality-cells-and-noise-correction">滤低质量细胞和噪声校正 Filtering low-quality cells and noise correction</h3>
<p>大多数分析过程假定每个单细胞液滴中都包含来自单个完整细胞的 RNA。细胞质量差(比如死细胞)、溶液中破裂细胞产生的 RNA 污染、同时捕获多个细胞的情况违反这一假设,因此需要在进一步分析之前去除这部分数据。</p>
<ul>
<li>
<p><strong>低质量细胞</strong>:检测基因数量过少、深度过低或是线粒体计数(植物还有叶绿体)过多的细胞都被认为是低质量细胞。一般通过手动设置的阈值来进行识别和过滤,可以参照<a href="https://doi.org/10.15252%2Fmsb.20188746">先前文章</a>或是基于中值绝对偏差数对样本进行过滤。需要综合考虑过滤的指标,同时注意<strong>质量控制是在样本水平上进行的</strong>,因为样本之间的阈值可能会有很大差异。</p>
<ul>
<li>
<p>中值绝对偏差数(Median Absolute Deviation, MAD)</p>
<p>基于中值绝对偏差数(Median Absolute Deviation, MAD)的样本自动过滤是一种统计方法,用于识别和剔除数据集中的离群值。这种方法主要有以下几个步骤:</p>
<ol>
<li>计算数据集的中位数(median)。</li>
<li>对每个数据点,计算其与中位数的偏差绝对值。</li>
<li>计算这些绝对偏差的中位数,得到MAD值。</li>
<li>选择一个阈值,通常是MAD的某个倍数(例如3倍MAD),用于判断数据点是否为离群值。</li>
<li>剔除大于阈值的数据点,即被认为是离群值的数据点。</li>
</ol>
<p>这种基于MAD的方法对于处理非正态分布的数据集具有较好的鲁棒性,而且相对标准差受异常值影响更小,因此可以有效地识别和过滤异常值。</p>
</li>
</ul>
</li>
<li>
<p><strong>无细胞游离 RNA</strong>:通常存在于溶液中,并在建库过程中分配给每个细胞,产生一种类似背景值的效果。如果是细胞类群特异性标志物的 RNA 污染了其他细胞,后续分析中可能将不同的细胞群混合在一起。流行的 SoupX 等方法根据样本中「空液滴」的表达情况去除污染;CellBender 将环境 RNA 的去除制定为无监督贝叶斯模型,该模型不需要细胞类型特异性基因表达谱的先验知识。应当尽量不忽略去除环境 RNA 这一步,以提升下游分析的质量。</p>
</li>
<li>
<p><strong>空液滴和双联体液滴</strong>(包含两个细胞的液滴):由不同细胞类型形成的双联体很难注释,并可能导致细胞类型标注错误。常见的双峰检测方法随机抽取两个细胞组合成模拟双联体,并将其与其他细胞测量结果进行比较。scDblFinder 采用此方法, 并在多项测试中准确性和计算效率优于其他方法。还可以应用多种不同检测方法并比较结果以提高准确性。</p>
</li>
</ul>
<h3 id="归一化和方差稳定化-normalization-and-variance-stabilization">归一化和方差稳定化 Normalization and variance stabilization</h3>
<p>不同细胞的 mRNA 含量不同,加上测序过程随机误差,将导致细胞有不同的初始计数。归一化可以矫正测序深度的差异,使不同细胞具有可比性。随后稳定方差以确保少数离群值对整体数据影响不会太大。</p>
<p><strong>shifted logarithm transformation</strong>:使用缩放系数s的 log(𝑦/𝑠+1) 表现良好,但不应使用 CPM(counts per million)作为输入。</p>
<blockquote>
<p>should not be used with counts per million as an input, as it reflects an unrealistically large overdispersion.
原文如此,但没看懂为什么,待补充。</p></blockquote>
<p>通过使用一个公因数缩放所有基因,会假定由于细胞大小导致的测序深度差异可以忽略不计。然而对于异质性较大的数据集,其中包含了特性不同的各种细胞,其本身基因表达差异就较大,难以对每个基因找到一个合适所有细胞的系数。</p>
<p>此外还有 <strong>Scran</strong> 标准化和 <strong>Pearson</strong> 残差方法。shifted logarithm transformation 被证明可以更好地稳定方差以进行后续降维,Scran 在批量校正任务中表现良好,Pearson 残差更适合生物变异基因的选择和稀有细胞身份的识别。</p>
<h3 id="消除混杂的变异来源-removing-confounding-sources-of-variation">消除混杂的变异来源 Removing confounding sources of variation</h3>
<p>混杂变异可以来源于技术和生物,并且应该分开处理。</p>
<ul>
<li><strong>批次效应</strong>:多个样本的数据集可能会由于技术产生的批次效应,需要恰当的数据整合方法。canonical correlation analysis 与 Harmony 等线性嵌入模型在处理不同批次的简单整合上表现良好;在复杂任务重, atlas、scANVI、scVI、scGen等深度学习方法,及 Scanorama 等显性嵌入模型表现较好。scIB 包可用于使用上述基准的评估指标来评估整合效果。</li>
<li><strong>生物因素</strong>:细胞周期等因素同样会引入误差,使得细胞间差异来源于细胞周期而非细胞类型。可以使用 Scanpy 或 Seurat 中内置的细胞周期标记与校正函数作为基础;Tricycle 等更复杂的方法对细胞异质性高的数据集表现良好。</li>
</ul>
<h3 id="选择信息特征和降维-selecting-informative-features-and-reducing-dimensionality">选择信息特征和降维 Selecting informative features and reducing dimensionality</h3>
<p>理想情况下,特征选择方法应优先选择在亚群间(而非亚群内)变化的基因,并且不影响亚群内更小亚群的可识别性。</p>
<p>通过拟合基因模型获得 deviance 来识别信息量大的基因,模型假定所有细胞基因表达一致,然后检验哪些基因违反假设,从而选择基因。此外,按此方法利用了原始计数对基因进行排序,不依赖归一化,不会因为数值中零值过多产生误差(zero inflation)。选择特征后可以通过 PCA 等方式进一步降低数据集维度,同时 PCA 结果可以进一步作为输入应用 t-SNE、UMAP、PHATE 等可视化降维方法。注意,最近有研究表明,仅仅依靠2D嵌入可能会导致对细胞之间关系的错误解读。因此,结果不应仅基于对结果的视觉检查来制定,而应结合其他定量评估。</p>
<h2 id="为细胞集群确定亚群身份-from-clusters-to-cell-identities">为细胞集群确定亚群身份 From clusters to cell identities</h2>
<h3 id="从单个细胞到集群-from-single-cells-to-clusters">从单个细胞到集群 From single cells to clusters</h3>
<p>识别细胞群的第一步是将表达模式相似的细胞聚类成组,推荐使用 Leiden 算法进行聚类。</p>
<h3 id="为细胞集群确定细胞身份-mapping-cell-clusters-to-cell-identities">为细胞集群确定细胞身份 Mapping cell clusters to cell identities</h3>
<p>建议分三步进行:自动注释 → 手动注释 → 验证。</p>
<ul>
<li><strong>自动注释</strong>:分为基于分类器的方法和基于参考数据集的映射方法。分类器可以参考 CellTypist 和 Clustifyr,两者利用先前注释过的数据集进行训练并参考大量基因进行分类;映射方法有 scArches、Symphony 或 Azimuth。这两类方法都依赖于参考数据的质量+模型质量+参考数据集对新数据的适用性。</li>
<li><strong>手动注释</strong>:利用细胞群的特异性标志基因进行注释。</li>
<li><strong>验证</strong>:需要仔细设计验证试验,并且对于复杂数据或稀有亚群,可能没有资料可供参考。</li>
</ul>
<h3 id="伪时间分析从离散状态到连续过程-from-discrete-states-to-continuous-processes">伪时间分析——从离散状态到连续过程 From discrete states to continuous processes</h3>
<p>根据细胞表达模式的相似性,沿着轨迹对细胞进行排序的模型被称为轨迹推断或伪时间分析。这类分析的效果取决于数据集中存在的轨迹类型,Slingshot 对简单结构表现良好,PAGA 和 RaceID/StemID 对复杂结构表现更好,建议使用 <a href="http://guidelines.dynverse.org/">dynguidelines</a> 选择适用的方法。当预期结构未知时,应采用不同假设通过多种方法确认轨迹和假设。推断出的轨迹不一定有生物学意义。</p>
<p>利用 RNA velocity (mRNA 剪切、未剪切、降解的比例),比如考虑模型剪接动力学的 <strong>velocyto</strong> 和 <strong>scVelo</strong>,可以根据未剪接和剪接片段确定 RNA velocity:如果一个基因被激活了,未剪切的 RNA 会先于剪切的 RNA 产生。获得的 RNA velocity 输入 <strong>CellRank</strong> 来估计细胞命运。</p>
<p><strong>谱系追踪</strong>根据细胞中观察到的变异(例如自然发生的基因突变)来推断其谱系模型,判断克隆群体中的细胞分裂历史,可以使用 <strong>Cassiopeia</strong> 提供的多种算法进行分析,建议对比多种算法的结果。同时还有能处理更加复杂谱系的 <strong>LineageOT</strong> 工具,以及追踪静态条形码的 <strong>CoSpar</strong>。</p>
<h2 id="揭示机制-revealing-mechanisms">揭示机制 Revealing mechanisms</h2>
<p>对数据进行注释后,可选的分析变得多样,根据研究问题选择要进行的后续分析。</p>
<h3 id="差异基因表达分析-differential-gene-expression-analysis">差异基因表达分析 Differential gene expression analysis</h3>
<p>目前单细胞的差异表达分析从两个思路进行:</p>
<ul>
<li><strong>样本层面的方法</strong>:将单个样本中的多个细胞细胞读数合并,生成人工合成的批量样本,再利用常规的转录组差异表达分析工具,比如 edgeR、DESeq2 或是 limma。</li>
<li><strong>细胞层面的方法</strong>:利用广义混合效应模型(比如 MAST),对单个细胞进行建模。</li>
</ul>
<p>各个工具之间的一致性和稳健性较低,但相对而言,<strong>为常规 DEG 设计的工具表现良好</strong>,而单细胞特异性的方法通常会低估基因表达的变异程度,因此倾向于将高表达的基因错误标记为差异表达基因。因此推荐 limma、edgeR 或 DESeq2。</p>
<h3 id="基因集富集分析-gene-set-enrichment-analysis">基因集富集分析 Gene set enrichment analysis</h3>
<p>常见的数据库包括 MSigDB、Gene Ontology、KEGG 与 Reactome 等,此外还有用于癌症信号通路的 PROGENy 和转录因子的 DoRothEA。一般采用超几何分布检验、GSEA 或 GSVA 进行富集分析。研究发现富集分析对基因集的选择敏感性大于统计方法,因此应当谨慎选择数据库。decoupleR 等框架提供了多个不同数据库的访问方法。为常规转录组开发的方法可以用于单细胞数据,但一些基于单细胞的方法(比如 Pagoda2),效果可能优于前者。</p>
<h3 id="检测细胞组成的变化-deciphering-changes-in-cell-composition">检测细胞组成的变化 Deciphering changes in cell composition</h3>
<p>成分分析比较的是不同细胞类型的相对丰度。单变量统计模型,比如泊松回归或是 Wilcoxon 秩和检验可能将部分细胞类型误报为有显著差异,而实际只是样本组成引起的假象。有专门为单细胞设计的 scDC、scCODA 和 tascCODA 测试方法。</p>
<h3 id="推断扰动效应-inferring-perturbation-effects">推断扰动效应 Inferring perturbation effects</h3>
<p>利用细胞培养体系,同时进行大规模多重条件处理细胞的方法称为扰动(perturbations),perturb-seq 或 CROP-seq 等最新技术允许使用多模态数据、全基因组扰动和组合扰动对混池 CRISPR–Cas9 筛选进行分析。</p>
<h3 id="细胞通讯分析-communication-events-across-cells">细胞通讯分析 Communication events across cells</h3>
<p>细胞通讯分析利用配体、受体及其相互作用的数据库来预测细胞间相互作用。推荐使用 LIANA,它提供了多种方法和数据库。此外还有CellChat、CellPhoneDB、SingleCellSignalR;提供细胞内活动的补充估计的 Nichenet 和 Cytotalk。</p>
<h2 id="原文">原文</h2>
<p>原文中还有关于单细胞 ATAC、单细胞蛋白组、空间转录组等内容,但目前还不太关注,就先不记录了。如果有需要还请阅读原文。</p>
<p><a href="https://www.nature.com/articles/s41576-023-00586-w">Best practices for single-cell analysis across modalities</a></p>
]]></content:encoded>
</item>
<item>
<title>Docker 打包 Shiny App</title>
<link>https://yorks0n.github.io/posts/docker-pack-shiny-app/</link>
<pubDate>Tue, 28 Mar 2023 09:30:49 +0000</pubDate>
<guid>https://yorks0n.github.io/posts/docker-pack-shiny-app/</guid>
<description>介绍了如何将 Shiny App 打包成 Docker 格式,方便在不同设备环境下托管</description>
<content:encoded><![CDATA[<h2 id="文件准备">文件准备</h2>
<p>将Shiny用到的<code>server.R</code>和<code>ui.R</code>一起放在<code>app</code>文件夹中</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./app:
</span></span><span style="display:flex;"><span>server.R ui.R
</span></span></code></pre></div><p>写个<code>Dockerfile</code>,把用到的包都写在 <code>install.r</code> 后面</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>FROM rocker/r-base:latest
</span></span><span style="display:flex;"><span>LABEL maintainer<span style="color:#f92672">=</span><span style="color:#e6db74">"email <[email protected]>"</span>
</span></span><span style="display:flex;"><span>RUN apt-get update <span style="color:#f92672">&&</span> apt-get install -y --no-install-recommends <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> sudo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libcurl4-gnutls-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libcairo2-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libxt-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libssl-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libssh2-1-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> <span style="color:#f92672">&&</span> rm -rf /var/lib/apt/lists/*
</span></span><span style="display:flex;"><span>RUN install.r shiny shinyBS dplyr stringr
</span></span><span style="display:flex;"><span>RUN echo <span style="color:#e6db74">"local(options(shiny.port = 3838, shiny.host = '0.0.0.0'))"</span> > /usr/lib/R/etc/Rprofile.site
</span></span><span style="display:flex;"><span>RUN addgroup --system app <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> <span style="color:#f92672">&&</span> adduser --system --ingroup app app
</span></span><span style="display:flex;"><span>WORKDIR /home/app
</span></span><span style="display:flex;"><span>COPY app .
</span></span><span style="display:flex;"><span>RUN chown app:app -R /home/app
</span></span><span style="display:flex;"><span>USER app
</span></span><span style="display:flex;"><span>EXPOSE <span style="color:#ae81ff">3838</span>
</span></span><span style="display:flex;"><span>CMD <span style="color:#f92672">[</span><span style="color:#e6db74">"R"</span>, <span style="color:#e6db74">"-e"</span>, <span style="color:#e6db74">"shiny::runApp('/home/app')"</span><span style="color:#f92672">]</span>
</span></span></code></pre></div><p>按以下方式进行构建</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># name of the image</span>
</span></span><span style="display:flex;"><span>export IMAGE<span style="color:#f92672">=</span><span style="color:#e6db74">"yorks0n/trxiv_shiny"</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># build image</span>
</span></span><span style="display:flex;"><span>docker build -t $IMAGE .
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># run and test locally</span>
</span></span><span style="display:flex;"><span>docker run -p 8080:3838 $IMAGE
</span></span></code></pre></div><p>居然就行了!</p>
<h2 id="部署在-railway-上">部署在 Railway 上</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 安装</span>
</span></span><span style="display:flex;"><span>brew install railway
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 登录</span>
</span></span><span style="display:flex;"><span>railway login
</span></span></code></pre></div><p>创建一个项目,并输入项目名称</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>➜ railway init
</span></span><span style="display:flex;"><span>> Project Name TRxiv_shiny
</span></span><span style="display:flex;"><span>Created project TRxiv_shiny on Personal
</span></span></code></pre></div><p>虽然当前目录下有个 <code>Dockerfile</code> 了,但是这 XX 的 railway 部署时候端口和默认 docker 的不太一样,或者我没找到哪里设置,所以如果要部署在 railway 上,还需要改一下 <code>Dockerfile</code>。虽然在这指定了<code>ENV PORT=3838</code>,但实际上 railway 每次开的端口都不太一样,所以标准 docker 的写法不太行。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>FROM rocker/r-base:latest
</span></span><span style="display:flex;"><span>LABEL maintainer<span style="color:#f92672">=</span><span style="color:#e6db74">"Yorkson <[email protected]>"</span>
</span></span><span style="display:flex;"><span>RUN apt-get update <span style="color:#f92672">&&</span> apt-get install -y --no-install-recommends <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> sudo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libcurl4-gnutls-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libcairo2-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libxt-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libssl-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> libssh2-1-dev <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> <span style="color:#f92672">&&</span> rm -rf /var/lib/apt/lists/*
</span></span><span style="display:flex;"><span>RUN install.r shiny shinyBS dplyr stringr
</span></span><span style="display:flex;"><span>RUN addgroup --system app <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> <span style="color:#f92672">&&</span> adduser --system --ingroup app app
</span></span><span style="display:flex;"><span>WORKDIR /home/app
</span></span><span style="display:flex;"><span>COPY app .
</span></span><span style="display:flex;"><span>RUN chown app:app -R /home/app
</span></span><span style="display:flex;"><span>USER app
</span></span><span style="display:flex;"><span>EXPOSE <span style="color:#ae81ff">3838</span>
</span></span><span style="display:flex;"><span>ENV PORT<span style="color:#f92672">=</span><span style="color:#ae81ff">3838</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 研究后发现,这里以获取PORT的方式,才能在 Railway 中部署</span>
</span></span><span style="display:flex;"><span>CMD <span style="color:#f92672">[</span><span style="color:#e6db74">"R"</span>, <span style="color:#e6db74">"-e"</span>, <span style="color:#e6db74">"shiny::runApp('/home/app', host = '0.0.0.0', port=as.numeric(Sys.getenv('PORT')))"</span><span style="color:#f92672">]</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>railway up
</span></span></code></pre></div><h2 id="参考资料">参考资料</h2>
<p><a href="https://blog.csdn.net/douerw/article/details/121256642">在Windows系统下,基于WLS从零开始部署我的 R shiny项目到 Docker 中,并将镜像发布到 Docker Hub(内附详细操作流程)_douerw的博客-CSDN博客</a></p>
<p><a href="https://hosting.analythium.io/dockerizing-shiny-applications/">Dockerizing Shiny Applications</a></p>
<p><a href="https://hosting.analythium.io/dockerized-shiny-apps-with-dependencies/">Dockerized Shiny Apps with Dependencies</a></p>
]]></content:encoded>
</item>
<item>
<title>部署预印本追踪 TRxiv 到 Github Action</title>
<link>https://yorks0n.github.io/posts/deploy-trxiv-github-action/</link>
<pubDate>Fri, 24 Mar 2023 16:30:23 +0000</pubDate>
<guid>https://yorks0n.github.io/posts/deploy-trxiv-github-action/</guid>
<description><p>将JS写的抓取 Altmetric 热门预印本,与 bioRxiv API 结合后储存在 csv 文件中的过程,部署在 Github Action 中。官方文档写的顺序都有点混乱,网上找到的教程又经常都比较老了,因此自己记录一个。</p>
<h2 id="新建github仓库">新建Github仓库</h2>
<p>新建一个public仓库,名叫TRxiv,并将远程仓库<code>git clone</code>到本地。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone [email protected]:Yorks0n/TRxiv.git
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>cd TRxiv
</span></span></code></pre></div><h2 id="创建一个动作元数据文件">创建一个动作元数据文件</h2>
<p>要让仓库里能被以Action的形式直接调用,需要在<strong>根目录</strong>中创建一个<code>action.yml</code>配置文件,可以在这个文件中指定Action的输入和输出,调用的参数及运行环境</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># action.yml</span>
</span></span><span style="display:flex;"><span>name: <span style="color:#e6db74">&#39;trxiv&#39;</span>
</span></span><span style="display:flex;"><span>description: <span style="color:#e6db74">&#39;Tracking popular bioRxiv and medRxiv preprints&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>runs:
</span></span><span style="display:flex;"><span> using: <span style="color:#e6db74">&#39;node16&#39;</span>
</span></span><span style="display:flex;"><span> main: <span style="color:#e6db74">&#39;dist/index.js&#39;</span>
</span></span></code></pre></div><h2 id="准备运行的代码">准备运行的代码</h2>
<p>手动将写好的JS脚本拷贝进来,完整代码在此 <a href="https://github.com/Yorks0n/TRxiv">Yorkson/TRxiv</a>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 在这里初始化一下npm</span>
</span></span><span style="display:flex;"><span>npm init -y
</span></span></code></pre></div><p>准备一个<code>.gitignore</code>文件,防止在推送的时候把不必要的文件放到储存库,可以用下面这个工具,或者自己写一下,比如这里就可能有<code>node_modules</code></p>
<p><a href="https://www.toptal.com/developers/gitignore">https://www.toptal.com/developers/gitignore</a></p>
<h2 id="文件推送到远程仓库">文件推送到远程仓库</h2>
<p>然后push到远程仓库</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git add .
</span></span><span style="display:flex;"><span>git commit -m <span style="color:#e6db74">&#34;Initialize&#34;</span>
</span></span><span style="display:flex;"><span>git push
</span></span></code></pre></div><h2 id="打包软件">打包软件</h2>
<p>因为前面把<code>node_modules</code> 从上传的文件列表中忽略了,但脚本<code>index.js</code>内有些依赖的包,所以最好把软件和依赖打包在一起,官方推荐用<code>ncc</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install @vercel/ncc
</span></span></code></pre></div><p>然后对<code>index.js</code> 进行打包</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ncc build index.js -o dist
</span></span></code></pre></div><p>打包产物会存放于<code>dist/index.js</code></p></description>
<content:encoded><![CDATA[<p>将JS写的抓取 Altmetric 热门预印本,与 bioRxiv API 结合后储存在 csv 文件中的过程,部署在 Github Action 中。官方文档写的顺序都有点混乱,网上找到的教程又经常都比较老了,因此自己记录一个。</p>
<h2 id="新建github仓库">新建Github仓库</h2>
<p>新建一个public仓库,名叫TRxiv,并将远程仓库<code>git clone</code>到本地。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone [email protected]:Yorks0n/TRxiv.git
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>cd TRxiv
</span></span></code></pre></div><h2 id="创建一个动作元数据文件">创建一个动作元数据文件</h2>
<p>要让仓库里能被以Action的形式直接调用,需要在<strong>根目录</strong>中创建一个<code>action.yml</code>配置文件,可以在这个文件中指定Action的输入和输出,调用的参数及运行环境</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># action.yml</span>
</span></span><span style="display:flex;"><span>name: <span style="color:#e6db74">'trxiv'</span>
</span></span><span style="display:flex;"><span>description: <span style="color:#e6db74">'Tracking popular bioRxiv and medRxiv preprints'</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>runs:
</span></span><span style="display:flex;"><span> using: <span style="color:#e6db74">'node16'</span>
</span></span><span style="display:flex;"><span> main: <span style="color:#e6db74">'dist/index.js'</span>
</span></span></code></pre></div><h2 id="准备运行的代码">准备运行的代码</h2>
<p>手动将写好的JS脚本拷贝进来,完整代码在此 <a href="https://github.com/Yorks0n/TRxiv">Yorkson/TRxiv</a>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 在这里初始化一下npm</span>
</span></span><span style="display:flex;"><span>npm init -y
</span></span></code></pre></div><p>准备一个<code>.gitignore</code>文件,防止在推送的时候把不必要的文件放到储存库,可以用下面这个工具,或者自己写一下,比如这里就可能有<code>node_modules</code></p>
<p><a href="https://www.toptal.com/developers/gitignore">https://www.toptal.com/developers/gitignore</a></p>
<h2 id="文件推送到远程仓库">文件推送到远程仓库</h2>
<p>然后push到远程仓库</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git add .
</span></span><span style="display:flex;"><span>git commit -m <span style="color:#e6db74">"Initialize"</span>
</span></span><span style="display:flex;"><span>git push
</span></span></code></pre></div><h2 id="打包软件">打包软件</h2>
<p>因为前面把<code>node_modules</code> 从上传的文件列表中忽略了,但脚本<code>index.js</code>内有些依赖的包,所以最好把软件和依赖打包在一起,官方推荐用<code>ncc</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install @vercel/ncc
</span></span></code></pre></div><p>然后对<code>index.js</code> 进行打包</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ncc build index.js -o dist
</span></span></code></pre></div><p>打包产物会存放于<code>dist/index.js</code></p>
<h2 id="创建工作流">创建工作流</h2>
<p>在本地创建<code>.github/workflows/main.yml</code>,这个文件实际确定了 Github Action 的行为,此<code>yml</code>格式文件一定要注意正确的缩进。</p>
<p>配置文件内容:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># main.yml</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## 动作的名称</span>
</span></span><span style="display:flex;"><span>name: <span style="color:#e6db74">'TRxiv update'</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 触发条件</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## 条件1,代码push进仓库</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">## 条件2,定时任务,在UTC 10和22点,即北京时间18点和6点运行</span>
</span></span><span style="display:flex;"><span>on:
</span></span><span style="display:flex;"><span> push:
</span></span><span style="display:flex;"><span> schedule:
</span></span><span style="display:flex;"><span> - cron: <span style="color:#e6db74">'0 10,22 * * *'</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># jobs 定义了要执行的一系列任务 </span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>jobs:
</span></span><span style="display:flex;"><span> <span style="color:#75715e">## 这个job的名称</span>
</span></span><span style="display:flex;"><span> TRxiv_update_job:
</span></span><span style="display:flex;"><span> <span style="color:#75715e">## runs-on 内置运行任务的server类型</span>
</span></span><span style="display:flex;"><span> runs-on: ubuntu-latest
</span></span><span style="display:flex;"><span> name: Update the csv file
</span></span><span style="display:flex;"><span> steps:
</span></span><span style="display:flex;"><span> <span style="color:#75715e">## 必须有这一步,让运行的文件夹成为一个git repo</span>
</span></span><span style="display:flex;"><span> - uses: actions/checkout@v3
</span></span><span style="display:flex;"><span> <span style="color:#75715e">## 运行脚本,指向我们的repo</span>
</span></span><span style="display:flex;"><span> - name: <span style="color:#e6db74">'run script'</span>
</span></span><span style="display:flex;"><span> uses: Yorks0n/TRxiv@main
</span></span><span style="display:flex;"><span> <span style="color:#75715e">## 运行完后,把运行产生在本地的data.csv更新回仓库中</span>
</span></span><span style="display:flex;"><span> - name: Commit files
</span></span><span style="display:flex;"><span> run: |
</span></span><span style="display:flex;"><span> git config --local user.email <span style="color:#e6db74">"github email adress"</span>
</span></span><span style="display:flex;"><span> git config --local user.name <span style="color:#e6db74">"github user name"</span>
</span></span><span style="display:flex;"><span> git pull
</span></span><span style="display:flex;"><span> git add README.md
</span></span><span style="display:flex;"><span> git add data.csv
</span></span><span style="display:flex;"><span> git commit -m <span style="color:#e6db74">"update data.csv"</span>
</span></span><span style="display:flex;"><span> shell: bash
</span></span><span style="display:flex;"><span> <span style="color:#75715e">## 使用秘钥确认具有上传权限,同时以秘钥方式提交不会触发push action,避免陷入loop</span>
</span></span><span style="display:flex;"><span> - name: Push changes
</span></span><span style="display:flex;"><span> uses: ad-m/github-push-action@master
</span></span><span style="display:flex;"><span> with: