序列比對(一)——全局比對Needleman-Wunsch算法

原創(chuàng):hxj7

前言
序列比對是生信領(lǐng)域的一個(gè)古老課題湾揽,在這一波NGS的浪潮中重新引起大家的廣泛關(guān)注。由于生物序列的特殊性售貌,在比對的時(shí)候允許插入缺失育特,所以往往是一種不精確匹配生逸。從本文開始,我們陸續(xù)學(xué)習(xí)前人開發(fā)出的流行算法。

全局比對算法
所謂全局比對算法槽袄,就是根據(jù)一個(gè)打分矩陣(替換矩陣)計(jì)算出兩個(gè)序列比對最高得分的算法。關(guān)于它的介紹網(wǎng)上已經(jīng)非常多了锋谐,我們只需看看其中的關(guān)鍵點(diǎn)實(shí)現(xiàn)代碼遍尺。
關(guān)鍵點(diǎn)
1. 打分矩陣:
選用不同的打分矩陣或者罰分分值會(huì)導(dǎo)致比對結(jié)果不同,常用BLAST打分矩陣涮拗。
2. 計(jì)算比對最高得分的算法:
常用動(dòng)態(tài)規(guī)劃算法(Needleman-Wunsch算法)乾戏。

image

圖片引自http://www.reibang.com/p/2b99d0d224a2

3. 打印出最高得分相應(yīng)的序列比對結(jié)果:
根據(jù)得分矩陣回溯,如果最優(yōu)比對結(jié)果有多個(gè)三热,全部打印出來鼓择。
4. 理解打分系統(tǒng)背后的概率論模型:
比對分值可以理解為匹配模型和隨機(jī)模型的對數(shù)幾率比(log-odds ratio)。

實(shí)現(xiàn)代碼(C代碼及示例)
網(wǎng)上的教程給出的代碼大多不全就漾,所以我決定還是自己造輪子了呐能。
需要說明的是:
1. 沒有對輸入進(jìn)行檢查,如果有非AGCT的字符程序可能會(huì)出錯(cuò)抑堡。
2. 對空位的罰分是線性的摆出,即penalty=gd,其中g(shù)為空位長度首妖,d為一個(gè)空位的罰分偎漫。
在以后的文章中,我們會(huì)給出仿射型罰分的代碼有缆,即
penalty=d+(g-1)
e象踊,其中g(shù)為連續(xù)空位的長度,d為連續(xù)空位中第一個(gè)空位的罰分棚壁,e為連續(xù)空位中第2個(gè)及以后空位的罰分杯矩。
3. 其他未提及的錯(cuò)誤或者不足。

示例

image

代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSEQ 1000
#define GAP_CHAR '-'

// 對空位的罰分是線性的
struct Unit {
    int W1;   // 是否往上回溯一格
    int W2;   // 是否往左上回溯一格
    int W3;   // 是否往左回溯一格
    float M;      // 得分矩陣第(i, j)這個(gè)單元的分值灌曙,即序列s(1,...,i)與序列r(1,...,j)比對的最高得分
};
typedef struct Unit *pUnit;

void strUpper(char *s);
float max3(float a, float b, float c);
float getFScore(char a, char b);
void printAlign(pUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n);
void align(char *s, char *r);

int main() {
    char s[MAXSEQ];
    char r[MAXSEQ];
    printf("The 1st seq: ");
    scanf("%s", s);
    printf("The 2nd seq: ");
    scanf("%s", r);
    align(s, r);
    return 0;
}

void strUpper(char *s) {
    while (*s != '\0') {
        if (*s >= 'a' && *s <= 'z') {
            *s -= 32;
        }
        s++;
    }
}

float max3(float a, float b, float c) {
    float f = a > b ? a : b;
    return f > c ? f : c;
}

// 替換矩陣:match分值為5菊碟,mismatch分值為-4
// 數(shù)組下標(biāo)是兩個(gè)字符的ascii碼減去65之后的和
float FMatrix[] = {
    5, 0, -4, 0, 5, 0, -4, 0, -4, 0,
    0, 0, 5, 0, 0, 0, 0, 0, 0, -4,
    0, -4, 0, 0, 0, -4, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 5
};

float getFScore(char a, char b) {
    return FMatrix[a + b - 'A' - 'A'];
}

void printAlign(pUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n) {
    int k;
    pUnit p = a[i][j];
    if (! (i || j)) {   // 到矩陣單元(0, 0)才算結(jié)束,這代表初始的兩個(gè)字符串的每個(gè)字符都被比對到了
        for (k = n - 1; k >= 0; k--)
            printf("%c", saln[k]);
        printf("\n");
        for (k = n - 1; k >= 0; k--)
            printf("%c", raln[k]);
        printf("\n\n");
        return;
    }
    if (p->W1) {    // 向上回溯一格
        saln[n] = s[i - 1];
        raln[n] = GAP_CHAR;
        printAlign(a, i - 1, j, s, r, saln, raln, n + 1);
    }
    if (p->W2) {    // 向左上回溯一格
        saln[n] = s[i - 1];
        raln[n] = r[j - 1];
        printAlign(a, i - 1, j - 1, s, r, saln, raln, n + 1);
    }
    if (p->W3) {    // 向左回溯一格
        saln[n] = GAP_CHAR;
        raln[n] = r[j - 1];
        printAlign(a, i, j - 1, s, r, saln, raln, n + 1);
    }
}

void align(char *s, char *r) {
    int i, j;
    int m = strlen(s);
    int n = strlen(r);
    float gap = -2.5;     // 對空位的罰分
    float m1, m2, m3, maxm;
    pUnit **aUnit;
    char* salign;
    char* ralign;
    // 初始化
    if ((aUnit = (pUnit **) malloc(sizeof(pUnit*) * (m + 1))) == NULL) {
        fputs("Error: Out of space!\n", stderr);
        exit(1);
    }
    for (i = 0; i <= m; i++) {
        if ((aUnit[i] = (pUnit *) malloc(sizeof(pUnit) * (n + 1))) == NULL) {
            fputs("Error: Out of space!\n", stderr);
            exit(1);     
        }
        for (j = 0; j <= n; j++) {
            if ((aUnit[i][j] = (pUnit) malloc(sizeof(struct Unit))) == NULL) {
                fputs("Error: Out of space!\n", stderr);
                exit(1);     
            }
            aUnit[i][j]->W1 = 0;
            aUnit[i][j]->W2 = 0;
            aUnit[i][j]->W3 = 0;
        }
    }
    aUnit[0][0]->M = 0;
    for (i = 1; i <= m; i++) {
        aUnit[i][0]->M = gap * i;
        aUnit[i][0]->W1 = 1;
    }
    for (j = 1; j <= n; j++) {
         aUnit[0][j]->M = gap * j;
        aUnit[0][j]->W3 = 1;
    }
    // 將字符串都變成大寫
    strUpper(s);
    strUpper(r);
    // 動(dòng)態(tài)規(guī)劃算法計(jì)算得分矩陣每個(gè)單元的分值
    for (i = 1; i <= m; i++) {
        for (j = 1; j <= n; j++) {
            m1 = aUnit[i - 1][j]->M + gap;
            m2 = aUnit[i - 1][j - 1]->M + getFScore(s[i - 1], r[j - 1]);
            m3 = aUnit[i][j - 1]->M + gap;
            maxm = max3(m1, m2, m3);
            aUnit[i][j]->M = maxm;
            if (m1 == maxm) aUnit[i][j]->W1 = 1;
            if (m2 == maxm) aUnit[i][j]->W2 = 1;
            if (m3 == maxm) aUnit[i][j]->W3 = 1;
        }
    }
/*
    // 打印得分矩陣
    for (i = 0; i <= m; i++) {
        for (j = 0; j <= n; j++)
            printf("%f ", aUnit[i][j]->M);
        printf("\n");
    }
*/
    printf("max score: %f\n", aUnit[m][n]->M);
    // 打印最優(yōu)比對結(jié)果在刺,如果有多個(gè)逆害,全部打印
    // 遞歸法
    if ((salign = (char*) malloc(sizeof(char) * (m + n + 1))) == NULL) {
        fputs("Error: Out of space!\n", stderr);
        exit(1);
    }
    if ((ralign = (char*) malloc(sizeof(char) * (m + n + 1))) == NULL) {
        fputs("Error: Out of space!\n", stderr);
        exit(1);
    }
    printAlign(aUnit, m, n, s, r, salign, ralign, 0);
    // 釋放內(nèi)存
    free(salign);
    free(ralign);
    for (i = 0; i <= m; i++) {
        for (j = 0; j <= n; j++) {
            free(aUnit[i][j]);
        }
        free(aUnit[i]);
    }
    free(aUnit);
}

(公眾號:生信了)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蚣驼,隨后出現(xiàn)的幾起案子魄幕,更是在濱河造成了極大的恐慌,老刑警劉巖颖杏,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纯陨,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翼抠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門咙轩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人阴颖,你說我怎么就攤上這事活喊。” “怎么了量愧?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵钾菊,是天一觀的道長。 經(jīng)常有香客問我偎肃,道長煞烫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任累颂,我火速辦了婚禮滞详,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘喘落。我一直安慰自己茵宪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布瘦棋。 她就那樣靜靜地躺著稀火,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赌朋。 梳的紋絲不亂的頭發(fā)上凰狞,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音沛慢,去河邊找鬼赡若。 笑死,一個(gè)胖子當(dāng)著我的面吹牛团甲,可吹牛的內(nèi)容都是我干的逾冬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼躺苦,長吁一口氣:“原來是場噩夢啊……” “哼身腻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匹厘,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤嘀趟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后愈诚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體她按,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牛隅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酌泰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒佣。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宫莱,靈堂內(nèi)的尸體忽然破棺而出丈攒,到底是詐尸還是另有隱情,我是刑警寧澤授霸,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站际插,受9級特大地震影響碘耳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜框弛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一辛辨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瑟枫,春花似錦斗搞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膝擂,卻和暖如春虑啤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背架馋。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工狞山, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叉寂。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓萍启,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屏鳍。 傳聞我的和親對象是個(gè)殘疾皇子勘纯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容