Intercambiar un número ilimitado de columnas


12

Tengo un archivo con columnas. Vea a continuación un ejemplo:

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

Me gustaría intercambiar todas las columnas donde la primera se convierte en la última, la segunda se convierte en la anterior a la última ... etc.

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

¿Hay un revestimiento ( awko sed) que hace esto?
Sé que se puede usar awkcuando solo hay un par de columnas, pero me gustaría poder hacerlo en archivos con miles de columnas.

tachace esto perfectamente para líneas.
Supongo que estoy buscando el equivalente para columnas.

rev no me ha funcionado, ya que también intercambia contenido en la columna.


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

Respuestas:


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

Hice un trabajo demasiado duro para una tarea tan simple. Siempre más simple es mejor. +1
Birei

10

Podrías hacerlo con un pequeño script de 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 no le importa Python, esta línea invertida invertirá el orden de las columnas separadas por espacios en cada línea:

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$ 

Lo anterior también funciona con 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$ 

Este método es el más rápido de todos los ansers que he probado.
Peter.O

4

Una forma de usar awk.

Contenido 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

Ejecute el siguiente awkcomando:

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

Con el siguiente resultado:

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

Esto es lento, pero tiene una función de canje. Mantiene el ancho de los separadores de campo, cuando son más anchos que un solo carácter. FWIW: Si ejecuta este script dos veces, el resultado es idéntico al original.

Aquí está el guión.

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"

Aquí hay algunas comparaciones de tiempo. El archivo de prueba contenía 1 línea.

                      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

Usted puede usar tacsólo tiene que transponer la entrada de antes y después. Esto se puede hacer con la calculadora de hoja de cálculo scy su compinche psc:

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

Como se ve aquí .

Esto funciona mejor cuando todas las columnas están llenas.

en archivo

 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

Editar

Como señaló PeterO, sc tiene un límite estricto de 702 columnas, por lo que ese es el tamaño máximo admitido por este método.


1
Convierte números en puntos flotantes (para mí), por ejemplo. 1-> 1.00. Además, obtengo errores para líneas de más de 702 campos de ancho. Parece estar relacionado con un límite numérico de 32768 ... pero es bastante rápido, también.
Peter.O

No veo la conversión de punto flotante, pero agregar -Sal psccomando debería interpretar todo como cadenas. Con respecto al límite de columna 702, ese es un límite difícil porque solo se admiten columnas A a ZZ (26 + 26 * 26), agregaré un comentario al respecto.
Thor

1
En realidad, el problema de la coma flotante está bien. Lo examiné más a fondo y descubrí que no debería verificar los resultados cuando me apresuro a salir por la puerta. Las coma flotante solo ocurren después de que alcanza el límite de 702 ... es más rápido que las respuestas de python para 1 línea de 702 campos, pero para 100 líneas se convierte en el más lento de todos los métodos dados :( .. Debe tener un tiempo de inicio más corto que python.
Peter.O

3

Esta tubería es más rápida que la otra respuesta más rápida por un factor significativo (ver resultados). Utiliza try tac. Necesita utilizar 2 bytes ASCII (\ x00- \ x7F) que no existen en sus datos.

\x00Por lo general, es una buena opción, \x01pero puede usar cualquier byte ASCII que no esté en los datos.

En este ejemplo, ESPACIO y TAB como delimitadores de caracteres. Los delimitadores pueden ser de varios bytes o simples. El delimitador de salida es un espacio único.

Aquí está el comando. El nombre del archivo muestra el numberof fields_xnumber of lines

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

Si desea / necesita verificar los bytes no utilizados, puede verificar de antemano con este awkscript opcional . El tiempo total, incluso cuando se ejecuta este script opcional, sigue siendo significativamente más rápido que otros métodos (hasta ahora :) .. Aquí está el script de preprocesamiento.

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

Este es el 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}}}

El segundo conjunto de tiempos, para este script, incluye char-ascii-not-in-streamel tiempo 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

También puedes hacerlo sin imprimir f :

awk 'BEGIN{ORS=""} {for(k=NF;k>0;--k) {print $k; if (k==1) print "\n"; else print " "}} ' file
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.