Scambio di un numero illimitato di colonne


12

Ho un file con colonne. Vedi sotto per un esempio:

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

Vorrei scambiare tutte le colonne in cui la prima diventa l'ultima, la seconda diventa quella prima dell'ultima ... ecc.

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

C'è un liner ( awko sed) che lo fa?
So che uno può usare awkquando ci sono solo un paio di colonne, ma mi piacerebbe poterlo fare su file con migliaia di colonne.

taclo fa perfettamente per le linee.
Immagino che sto cercando l'equivalente per le colonne.

rev non ha funzionato per me, in quanto scambia anche contenuto nella colonna.


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

Risposte:


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

Ho lavorato troppo duramente per un compito così semplice. Sempre più semplice è meglio. +1
Birei,

10

Potresti farlo con un piccolo 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

Se non ti dispiace python, questo one-liner invertirà l'ordine delle colonne separate da spazio in ogni riga:

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$ 

Quanto sopra funziona anche 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$ 

Questo metodo è il più veloce di tutte le risposte che ho testato.
Peter

4

Un modo usando awk.

Contenuto di 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

Esegui il awkcomando seguente :

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 il seguente risultato:

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

Questo è lento, ma ha una funzione di riscatto. Mantiene la larghezza dei separatori di campo, quando sono più larghi di un singolo carattere. FWIW: se si esegue questo script due volte, il risultato è identico all'originale.

Ecco la sceneggiatura.

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"

Ecco alcuni confronti temporali. Il file di test conteneva 1 riga.

                      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

È possibile utilizzare tacsolo bisogno di trasporre l'ingresso prima e dopo. Questo può essere fatto con il calcolatore del foglio di calcolo sce il suo sidekick psc:

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

Come visto qui .

Funziona meglio quando tutte le colonne sono riempite.

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 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

modificare

Come notato da PeterO sc ha un limite rigido di 702 colonne, quindi questa è la dimensione massima supportata da questo metodo.


1
Converte i numeri in punti mobili (per me) ad es. 1-> 1.00. Inoltre, ricevo errori per le linee più larghe di 702 campi. Sembra essere correlato a un limite numerico di 32768 ... ma è abbastanza veloce.
Peter,

Non vedo la conversione in virgola mobile, ma l'aggiunta -Sal psccomando dovrebbe interpretare tutto come stringhe. Per quanto riguarda il limite della colonna 702, questo è un limite rigido perché sono supportate solo le colonne dalla A alla ZZ (26 + 26 * 26), aggiungerò un commento a riguardo.
Thor,

1
In realtà, il problema in virgola mobile è ok. Ho studiato più a fondo e ho scoperto che non avrei dovuto controllare i risultati mentre mi affretto fuori dalla porta .. I punti in virgola mobile si verificano solo dopo che ha raggiunto il limite di 702 ... è più veloce della risposta di Python per 1 riga di 702 campi, ma per 100 righe diventa il più lento di tutti i metodi indicati :( .. Deve avere un tempo di avvio più breve di Python.
Peter.O

3

Questa pipeline è più veloce dell'altra risposta più veloce di un fattore significativo (vedi risultati). Usa tre tac. Deve utilizzare 2 byte ASCII (\ x00- \ x7F) che non esistono nei tuoi dati.

\x00è in genere una buona scelta, così com'è \x01, ma è possibile utilizzare qualsiasi byte ASCII che non sia presente nei dati.

In questo esempio, SPACE e TAB come caratteri delimitatori. I delimitatori possono essere multi-byte o singoli. Il delimitatore di output è un singolo spazio.

Ecco il comando Il nome del file mostra numberof fields_xnumber of lines

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

Se si desidera / è necessario verificare i byte non utilizzati, è possibile verificare in anticipo con questo awkscript opzionale . Il tempo complessivo, anche quando si esegue questo script opzionale, è ancora significativamente più veloce di altri metodi (finora :) .. Ecco lo script di pre-elaborazione.

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

Questo è lo 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}}}

La seconda serie di tempi, per questo script, include char-ascii-not-in-streamil tempo.

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

Puoi anche farlo senza stampare f :

awk 'BEGIN{ORS=""} {for(k=NF;k>0;--k) {print $k; if (k==1) print "\n"; else print " "}} ' file
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.