Échange d'un nombre illimité de colonnes


12

J'ai un fichier avec des colonnes. Voir ci-dessous pour un exemple:

a b c ... z  
1 2 3 ... 26

Je voudrais permuter toutes les colonnes où la 1ère devient la dernière, la seconde devient l'avant-dernière ... etc.

z y x ... a  
26 25 24 ... 1

Y a-t-il une doublure ( awkou sed) qui fait cela?
Je sais que l'on peut utiliser awkquand il n'y a que quelques colonnes, mais j'aimerais pouvoir le faire sur des fichiers avec des milliers de colonnes.

tacfait cela parfaitement pour les lignes.
Je suppose que je cherche l'équivalent pour les colonnes.

rev n'a pas fonctionné pour moi, car il échange également le contenu de la colonne.


perl -lane 'print join " ", reverse @F'

Réponses:


15
awk '{for(i=NF;i>0;i--)printf "%s ",$i;print ""}' file

J'ai fait trop de travail pour une tâche aussi simple. Toujours plus simple, c'est mieux. +1
Birei

10

Vous pouvez le faire avec un petit script python:

#!/usr/bin/env python

# Swaps order of columns in file, writes result to a file.
# usage: program.py input_file output_file

import sys, os

out = []

for line in open(sys.argv[1], 'r'):
    fields = line.split()
    rev = ' '.join(list(reversed(fields)))
    out.append(rev)

f = open(sys.argv[2], 'w')
f.write(os.linesep.join(out))

7

Si cela ne vous dérange pas python, cette ligne inversera l'ordre des colonnes séparées par des espaces dans chaque ligne:

paddy$ cat infile.txt 
a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
a e i o u
paddy$ python3 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))'
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
paddy$ 

Ce qui précède fonctionne également avec python2.7:

paddy$ python2.7 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))'
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
paddy$ 

Cette méthode est la plus rapide de tous les ansers que j'ai testés.
Peter.O

4

Utilisation à sens unique awk.

Contenu de infile:

a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
a e i o u

Exécutez la awkcommande suivante :

awk '{
    ## Variable 'i' will be incremented from first field, variable 'j'
    ## will be decremented from last field. And their values will be exchanged.
    ## The loop will end when both values cross themselves.
    j = NF; 
    for ( i = 1; i <= NF; i++ ) { 
        if ( j - i < 1 ) { 
            break;
        } 
        temp = $j; 
        $j = $i; 
        $i = temp; 
        j--; 
    }
    print;
}' infile

Avec le résultat suivant:

l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a

3

C'est lent, mais il a une fonction de rachat. Il conserve la largeur des séparateurs de champ, lorsqu'ils sont plus larges qu'un seul caractère. FWIW: Si vous exécutez ce script deux fois, le résultat est identique à l'original.

Voici le script.

awk '{ eix = length($0) 
       for( fn=NF; fn>0; fn--) { dix=eix
            while( substr($0,dix,1) ~ /[ \t]/ ) dix--
            printf "%s%s", substr($0,dix+1,eix-dix), $fn
            dix-=length($fn); eix=dix }
       print substr($0,1,dix)
    }' "$file"

Voici quelques comparaisons temporelles. Le fichier de test contenait 1 ligne.

                      fields           fields     
                      10,0000          10,000,000

user11136 {python} | real  0.029s     real  3.235s
reversible? no     | user  0.032s     user  2.008s
                   | sys   0.000s     sys   1.228s

jmp {python}       | real  0.078s     real  5.045s
reversible? no     | user  0.068s     user  4.268s
                   | sys   0.012s     sys   0.560s

rush {awk}         | real  0.120s     real  10.889s
reversible? no     | user  0.116s     user   8.641s
                   | sys   0.008s     sys    2.252s

petero {awk}       | real  0.319s     real  35.750s
reversible? yes    | user  0.304s     user  33.090s
                   | sys   0.016s     sys    2.660s

3

Vous pouvez utiliser tacvous avez juste besoin de transposer l'entrée avant et après. Cela peut être fait avec le calculateur de feuille de calcul scet son acolyte psc:

< infile psc -S -r | sc -W% - | tac | psc -S -r | sc -W% - > outfile

Comme on le voit ici .

Cela fonctionne mieux lorsque toutes les colonnes sont remplies.

dans le fichier

 a b c d e f g h i  j  k  l
 1 2 3 4 5 6 7 8 9 10 11 12
 A B C D E F G H I  J  K  L

outfile

  l  k  j i h g f e d c b a
 12 11 10 9 8 7 6 5 4 3 2 1
  L  K  J I H G F E D C B A

Éditer

Comme indiqué par PeterO sc a une limite stricte de 702 colonnes, c'est donc la taille maximale prise en charge par cette méthode.


1
Il convertit les nombres en virgules flottantes (pour moi), par exemple. 1-> 1.00. De plus, j'obtiens des erreurs pour les lignes de plus de 702 champs de large. Il semble être lié à une limite numérique de 32768 ... mais c'est assez rapide, asis.
Peter.O

Je ne vois pas la conversion en virgule flottante, mais l'ajout -Sà la psccommande devrait tout interpréter comme des chaînes. En ce qui concerne la limite de 702 colonnes, c'est une limite stricte car seules les colonnes A à ZZ sont prises en charge (26 + 26 * 26), je vais ajouter un commentaire à ce sujet.
Thor

1
En fait, la question de la virgule flottante est ok.I a approfondi, et je découvre que je ne devrais pas vérifier les résultats que je me dépêche à la porte .. Les points flottants se produire que après il frappe la 702 limite ... Il est plus rapide que les réponses python pour 1 ligne de 702 champs, mais pour 100 lignes, il devient la plus lente de toutes les méthodes données :( .. Il doit avoir un temps de démarrage plus court que python.
Peter.O

3

Ce pipeline est plus rapide que l'autre réponse la plus rapide d'un facteur significatif (voir les résultats). Il utilise tret tac. Il doit utiliser 2 octets ASCII (\ x00- \ x7F) qui n'existent pas dans vos données.

\x00est généralement un bon choix, \x01mais vous pouvez utiliser n'importe quel octet ASCII qui ne figure pas dans les données.

Dans cet exemple, ESPACE et TAB comme caractères de délimitation. Les délimiteurs peuvent être multi-octets ou simples. Le délimiteur de sortie est un espace unique.

Voici la commande. Le nom de fichier montre le numberof fields_xnumber of lines

 <"$file" tr ' \t\n' '\0\0\1' |tr -s '\0' '\n' |tac |tr '\n' ' ' |tr '\1' '\n'

Si vous voulez / devez vérifier les octets inutilisés, vous pouvez vérifier au préalable avec ce awkscript facultatif . Le temps global, même lors de l'exécution de ce script facultatif, est encore beaucoup plus rapide que les autres métodes (jusqu'à présent :) .. Voici le script de prétraitement.

o=($(<"$file" char-ascii-not-in-stream)); x="${o[0]}"; y="${o[1]}"
<"$file" tr ' \t\n' "$x$x$y" |tr -s "$x" '\n' |tac |tr '\n' ' ' | tr '$y' '\n' >"$file".$user

Voici le script awk: char-ascii-not-in-stream

#!/usr/bin/awk -f
{c[$0]} END{for(i=0;i<=127;i++) {if(sprintf("%c", i) in c);else {printf "\\%03o ",i}}}

Le deuxième ensemble de temps, pour ce script, inclut char-ascii-not-in-streamle temps de.

Peter.O {tr,tac,tr} ==== file_10_x10000
real    0m0.013s    0m0.015s
user    0m0.020s    0m0.020s
sys     0m0.008s    0m0.012s   

user11136 {python} ===== file_10_x10000
real    0m0.057s
user    0m0.048s
sys     0m0.008s

jmp {python} =========== file_10_x10000
real    0m0.160s
user    0m0.160s
sys     0m0.000s

rush {awk} ============= file_10_x10000
real    0m0.121s
user    0m0.120s
sys     0m0.000s

##############################################

Peter.O {tr,tac,tr} ==== file_1000_x1000
real    0m0.048s    0m0.059s
user    0m0.040s    0m0.040s
sys     0m0.040s    0m0.048s

user11136 {python} ===== file_1000_x1000
real    0m0.158s
user    0m0.136s
sys     0m0.028s

jmp {python} =========== file_1000_x1000
real    0m0.327s
user    0m0.320s
sys     0m0.008s

rush {awk} ============= file_1000_x1000
real    0m0.832s
user    0m0.820s
sys     0m0s012s

##############################################

Peter.O {tr,tac,tr} ==== file_1000000_x50
real    0m5.221s    0m6.458s
user    0m4.208s    0m5.248s
sys     0m2.624s    0m2.396s

user11136 {python} ===== file_1000000_x50
real    0m16.286s
user    0m10.041s
sys     0m5.148s

jmp {python} =========== file_1000000_x50
real    0m22.845s
user    0m20.705s
sys     0m1.140s

rush {awk} ============= file_1000000_x50
real    0m44.793s
user    0m43.583s
sys     0m0.848s

##############################################

0

Vous pouvez également le faire sans imprimer f :

awk 'BEGIN{ORS=""} {for(k=NF;k>0;--k) {print $k; if (k==1) print "\n"; else print " "}} ' file
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.