Unix - eliminer des colonnes .txt

Résolu/Fermé
AlexMac - 14 nov. 2011 à 09:48
 AlexMac - 15 nov. 2011 à 00:46
Bonjour a tous,

J'ai en input un fichier texte contenant
--at-gg---
--at-gga--
--at-gtcg-
--atgtgtt-

et je voudrais eliminer les colonnes contenant plus de 80% de -, afin d'obtenir :

atgg--
atgga-
atgtcg
attgtt


J'ai cherche beaucoup mais rien trouve. Peut etre la commande awk pour compter es occurences de - dans chaque colonne, mais je ne vois pas comment faire

Merci pour votre aide

Alexandre


4 réponses

dubcek Messages postés 18764 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 février 2025 5 624
14 nov. 2011 à 12:37
hello
la colonne 5 contient moins de 80% de -, c'est pour cela que je teste <75
$ cat a1
--at-gg---
--at-gga--
--at-gtcg-
--atgtgtt-
$ 
$ awk '{x[NR]=$0}END{for(i=1;i<=10;i++){for(n=1;n<=NR;n++){if(substr(x[n],i,1)=="-")c[i]++} ;col[i]=(100*c[i])/NR} ; for(n=1;n<=NR;n++){for(i=1;i<=10;i++){if(col[i]<75)printf substr(x[n], i, 1)} print ""}}' a1
atgg--
atgga-
atgtcg
attgtt
$ 
$ 
1
Merci beacoup dubcek,

Ca marche a merveille!

Je vais essayer de décortiquer pour comprendre, mais j'avoue que quelques explication seraient les bienvenues.

Merci encore beaucoup

Alex
0
dubcek Messages postés 18764 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 9 février 2025 5 624
14 nov. 2011 à 15:42
même code, mais dans un fichjier avec indentations
# on lit le fichirer en entier qu'on stocke dans un tableau x
{x[NR]=$0}
END{
# pour chaque colonne (ici 10) de chaque ligne (NR == nb de lignes), on
# compte le nombre de -,  nombre que l'on stocke dans un tableau c
        for(i=1;i<=10;i++){
                for(n=1;n<=NR;n++){
                        if(substr(x[n],i,1)=="-")
                                c[i]++} ;
# on a le nombre de - par colonne, on peut calculer le %
                        col[i]=(100*c[i])/NR
        } ;
# pour chaque ligne et chaque colonne, on teste si le % de cette colonne
# est < 75, si oui on imprime le caractère
                        for(n=1;n<=NR;n++){
                                for(i=1;i<=10;i++){
                                        if(col[i]<75)
                                                printf substr(x[n], i, 1)
                                }
# on imprime un saut de ligne en fin de ligne
                                print ""
                        }
}

0
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828
14 nov. 2011 à 20:40
En fait si je résume cette longue discussion c'est que le pourcentage seuil (80%) est relatif à chaque colonne, ce qui force à lire l'ensemble des lignes pour savoir si la colonne est conservée ou non.

À mon avis il n'y a pas quarante solutions, il faut lire toutes les lignes, les stocker en mémoire, et parcourir la structure ainsi lue. A peu près n'importe quel langage de programmation permet de le faire.

Personnellement je fais plutôt du C++, mais si tu préfères le python ou n'importe quoi d'autre pourquoi pas...

Source (toto.cpp)
#include <fstream>
#include <vector>
#include <iostream>
#include <string>

int main(int argc, char **argv){
	const char *filename_in  = argv[1],
               *filename_out = argv[2];

	const unsigned int num_chars = 10; // Chaque ligne valide doit comporter 10 caractères
	const unsigned int threshold = 80; // 80 %
	unsigned int num_lines = 0;

	std::string line;

	std::ifstream ifs;
	std::ofstream ofs;

	std::vector<std::string> lines;
	std::vector<unsigned int> num_minus(num_chars, 0);
	std::vector<bool> column_to_write(num_chars);

	if(argc != 3) {
		std::cerr << "usage: " << argv[0] << " input_file output_file" << std::endl;
		goto END;
	}

	// Ouvrir le fichier d'entrée
	ifs.open(filename_in);
	if(!ifs) {
		std::cerr << argv[0] << ": can't read " << filename_in << std::endl;
		goto END;
	}

	// Ouvrir le fichier de sortie 
	ofs.open(filename_out);
	if(!ofs) {
		std::cerr << argv[0] << ": can't write " << filename_out << std::endl;
		goto END;
	}

	// Charger les lignes en mémoire et compter le nombre de '-'
	while(std::getline(ifs, line)) {
		if(line.size() == num_chars){
			for(std::size_t i = 0; i < num_chars; ++i){
				if(line[i] == '-') ++num_minus[i];
			}
			lines.push_back(line);
			++num_lines;
		}
	}

	// Quelles colonnes doit on réécrire ?
	for(std::size_t i = 0; i < num_chars; ++i){
		column_to_write[i] = ((100 * num_minus[i] / num_lines) < threshold);
	}

	// Écrire les lignes en ne conservant que le nécessaire 
	for(std::size_t i = 0; i < lines.size(); ++i){
		for(std::size_t j = 0; j < num_chars; ++j){ 
			if(column_to_write[j]){
				ofs << lines[i][j];
			}
		}
		ofs << std::endl;
	}

END:
	// On ferme proprement
	if(ifs) ifs.close();
	if(ofs) ofs.close();
	return 0;
}


Avec ce fichier d'entrée (input.txt) :

--at-gg---
--at-gga--
--at-gtcg-
--atgtgtt-


Il faut à présent installer un compilateur (par exemple g++ sous linux ou code::blocks sous windows). Par exemple sous linux (ubuntu, debian...) :

sudo apt-get update
sudo apt-get install g++


On compile notre programme (appelons le par exemple convertir.exe) :

(mando@aldur) (~) $ g++ -W -Wall toto.cpp -o convertir.exe
(mando@aldur) (~) $ ./convertir input.txt output.txt


On obtient le fichier output.txt :

at-gg--
at-gga-
at-gtcg
atgtgtt


... ce qui est le bon résultat si j'ai bien pigé.

À l'époque tu avais dit que normalement la sortie était :

atgg--
atgga-
atgtcg
attgtt 


... mais la colonne qui contient (-,-,-,g) contient seulement 75% de '-' soit moins de 80%, donc normalement on la conserve...

Bonne chance
1
Merci Mamiemando pour cette reponse,
Je vais prendre le temps d'essayer cela. Cela dit, le script propose par dubcek marche bien et il est assez simple.
Tu as raison je me suis plante pour la colonne (- - - g) ;-/ Desole
Merci encore Beaucoup
Alex
0
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 6 422
14 nov. 2011 à 09:59
Salut,

Rien pigé à ton histoire de 80% ;-((

Sur la ligne 2, pourquoi t'élimines les 2 premiers et qu'un seul sur les 2 derniers ???
--at-gga-- => atgga- 
0
En fait il faut lire en colonne :
la premiere colonne ne contient que des - (donc 100% de -), elle degage
la deuxieme idem
la troisieme ne contient pas de - (il n'a que des a), elle reste
etc
l'avant dernier ne contient que deux - (et un g et un t), donc elle reste (50% de -)
et la derniere ne contient que des -, elle degage

J'espere avoir reussi a etre clair cette fois

Merci

Alex
0
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 6 422
14 nov. 2011 à 10:15
En fait il faut lire en colonne
Ok, mais à quoi reconnaît-on une colonne ???
0
bah oui c un des probleme.
Si je veux utiliser awk je peux par exemple ajouter des pipes (|) apres chaque caracteres.
genre sed "s/./|/g"

C'est pas trop un soucis vu que je pourrais les retirer ensuite
0
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 6 422
14 nov. 2011 à 10:24
Tes explications sont complètement incohérentes ;-((

la premiere colonne ne contient que des - (donc 100% de -), elle degage
la deuxieme idem

En considérant que chaque caractère est en fait une colonne à lui tout seul, nous sommes d'accord ;-\

la troisieme ne contient pas de - (il n'a que des a), elle reste
etc

Idem qu'au-dessus ;-\

l'avant dernier ne contient que deux - (et un g et un t), donc elle reste (50% de -)
C'est là où ça se corse ;-((
Pourquoi subitement ta colonne contient plusieurs caractères et commence à mélanger caractères alphanumériques avec des caractères non-alphanumériques ???

Où est la logique dans tout ça ??? ;-\
0
il s'agit de sequences d'ADN (succession de molecules A T C et G pour Adenosine, Guanine, Thymine, Custeine) qui sont alignees selon leur similarite, imaginons:

gene humain ATGCT - -TCGCT
gene mouche ATGCTTGTCGCT
gene meduse ATCGT - -TCGCT

Ces genes sont identiques chez les trois organismes (humain, mouche , meduse), sauf que dans la sequence genetique de la mouche, il y a deux molecules en plus, un T et un G.
Ce molecules absente chez l'homme et la meduse sont representes par des tirets.

Ce que je voudrais c'est conserver cet alignement tou en eliminant les zones contenant beaucoup de tirets, c'est a dire les zones genetiques peu conservees entre differentes especes.

Voila, j'espere que c'est plu clair pour toi

Alex
0
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828
Modifié par mamiemando le 14/11/2011 à 10:00
Oui tu peux t'en sortir avec awk.

man awk


$0 désigne la ligne courante, affecte là à s.
length(s) permet de calculer la longueur de s.
Crée un compteur (par exemple num_minus) initialisé à 0.
Dans une boucle for allant de 0 à length(s) - 1 :
- substr(s, i, 1) permet d'extraire le i-ème caractère.
- si c'est un tiret, incrémente ton compteur num_minus
Une fois la boucle for, calcule le pourcentage (100 * num_minus / length(s)). S'il est supérieur à ton seuil de 80, print $0.

Bonne chance
0
OK je vais essayer, mais j'ai l'impression que dans ce que tu me suggere, ce sont les - sur les lignes qui sont comptes, et non sur les colonnes. C'est justement ca qui me pose probleme.
Alex
0