1
votes

Comment comparer deux paires de colonnes dans deux fichiers différents et imprimer les correspondances fusionnées horizontalement (préférence pour BASH ou AWK)?

J'essaie de comparer deux fichiers séparés par des tabulations en faisant correspondre deux paires de colonnes différentes. Après une correspondance, j'aimerais imprimer les lignes correspondantes des deux fichiers fusionnés horizontalement (c'est-à-dire côte à côte, semblable à la pâte BASH). Les fichiers ont 12 colonnes.

En regardant partout, je n'ai trouvé aucune question similaire, ce qui me fait vraiment craindre de trop réfléchir au problème.

En termes simples, l'objectif serait de "correspondre à la colonne 1 (dans fichier 1) avec la colonne 2 (dans le fichier 2) "et" la colonne 2 (dans le fichier 1) avec la colonne 1 (dans le fichier 2) "et seulement ensuite imprimer les lignes correspondantes respectives côte à côte.

Un exemple:

File1.tsv

awk 'NR==FNR{a[$2]=$1 && a[$1]=$2;next} {print $0,a[$0]}' file1 file2

File2.tsv

1   A   ExtraInfo   A   1   ExtraInfo
3   C   ExtraInfo   C   3   ExtraInfo
4   D   ExtraInfo   D   4   ExtraInfo

Sortie souhaitée:

D   4   ExtraInfo
B   7   ExtraInfo
E   9   ExtraInfo
C   3   ExtraInfo
A   1   ExtraInfo

Bien que je n'ai trouvé personne avec le même problème, j'ai trouvé des questions similaires qui m'ont amené à penser qu'awk serait probablement mon meilleur pari. Malheureusement, je suis toujours une pomme de terre complète avec elle, donc mes tentatives se limitent à essayer d'adapter le code à partir de questions similaires.

J'ai essayé ce qui suit, en vain:

1   A   ExtraInfo
2   B   ExtraInfo
3   C   ExtraInfo
4   D   ExtraInfo
5   E   ExtraInfo


8 commentaires

ressemble à une requête avec deux colonnes d'index


En supposant qu'il s'agit d'un exercice d'apprentissage, je ne vous donnerai pas de réponse complète (veuillez me dire si je me trompe) mais je vous suggère de rechercher la commande Unix join , qui est utilisable depuis bash .


dans une autre langue par défaut sur le terminal GNOME Un "terminal" ne détermine pas la langue que vous utilisez ... C'est comme si une imprimante ne détermine pas si vous imprimez des documents en anglais ou dans une autre langue, il imprime juste. Un terminal s'affiche simplement.


Cela semble fonctionner: repl.it/repls/ThoughtfulGlamorousLocks


@Simon Non, ce n'est pas un exercice d'apprentissage. Désolé, j'aurais dû fournir plus de détails, mais je ne voulais pas encombrer le message. J'essaie de traiter la sortie tabulaire d'un algorithme d'alignement de protéines. J'ai une méthode semi-automatisée, mais j'essayais d'accélérer les choses dans la mesure du possible. À propos de rejoindre , je vais essayer de l'explorer merci! Peut-il fusionner des lignes sur plusieurs champs dans le même fichier? J'ai vraiment besoin de correspondre aux 4 colonnes que j'ai mentionnées. Encore une fois, merci pour l'aide!


@KamilCuk Désolé pour la faute! Je voulais dire dans n'importe lequel des langages qui sont fournis par défaut avec Linux (bash, awk, perl, etc.) et même cela je suppose que je voulais dire spécifiquement Ubuntu. Merci pour la correction, ne le prenez pas trop personnellement.


@alecxs Désolé si c'est une question évidente, mais pouvez-vous élaborer? Même pointer vers une documentation ou un manuel serait apprécié, merci!


Veuillez réviser la question pour indiquer que les fichiers .tsv ont 12 colonnes . L'exemple de sortie montre un exemple pour 3 colonnes. Veuillez réviser l'exemple de sortie pour montrer ce qui doit être fait avec les 9 autres colonnes de chaque fichier.


3 Réponses :


2
votes
  1. Extraire les colonnes 1 et 2 des deux fichiers dans une colonne distincte
  2. Joindre des fichiers.
  3. Les travaux suivants sur repl :

    join -t$'\t' -14 -24 -o 1.1,1.2,1.3,2.1,2.2,2.3 <(
        awk -v F='\t' -v OFS='\t' '{NF++;$4=$2$1}1' File1.tsv |
        sort -k4) <(
        awk -v F='\t' -v OFS='\t' '{NF++;$4=$1$2}1' File2.tsv |
        sort -k4)
    

    Si vous souhaitez conserver l'ordre de tri du premier fichier, numérotez les lignes du premier fichier avec nl , effectuez la jointure, puis triez à nouveau les numéros de ligne avec trier -k1 et supprimer les numéros de ligne avec cut.


2 commentaires

Merci pour la réponse! Désolé, je n'ai pas mentionné le fait que ce n'était pas pertinent, mais les fichiers ont en fait 12 colonnes séparées par des tabulations! Je vois où cela changerait les options de join , mais qu'en est-il de la partie ´awk`?


Le NF ++ incrémente le nombre de colonnes et $ 4 = affecte à la dernière colonne. Je suppose que cela pourrait être généralisé par $ NF = (ou même $ (NF ++) = , ach that awk)



2
votes

REMARQUE : après que OP a fourni plus de détails (par exemple, les fichiers ont 12 colonnes), j'ai ajouté les modifications suggérées par @ karafka dans ma réponse.


Hypothèses:

  • chaque paire 1ère / 2ème colonne est unique dans un fichier (c'est-à-dire qu'au plus une ligne correspond à chaque fichier)
  • La troisième colonne ne contient pas de tabulation (c'est-à-dire que chaque fichier délimité par des tabulations a un total de 3 colonnes)
  • la sortie est triée par 1ère et 2ème colonnes (c'est-à-dire sans tenter de maintenir un ordre basé sur le contenu des fichiers source)
  • Une solution awk / sort :

    1       A       ExtraInfo       A       1       ExtraInfo
    3       C       ExtraInfo       C       3       ExtraInfo
    4       D       ExtraInfo       D       4       ExtraInfo
    

    REMARQUE : supprimez les commentaires pour désencombrer le code .

    L'exécution de ce qui précède sur les exemples de fichiers de données génère:

    awk -F"\t" '                                          # input delimiter is a tab
    BEGIN         { OFS=FS }                              # output delimiter is also a tab
    NR==FNR       { a[$1,$2]=$0 ; next }                  # store first file line in array using fields 1 & 2 as index 
    ($2,$1) in a  { print a[$2,$1],$0 }                   # if array entry exists with first 2 fields as index (in reverse order) then print array element==matching-line-from-file1 and $0==current-line-from-file2 to stdout
    ' file1.tsv file2.tsv | sort                          # sort output from awk [optional]
    


    8 commentaires

    si $ 3 vaut zéro, la condition échouera, mieux vaut la remplacer par ( $ 2, $ 1) dans un


    @karakfa; oui, je pensais à l'approche ... dans une approche mais je n'étais pas sûr à 100% de l'aspect multi-index; mis à jour la réponse; Merci


    @ markp-fuso Merci pour la réponse! À propos de vos hypothèses (et désolé, je n'ai pas mentionné ces détails): -1st: Chaque paire est unique dans chaque fichier. -2ème: Il n'y a pas que 3 colonnes, désolé! Les fichiers ont en fait 12 colonnes séparées par des tabulations, elles n'étaient tout simplement pas importantes pour trouver la correspondance. -3rd: L'ordre n'est en fait pas pertinent, tant que les lignes sont correctement alignées à 100%. Désolé pour cela aussi, mon exemple aurait pu être meilleur.


    pour ce changement à a [$ 1, $ 2] = $ 0 dans la deuxième ligne et imprimer un [$ 2, $ 1], $ 0 dans la ligne suivante.


    @ DiogoL.A. re: 12 colonnes ... J'ai mis à jour la réponse pour inclure les modifications suggérées par karakfa (stockage / impression $ 0 == ligne entière); qui devrait (frapper du bois) adresser vos détails supplémentaires (???)


    @ markp-fuso Merci beaucoup Mark, cela a fonctionné! Et une solution aussi simple et bien expliquée! Je me demandais, si les paires n'étaient pas uniques, mais leur ordre importait, serait-il possible d'inclure une commande '' vu [$ 1] dans le code afin qu'elle ne garde que la première correspondance pour la colonne 1, fichier 1, (considéré comme le meilleur)?


    @ DiogoL.A. oui, c'est probablement possible; Je suggère a) de prendre le code ci-dessus et d'essayer de le modifier pour faire ce que vous voulez et si vous rencontrez un barrage routier, b) ouvrez une nouvelle question en vous assurant de fournir le plus de détails possible (exemple de données, résultat attendu , ce que tu as essayé)


    @ markp-fuso Merci, je vais essayer de jouer avec!



    1
    votes

    Utilisation de sort , paste et GNU egrep :

    paste File1.tsv <(sort File2.tsv) | 
    egrep '^(\w)\W*(\w)\W*(\w*\W*){10}\2\W*\1\W'
    

    Sortie:

    1   A   ExtraInfo   A   1   ExtraInfo
    3   C   ExtraInfo   C   3   ExtraInfo
    4   D   ExtraInfo   D   4   ExtraInfo
    

    Comment ça marche:

    Notez comment la sortie est, (au moins pour les éléments correspondants), une liste de palindromes partiels - mais les lignes à omettre ne sont pas palindromes.

    Commencez par trier le File2.tsv non trié, puis collez les deux fichiers ensemble.

    GNU grep fournit des expressions de retour qui permettent de rechercher des chaînes palindromiques.

    S'il y a plus de colonnes, remplacez le {1} par le nombre de colonnes supplémentaires qu'il y a. Donc, s'il y avait 12 colonnes dans chaque fichier .tsv , changez le {1} en {10} , comme ça:

    paste File1.tsv <(sort File2.tsv) | 
    egrep '^(\w)\W*(\w)\W*(\w*\W*){1}\2\W*\1\W'
    


    2 commentaires

    Désolé @agc, mais l'exécution de cette commande est retournée vide. Serait-ce parce que mes fichiers ont en fait 12 colonnes (désolé j'ai oublié de le mentionner, au fait)?


    @ DiogoL.A., Voir la réponse révisée.