Extraire la position de début et de fin d'un identifiant commun

J'ai un file qui ressemble à ceci:

Id Chr Start End Prom_1 chr1 3978952 3978953 Prom_1 chr1 3979165 3979166 Prom_1 chr1 3979192 3979193 Prom_2 chr1 4379047 4379048 Prom_2 chr1 4379091 4379092 Prom_2 chr1 4379345 4379346 Prom_2 chr1 4379621 4379622 Prom_3 chr1 5184469 5184470 Prom_3 chr1 5184495 5184496 

ce que je voudrais extraire est le début et la fin du même Id comme ceci:

 Id Chr Start End Prom_1 chr1 3978952 3979193 Prom_2 chr1 4379047 4379622 Prom_3 chr1 5184469 5184496 

comme vous l'avez remarqué, le nombre d' Id répétés n'est pas constant entre le début et la fin. Toute idée serait très appréciée.

Avec GNU datamash :

 datamash -H -W -g 1,2 min 3 max 4 <input 

Cela peut être fait avec une boucle classique pour lire le file ou avec d'autres moyens comme awk, mais je ne suis pas bon dans awk pour vous donner une solution basée sur awk. La solution ci-dessous fonctionne bien dans bash et utilise awk simple, grep et arrays.

Avec un identifiant connu (par paramètre ou par saisie user)

 id="Prom_1" #Or for user input read -p "Give Id :" id header=$(head -1 a.txt) #get the 1st line and store it as header. data=($(grep $id a.txt)) #grep the file for given the id and fill an array echo "$header" echo -e "${data[0]}\t${data[1]}\t${data[2]}\t${data[-1]}" #data[-1] refers to the last element of the data array #Output: Id Chr Start End Prom_1 chr1 3978952 3979193 

L'astuce est que le tableau obtient toutes les valeurs grep séparées par un espace blanc (IFS par défaut) et ainsi le tableau ressemble à ceci:

 root@debi64:# id="Prom_1";data=($(grep $id a.txt));declare -p data declare -a data=([0]="Prom_1" [1]="chr1" [2]="3978952" [3]="3978953" [4]=$'\nProm_1' [5]="chr1" [6]="3979165" [7]="3979166" [8]=$'\nProm_1' [9]="chr1" [10]="3979192" [11]="3979193") #declare -p command just prints out all the data of the array (keys and values) 

Pour scanner automatiquement le file pour les identifiants, vous pouvez utiliser le prog uniq comme ceci:

 readarray -t ids< <(awk -F" " '{print $1}' a.txt |uniq |tail -n+2) #For Field separator= " " print the first field (id), print them as unique fields and store them in an array. #Here the use of readarray is better to handle data separated by new lines. declare -p ids #Output: declare -a ids=([0]="Prom_1" [1]="Prom_2" [2]="Prom_3") 

Combiner tous set:

 header=$(head -1 a.txt) #get the 1st line and store it as header. readarray -t ids< <(awk -F" " '{print $1}' a.txt |uniq |tail -n+2) echo "$header" for id in ${ids[@]} do data=($(grep $id a.txt)) echo -e "${data[0]}\t${data[1]}\t${data[2]}\t${data[-1]}" done #Output Id Chr Start End Prom_1 chr1 3978952 3979193 Prom_2 chr1 4379047 4379622 Prom_3 chr1 5184469 5184496 

pouvez-vous essayer cette awk

 $ awk 'NR==1{print; next}NR!=1{if(!($1 in Arr)){printf("\t%s\n%s\t%s\t%s",a,$1,$2,$3);Arr[$1]++}else{a=$NF}}END{printf("\t%s\n",a)}' input.txt Id Chr Start End Prom_1 chr1 3978952 3979193 Prom_2 chr1 4379047 4379622 Prom_3 chr1 5184469 5184496 

 awk ' NR==1{print; next} NR!=1{ if(!($1 in Arr)) { printf("\t%s\n%s\t%s\t%s",a,$1,$2,$3);Arr[$1]++; } else { a=$NF } } END{ printf("\t%s\n",a) }' input.txt 

En supposant que les inputs de chaque ID sont sortingées numériquement sur la coordonnée de départ:

 #!/usr/bin/awk -f NR == 1 { # Deal with header (just print it and continue). print; next; } $1 != id { # This is a new ID. # Display the data for the ID we've been processing so far (if any). if (id) { print id, chr, start, stop; } # Store the data for the new ID. id = $1; chr = $2; start = $3; } { # The stop/end coordinate will be updated for each line. stop = $4; } END { # At the end, display the data for the last ID. print id, chr, start, stop; } 

Test (fonctionne avec GNU awk , BSD awk et mawk ):

 $ ./script.awk data.in Id Chr Start End Prom_1 chr1 3978952 3979193 Prom_2 chr1 4379047 4379622 Prom_3 chr1 5184469 5184496 

Si les inputs ne sont pas sortingées, sortingez-les:

 $ sort -k1,1 -k3,3n -o data.in data.in 

Cela peut gâcher la ligne d'en-tête si. Ce qui suit est une alternative qui va fonctionner:

 $ cat <( head -n 1 data.in ) <( sed '1d' data.in | sort -k1,1 -k3,3n ) >data.new $ mv data.new data.in 

Requiert bash ou ksh si …

Une autre solution avec awk et stockant dans des variables:

Prenez les en-têtes du file et mettez-les dans le file de sortie:

 row1=$(head -1 input_file) echo $row1 | sed -e 's/ /\t/g' > output_file 

Prenez les valeurs uniques de la première colonne:

 col1=$(for i in $(awk 'NR>1 {print $1}' input_file | uniq); do echo $i; done) 

Prenez la première occurrence de la valeur dans la deuxième ligne en fonction de chaque première valeur de la colonne:

 col2=$(for i in $(echo "$col1"); do grep -m1 $i input_file | awk '{print $2}'; done) 

Prenez la première valeur de la troisième colonne en fonction de chaque première valeur de la colonne:

 col3=$(for i in $(echo "$col1"); do grep -m1 $i input_file | tail -1 | awk '{print $3}'; done) 

Prenez la dernière valeur de la quasortingème colonne en fonction de chaque première valeur de colonne:

 col4=$(for i in $(echo "$col1"); do grep $i input_file | tail -1 | awk '{print $4}'; done) 

Ajoutez toutes ces valeurs au file de sortie:

 paste -d'\t' <(echo "$col1") <(echo "$col2") <(echo "$col3") <(echo "$col4") >> output_file