4 votes

Conversion des données en lignes en colonnes

Mon fichier d'entrée délimité par des tabulations se présente comme suit :

13435    830169  830264  a    95   y    16
09433    835620  835672  x    46
30945    838405  838620  a    21   c    19
94853    850475  850660  y    15
04958    865700  865978  c    16   a    98

Après les trois premières colonnes, le fichier indique la variable et sa valeur dans la colonne suivante. Je dois modifier la structure des données de sorte qu'après les trois premières colonnes, il y ait des colonnes pour les variables comme ceci :

                         a    x    y    c   
13435    830169  830264  95        16
09433    835620  835672       46
30945    838405  838620  21             19
94853    850475  850660            15
04958    865700  865978  98             16

Existe-t-il un code permettant de faire cela sous linux ? La taille du fichier est de 7,6 Mo et le nombre total de lignes est d'environ 450 000. Le nombre total de variables est de quatre.

Merci de votre attention.

2voto

M. Nejat Aydin Points 3551

En bash pur (nécessite bash 4.0 ou plus récent) :

#!/bin/bash

declare -A var

printf '\t\t\ta\tx\ty\tc\n'
while IFS=$'\t' read -ra fld; do
    var[a]=""  var[x]=""  var[y]=""  var[c]=""
    for ((i = 3; i < ${#fld[@]}; i += 2)); do
        var["${fld[i]}"]=${fld[i + 1]}
    done
    printf '%s\t' "${fld[@]:0:3}"
    printf '%s\t%s\t%s\t%s\n' "${var[a]}" "${var[x]}" "${var[y]}" "${var[c]}"
done < file

2voto

markp Points 2561

Hypothèses :

  • les quatre noms de variables ( a/c/x/y dans l'échantillon d'entrée) ne sont pas connues à l'avance
  • une variable est toujours suivie d'une valeur non vide
  • t
  • Le PO est d'accord pour imprimer les colonnes variables dans l'ordre alphabétique (le résultat souhaité par le PO ne précise pas si/comment les quatre colonnes variables doivent être triées).
  • l'ordre des lignes doit rester le même (ordre d'entrée = = ordre de sortie)
  • l'hôte dispose de suffisamment de mémoire pour contenir l'intégralité du fichier d'entrée en mémoire (par l'intermédiaire de la fonction awk tableaux) ; cela permet un passage unique du fichier d'entrée ; si la mémoire devait poser problème (c'est-à-dire si le fichier d'entrée ne pouvait tenir dans la mémoire), un codage/une conception différents seraient nécessaires (non abordés dans cette réponse).

Autre awk idée ... nécessitant GNU awk pour l'utilisation de tableaux multidimensionnels, ainsi que l'option PROCINFO["sorted_in"] construire :

awk '
BEGIN { FS=OFS="\t" }                             # input/output field delimiters = <tab>

      { first3[FNR]=$1 OFS $2 OFS $3              # store first 3 fields

        for (i=4;i<=NF;i=i+2) {                   # loop through rest of fields, 2 at a time
            vars[$i]                              # keep track of variable names
            values[FNR][$i]=$(i+1)                # store the value for this line/variable combo
        }
      }

END   { PROCINFO["sorted_in"]="@ind_str_asc"      # sort vars[] indexes in ascending order

        printf "%s%s", OFS, OFS                   # start printing header line ...
        for (v in vars)                           # loop through variable names ...
            printf "%s%s", OFS, v                 # printing to header line
        printf "\n"                               # terminate header line

        for (i=1;i<=FNR;i++) {                    # loop through our set of lines ...
            printf "%s",first3[i]                 # print the 1st 3 fields and then ...
            for (v in vars)                       # loop through list of all variables ...
                printf "%s%s",OFS,values[i][v]    # printing the associated value; non-existent values default to the empty string ""
            printf "\n"                           # terminate the current line of output
        }
      }
' inputfile

NOTE : Cette conception permet de traiter un nombre variable de variables.

À des fins de démonstration, nous utiliserons les fichiers d'entrée suivants, délimités par des tabulations :

$ cat input4                                         # OP's sample input file w/ 4 variables
13435   830169  830264  a       95      y       16
09433   835620  835672  x       46
30945   838405  838620  a       21      c       19
94853   850475  850660  y       15
04958   865700  865978  c       16      a       98

$ cat input6                                         # 2 additional variables added to OP's original input file
13435   830169  830264  a       95      y       16
09433   835620  835672  x       46      t       375
30945   838405  838620  a       21      c       19
94853   850475  850660  y       15      j       127     t       453
04958   865700  865978  c       16      a       98

En les passant au crible de l'outil awk script génère :

############# input4
                        a       c       x       y
13435   830169  830264  95                      16
09433   835620  835672                  46
30945   838405  838620  21      19
94853   850475  850660                          15
04958   865700  865978  98      16

############# input6
                        a       c       j       t       x       y
13435   830169  830264  95                                      16
09433   835620  835672                          375     46
30945   838405  838620  21      19
94853   850475  850660                  127     453             15
04958   865700  865978  98      16

1voto

David C. Rankin Points 2674

Si vous savez que vous avez 4 variables a , x , y , c et que le fichier est formaté comme un fichier séparé par des tabulations et que vous voulez le format exact de la sortie montrée, vous pouvez simplement utiliser un " Force brute "où vous vérifiez le contenu des champs 4 y 6 pour le nom de la variable et affiche la valeur du champ 5 ou 7 formaté comme indiqué en utilisant printf .

Par exemple, en connaissant les noms des variables, vous pouvez simplement éditer la ligne d'en-tête avant de traiter chaque enregistrement comme suit :

awk -F"\t" '
  FNR==1 { 
    print "\t\t\t  a    x    y    c"
  }
  {
    printf "%-8s%8s%8s  ", $1, $2, $3

    if ($4=="a")
      printf "%-5s", $5
    else if ($6=="a")
      printf "%-5s", $7
    else
      printf "%-5s", " "

    if ($4=="x")
      printf "%-5s", $5
    else if ($6=="x")
      printf "%-5s", $7
    else
      printf "%-5s", " "

    if ($4=="y")
      printf "%-5s", $5
    else if ($6=="y")
      printf "%-5s", $7
    else
      printf "%-5s", " "

    if ($4=="c")
      printf "%-5s\n", $5
    else if ($6=="c")
      printf "%-5s\n", $7
    else
      print ""
  }
' tabfile

Exemple d'utilisation/de sortie

Avec votre contribution en tabfile vous l'auriez fait :

$ awk -F"\t" '
>   FNR==1 {
>     print "\t\t\t  a    x    y    c"
>   }
>   {
>     printf "%-8s%8s%8s  ", $1, $2, $3
>
>     if ($4=="a")
>       printf "%-5s", $5
>     else if ($6=="a")
>       printf "%-5s", $7
>     else
>       printf "%-5s", " "
>
>     if ($4=="x")
>       printf "%-5s", $5
>     else if ($6=="x")
>       printf "%-5s", $7
>     else
>       printf "%-5s", " "
>
>     if ($4=="y")
>       printf "%-5s", $5
>     else if ($6=="y")
>       printf "%-5s", $7
>     else
>       printf "%-5s", " "
>
>     if ($4=="c")
>       printf "%-5s\n", $5
>     else if ($6=="c")
>       printf "%-5s\n", $7
>     else
>       print ""
>   }
> ' tabfile
                          a    x    y    c
13435     830169  830264  95        16
09433     835620  835672       46
30945     838405  838620  21             19
94853     850475  850660            15
04958     865700  865978  98             16

Ce qui permet d'obtenir le résultat souhaité. Cette approche en une seule passe sera également très efficace pour 450 000 lignes d'entrée. Comme c'est un peu long pour un script en ligne de commande, vous pouvez simplement le mettre dans un awk script et l'appeler avec le nom du fichier. N'hésitez pas à me contacter si vous avez des questions.

En tant que fichier script.

En utilisant un fichier script, il suffit de placer le contenu dans un fichier et de le rendre exécutable, par ex.

#!/usr/bin/awk -f

BEGIN { FS="\t" }
FNR==1 { 
  print "\t\t\t  a    x    y    c"
}
{
  printf "%-8s%8s%8s  ", $1, $2, $3

  if ($4=="a")
    printf "%-5s", $5
  else if ($6=="a")
    printf "%-5s", $7
  else
    printf "%-5s", " "

  if ($4=="x")
    printf "%-5s", $5
  else if ($6=="x")
    printf "%-5s", $7
  else
    printf "%-5s", " "

  if ($4=="y")
    printf "%-5s", $5
  else if ($6=="y")
    printf "%-5s", $7
  else
    printf "%-5s", " "

  if ($4=="c")
    printf "%-5s\n", $5
  else if ($6=="c")
    printf "%-5s\n", $7
  else
    print ""
}

Enregistré en tant que awkscript vous le feriez chmod +x awkscript et de l'exécuter :

$ ./awkscript tabfile
                          a    x    y    c
13435     830169  830264  95        16
09433     835620  835672       46
30945     838405  838620  21             19
94853     850475  850660            15
04958     865700  865978  98             16

1voto

Fravadona Points 970
input="\
13435   830169  830264  a   95  y   16
09433   835620  835672  x   46
30945   838405  838620  a   21  c   19
94853   850475  850660  y   15
04958   865700  865978  c   16  a   98
"

avec awk :

printf '\t\t\ta\tx\ty\tc\n'
echo -n "$input" |
awk -v vars='a x y c' '
  BEGIN {NV = split(vars,V)}
  {
     s = $1 "\t" $2 "\t" $3;
     delete a;
     for(i = 4; i < NF; i = i+2) a[$i] = $(i+1);
     for(i = 1; i <= NV; i++) s = s "\t" a[V[i]];
     print s
  }
'

avec du rubis :

printf '\t\t\ta\tx\ty\tc\n'
echo -n "$input" |
vars='a x y c' ruby -ane '
    BEGIN{v = ENV["vars"].split};
    h = Hash[*$F[3..-1]];
    puts $F[0..2].concat(v.map{|v| h[v]}).join("\t")
'

des sorties :

            a   x   y   c
13435   830169  830264  95      16  
09433   835620  835672      46      
30945   838405  838620  21          19
94853   850475  850660          15  
04958   865700  865978  98          16

1voto

Shawn Points 13289

En perl :

$ perl -lane '
    BEGIN { print join("\t", "", "", "", "a", "x", "y", "c"); }
    my %vars = @F[3..$#F];
    print join("\t", @F[0..2], @vars{qw/a x y c/});
  ' input.tsv
                        a       x       y       c
13435   830169  830264  95              16
09433   835620  835672          46
30945   838405  838620  21                      19
94853   850475  850660                  15
04958   865700  865978  98                      16

La quatrième colonne et toutes les suivantes sont considérées comme des paires clé/valeur pour une table de hachage, puis les valeurs variables qui y sont présentes sont extraites dans le bon ordre, en même temps que les trois premières colonnes. Fait un usage intensif de tranches .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X