Seurat Weekly NO.14 || 讀源碼解決實際問題

天子呼來不上船,
自稱臣是菜鳥團。

在這裏,和國際同行一起學習單細胞數據分析。

我今天並沒有敲滿100行代碼,以至於我在寫這篇文章的時候都有點不好意思了。要讀/寫長代碼的想法腦海裏輪迴了很久了,哪來的時間呢?寫吧。

我們爲什麼要學習長代碼?

對大學畢業纔開始學習一門語言的人來講,學習基本概念並不是一件難事。Perl語言,聽過吧,有那麼多的操作符和參數,在不和C語言來比較的前提下,這並不是一個順從人類本能的語言。

知道一門語言的基本概念是一回事,能寫出一手好程序是另一回事。

Seurat Weekly 目前已經進行到第十四期了,之前我們讀了它的很多幫助文檔,這一次我們來試着讀一讀它的源代碼。藉此機會呢,也回答兩個有趣的問題。

首先,我們把Seurat的源代碼clone 到 本地便於我們查找和閱讀。

git clone  https://github.com/satijalab/seurat.git 

然後我們就要在結構上看看Seurat的框架

tree -L  1 
.
|-- CODE_OF_CONDUCT.md
|-- DESCRIPTION
|-- LICENSE
|-- NAMESPACE
|-- NEWS.md
|-- R
|-- README.md
|-- _pkgdown.yaml
|-- appveyor.yml
|-- azure-pipelines.yml
|-- cran-comments.md
|-- data
|-- index.md
|-- inst
|-- man
|-- seurat.Rproj
|-- src
|-- tests
|-- travis_setup.sh
`-- vignettes

這時候就發揮程序員最能讀文檔的功能了,先把眼前看到的文檔讀一遍,至少要cat 一番,知道它是如何架構的。然後是找出除了配置文件之外最重要的一個文件。也許在之前我們非常想看vignettes,也就是教程,但是教程我們已經寫的太多了,先把它往後排一點。這裏我們先看 R 這個文件夾:

.
|-- RcppExports.R
|-- clustering.R
|-- convenience.R
|-- data.R
|-- differential_expression.R
|-- dimensional_reduction.R
|-- generics.R
|-- integration.R
|-- mixscape.R
|-- objects.R
|-- preprocessing.R
|-- reexports.R
|-- tree.R
|-- utilities.R
|-- visualization.R
|-- zfRenderSeurat.old
`-- zzz.R

我們也看到這裏都是點R的文件,也就是R腳本,這時候我們就需要挨個來看了。當然了,如果你是比較喜歡畫圖,可以先學visualization.R,這裏有實打實的可視化腳本,腳本太長,我們就不再展示了。不管從哪裏開始最終都是要全看的,不是說要背下來,而是學習人家寫代碼的風格和一些小的技巧。然而,這裏可能回憶道一個閱讀障礙,那就是RcppExports.R 這個腳本,和一般的R代碼不同,裏面幾乎都是這個格式的代碼:

RunModularityClusteringCpp <- function(SNN, modularityFunction, resolution, algorithm, nRandomStarts, nIterations, randomSeed, printOutput, edgefilename) {
    .Call('_Seurat_RunModularityClusteringCpp', PACKAGE = 'Seurat', SNN, modularityFunction, resolution, algorithm, nRandomStarts, nIterations, randomSeed, printOutput, edgefilename)
}

RunUMISampling <- function(data, sample_val, upsample = FALSE, display_progress = TRUE) { 
    .Call('_Seurat_RunUMISampling', PACKAGE = 'Seurat', data, sample_val, upsample, display_progress)
}

RunUMISamplingPerCell <- function(data, sample_val, upsample = FALSE, display_progress = TRUE) { 
    .Call('_Seurat_RunUMISamplingPerCell', PACKAGE = 'Seurat', data, sample_val, upsample, display_progress)
}

於是我們就要找到Rcpp到底在哪放着。在src的目錄下放着:

|-- Makevars
|-- ModularityOptimizer.cpp
|-- ModularityOptimizer.h
|-- RModularityOptimizer.cpp
|-- RcppExports.cpp
|-- data_manipulation.cpp
|-- data_manipulation.h
|-- fast_NN_dist.cpp
|-- integration.cpp
|-- integration.h
|-- snn.cpp
|-- snn.h
`-- valid_pointer.c

想要看懂這些代碼除了四級詞彙還需要一些C語言的基礎知識。其實真看的話也並沒有那麼難,一行一行讀就是了,《紅樓夢》都看了。

#include "ModularityOptimizer.h"

#include <algorithm>
#include <exception>
#include <functional>
#include <numeric>
#include <stdexcept>

using namespace ModularityOptimizer;
using namespace std::chrono;

JavaRandom::JavaRandom(uint64_t seed) { 
  setSeed(seed);
}

void JavaRandom::setSeed(uint64_t seed) { 

  this->seed = (seed ^ uint64_t(0x5DEECE66D)) & ((uint64_t(1) << 48) - 1);
}

int JavaRandom::next(int bits) { 
  // Only 31 bits ever used.
  seed = (seed * uint64_t(0x5DEECE66D) + uint64_t(0xB)) & ((uint64_t(1) << 48) - 1);
  return static_cast<int>(seed >> (48 - bits)); 
}

要把這部分讀完估計是要發一段時間的,下面我們看開一個比較輕鬆的模塊vignettes是大家比較熟悉的教程,一個教程一個md。

.
|-- archive.Rmd
|-- archive.yaml
|-- assets
|-- atacseq_integration_vignette.Rmd
|-- cell_cycle_vignette.Rmd
|-- conversion_vignette.Rmd
|-- de_vignette.Rmd
|-- dim_reduction_vignette.Rmd
|-- essential_commands.Rmd
|-- extensions.Rmd
|-- future_vignette.Rmd
|-- get_started.Rmd
|-- hashing_vignette.Rmd
|-- install.Rmd
|-- integration_introduction.Rmd
|-- integration_large_datasets.Rmd
|-- integration_mapping.Rmd
|-- integration_rpca.Rmd
|-- interaction_vignette.Rmd
|-- merge_vignette.Rmd
|-- mixscape_vignette.Rmd
|-- multimodal_reference_mapping.Rmd
|-- multimodal_vignette.Rmd
|-- pbmc3k_tutorial.Rmd
|-- sctransform_vignette.Rmd
|-- spatial_vignette.Rmd
|-- v4_changes.Rmd
|-- vignettes.yaml
|-- visualization_vignette.Rmd
`-- weighted_nearest_neighbor_analysis.Rmd

看完本文你也許會納悶:爲什麼總有人在找教程,源代碼裏面不是滿滿的教程嗎?而且是Rmd的拿出來那是可以直接跑的啊,到處找人要什麼pipeline,也值當的。

在這裏,我們提出兩個問題,大家可以在留言區回答:

  • 爲什麼Seurat的函數運行完返回的還是Seurat對象,它讀入的和輸出的都一樣,好奇怪。
  • 爲什麼Seurat的聚類結果中編號是從零開始的,而且,細胞數量依次遞減

希望您從源碼中給出你的看法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章