C/C++

Tema 8 · Processament de seqüències

Introducció

El processament de seqüències d'ADN i proteïnes és una tasca fonamental en bioinformàtica. Aprendrem a calcular el percentatge GC (contingut de guanina i citosina), analitzar freqüències de nucleòtids i identificar ORFs (Open Reading Frames, marcs oberts de lectura) que poden correspondre a gens.

Objectius

  • Calcular el percentatge GC d'una seqüència d'ADN
  • Determinar la freqüència de cada nucleòtid (A, C, G, T)
  • Identificar codons d'inici (ATG) i de parada (TAA, TAG, TGA)
  • Extreure ORFs i traduir-los a seqüència d'aminoàcids

Percentatge GC i freqüències

Funció per calcular el percentatge GC i les freqüències de cada base:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

void analitzar_sequencia(char adn[]) {
    int i, len = strlen(adn);
    int a = 0, c = 0, g = 0, t = 0;

    for (i = 0; i < len; i++) {
        char base = toupper(adn[i]);
        switch (base) {
            case 'A': a++; break;
            case 'C': c++; break;
            case 'G': g++; break;
            case 'T': t++; break;
        }
    }

    int total_bases = a + c + g + t;
    float gc = total_bases > 0 ? 100.0 * (g + c) / total_bases : 0;

    printf("Total bases vàlides: %d\n", total_bases);
    printf("A: %d (%.1f%%)\n", a, 100.0 * a / total_bases);
    printf("C: %d (%.1f%%)\n", c, 100.0 * c / total_bases);
    printf("G: %d (%.1f%%)\n", g, 100.0 * g / total_bases);
    printf("T: %d (%.1f%%)\n", t, 100.0 * t / total_bases);
    printf("Contingut GC: %.1f%%\n", gc);
}

int main() {
    char adn[] = "AGCTTAGGCTAGCTAGC";
    analitzar_sequencia(adn);
    return 0;
}
La funció toupper() converteix a majúscules per evitar errors

Identificació d'ORFs

Un ORF comença amb un codó d'inici ATG i acaba amb un codó de parada (TAA, TAG o TGA). La seqüència entre ells, de longitud múltiple de 3, es pot traduir a aminoàcids.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

// Busca el següent ORF a partir d'una posició donada
// Retorna la posició final o -1 si no en troba
int trobar_orf(char adn[], int inici, char orf[], int *longitud) {
    int len = strlen(adn);
    int i = inici;
    
    // Buscar codó d'inici ATG
    while (i < len - 2) {
        if (toupper(adn[i]) == 'A' && 
            toupper(adn[i+1]) == 'T' && 
            toupper(adn[i+2]) == 'G') {
            break;
        }
        i++;
    }
    if (i >= len - 2) return -1;  // No hi ha més ATG
    
    int inici_orf = i;
    int j = 0;
    
    // Copiar codons fins a trobar un codó de parada
    while (i < len - 2) {
        char c1 = toupper(adn[i]);
        char c2 = toupper(adn[i+1]);
        char c3 = toupper(adn[i+2]);
        
        // Comprovar si és codó de parada
        if ((c1 == 'T' && c2 == 'A' && c3 == 'A') ||
            (c1 == 'T' && c2 == 'A' && c3 == 'G') ||
            (c1 == 'T' && c2 == 'G' && c3 == 'A')) {
            break;
        }
        
        // Copiar el codó
        orf[j++] = adn[i];
        orf[j++] = adn[i+1];
        orf[j++] = adn[i+2];
        i += 3;
    }
    orf[j] = '\0';
    *longitud = j / 3;  // Nombre d'aminoàcids
    return i;  // Posició final
}

int main() {
    char adn[] = "XXATGGCCTAATAG";
    char orf[1000];
    int long_aa, pos = 0;
    
    while ((pos = trobar_orf(adn, pos, orf, &long_aa)) != -1) {
        printf("ORF trobat: %s (%d aa)\n", orf, long_aa);
        pos++;  // Avançar per buscar més ORFs
    }
    return 0;
}
L'ORF comença amb ATG i s'atura en el primer codó de parada en el mateix marc de lectura

Traducció d'ADN a proteïna

Un cop tenim la seqüència d'ADN d'un ORF, podem traduir cada codó (3 bases) a un aminoàcid segons el codi genètic estàndard.

char traduir_codo(char *codo) {
    // Versió simplificada: només alguns aminoàcids
    if (strncmp(codo, "ATG", 3) == 0) return 'M'; // Metionina
    if (strncmp(codo, "TGG", 3) == 0) return 'W'; // Triptòfan
    if (strncmp(codo, "TTT", 3) == 0 || strncmp(codo, "TTC", 3) == 0) return 'F'; // Fenilalanina
    if (strncmp(codo, "TTA", 3) == 0 || strncmp(codo, "TTG", 3) == 0 ||
        strncmp(codo, "CTT", 3) == 0 || strncmp(codo, "CTC", 3) == 0 ||
        strncmp(codo, "CTA", 3) == 0 || strncmp(codo, "CTG", 3) == 0) return 'L'; // Leucina
    if (strncmp(codo, "TAA", 3) == 0 || strncmp(codo, "TAG", 3) == 0 || 
        strncmp(codo, "TGA", 3) == 0) return '*'; // STOP
    return 'X'; // Desconegut
}

void traduir_orf(char orf_adn[], char proteina[]) {
    int i, j = 0;
    for (i = 0; i < strlen(orf_adn); i += 3) {
        char codo[4];
        strncpy(codo, &orf_adn[i], 3);
        codo[3] = '\0';
        proteina[j++] = traduir_codo(codo);
    }
    proteina[j] = '\0';
}
Es pot ampliar amb la taula completa del codi genètic

Exercicis

  1. Escriu un programa que demani una seqüència d'ADN i mostri el percentatge GC i la freqüència de cada base.
  2. Identifica tots els codons ATG en una seqüència i mostra les posicions on apareixen.
  3. Amplia la taula de traducció per incloure almenys 10 aminoàcids diferents.

Mini Projecte – Analitzador de seqüències

Escriu un programa complet que:

  • Llegeixi una seqüència d'ADN des d'un fitxer (pot contenir diverses línies).
  • Calculi i mostri el percentatge GC i les freqüències de cada nucleòtid.
  • Identifiqui tots els ORFs en els tres marcs de lectura (començant a posició 0, 1 i 2).
  • Per a cada ORF trobat, mostri la seva longitud en aminoàcids i la seqüència proteica traduïda (encara que sigui parcial).
  • Desi els resultats en un fitxer de text amb un resum ordenat per longitud d'ORF (el més llarg primer).

Pista: utilitza funcions separades per a cada tasca i estructura les dades en structs si cal.