Rimozione di valori numerici in determinate colonne mantenendo i segni meno?


9

Ho il seguente frame di dati che continua indefinitamente in orizzontale e in verticale con numeri negativi solo nelle colonne dispari:

-1  2  3  4 -5  9
 2  3 -4  5 -6  11

E voglio le colonne complete 2a, 4a e 6a (o ogni colonna pari) e i segni meno solo dalla 1a, 3a e 5a (o ogni colonna dispari), quindi ottengo questo:

- 2   4 - 9
  3 - 5 - 11

E alla fine finiamo con questo:

-2  4 -9
 3 -5 -11

Quindi ho bisogno che i valori delle colonne pari siano invariati e delle colonne dispari, se c'è un valore negativo, mantieni il - solo e se c'è un valore positivo, scartalo.

C'è un modo per farlo con awk / sed?

Questo è circa quanto ottengo:

awk '{ for (i=2;i<=NF;i+=2) $i="" }1' FILE.txt | sed 's/[0-9,.]*//g' 

Quando dici che il tuo frame di dati continua indefinitamente, intendi in senso orizzontale o verticale? Quante colonne hai effettivamente?
terdon

Tutti e due. I miei dati di test sono 3 righe per 3 colonne ma i dati effettivi hanno numeri variabili, direi 40 righe e 40 colonne.
Trovato il

Risposte:


2

Ecco un modo:

$ awk '{for(i=1;i<=NF;i+=2){if($i<0){$i="-"}else{$i="";} }};1' file |
     sed 's/- */-/g; s/  */ /g'
-2 4 -9
 3 -5 -11

Lo awkscript va su tutte le colonne dispari e imposta il loro valore su -se sono negativi e vuoti in caso contrario. Quindi, sedrimuove tutti gli spazi che seguono a -e quindi sostituisce più spazi consecutivi con uno singolo. Si noti che ciò significa che l'allineamento verrà interrotto poiché alcuni campi avranno due o più caratteri e altri ne avranno uno. Questo non sarà un problema se stai lavorando con i campi, semplicemente non sembrano belli.


4

Il sedmodo:

sed -E '
    s/^(([ \t]*-?[ \t]*[0-9.]+[ \t]+[0-9.]+)*)[ \t]+-?[ \t]*[0-9.]+$/\1/;
    s/[0-9.]+[ \t]+([0-9.]+)/\1/g'

Produzione:

-2  4 -9
 3 -5 -11

La prima espressione uccide la colonna finale se c'è un numero dispari di colonne. Lo fa cercando 0 o più coppie <number> <number>, in cui il primo numero può essere negativo.

Modifica: una sedsoluzione più breve , ispirata a @mikeserv:

sed -E '
    s/[0-9.]+[ \t]*([0-9.]*)/\1/g;
    s/[- \t]*$//'

La stessa cosa con perl:

perl -lpe 's/^((\s*-?\s*[\d.]+\s*[\d.]+)*)\s+-?\s*[\d.]+$/$1/o; s/[\d.]+\s+([\d.]+)/$1/g'

Un altro modo con perl(probabilmente il più pulito):

perl -lpe '$a = 1; s/([\d.]+\s*)/$a++ % 2 ? "" : $1/eg; s/[-\s]*$//o'

Questo funziona bene sui miei dati effettivi purché aggiunga i punti decimali nello script. Grazie!
Trovato il

@Asfound Ok, ho modificato la mia risposta per supportare anche i punti decimali.
lcd047,

Aspetta, questo fallirà se c'è un valore negativo come ultimo (dispari) campo.
terdon

@terdon Fallisce se c'è un numero dispari di colonne, sì. Ma ci sono esattamente 6 colonne o "infinitamente molte" e "infinitamente molte" non è un numero dispari. :)
lcd047,

Il PO ha affermato che possono esserci "fino a 40 colonne" :(
terdon

3

Uno perl:

$ perl -anle 'BEGIN{$,=" "}
  print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2  4 -9
 3 -5 -11
  • -andividere l'input @Fnell'array
  • BEGIN{$,=" "} imposta il separatore del campo di output su uno spazio
  • grep{!($_%2)}0..$#Fottiene tutti gli indici pari @Fnell'array, che sono indici di elementi dispari
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}controlla se l'elemento dispari inizia con -, quindi aggiungi -all'elemento pari successivo, altrimenti aggiungi uno spazio

3

Come la risposta di @ terdon ma senza la sed:

awk '{ for(i=1;i<=NF;i+=2){
         if ($i<0) $(i+1)*=-1;
         $i = "";
       }
       print
     }'

3

Una pythonsoluzione

python -c 'from __future__ import print_function; 
import sys, math;
for line in sys.stdin:
  x = [int(y) for y in line.split()]
  print(*[int(math.copysign(b, a)) for a, b in zip(x[::2], x[1::2])], sep=" ")
' <file

2

Una semplice awksoluzione matematica :

$ cat <<M | awk '{for(i=2;i<=NF;i+=2){printf "%4s",($(i-1)<0?-1:1)*$i}print ""}'
-1  2  3  4 -5  9
2  3.2 -4  5 -6
M

  -2   4  -9
 3.2  -5
  • Ciclo dal secondo ( i=2) all'ultimo campo ( i<=NF).
  • Moltiplica il campo precedente ( $(i-1)) con -1 o 1.
  • Formatta bene l'output ( printf "%4s") e stampa una riga finale ( print "").

L'unica avvertenza è che se si dispone di un numero dispari di colonne, l'ultimo campo non visualizzerà nulla. Spero che questo sia quello che ti aspetti. Apparentemente questo è quello che ti aspetti. :)

(modificato per funzionare con valori decimali e per allineare le condizioni del ciclo con la domanda salvando 2 caratteri.)


1

Devi dimenticare del tutto il negativo - lascialo fuori. Si desidera consolidare due campi: da sinistra a destra. È molto facile

sed '   s/ *\(.*\)/\1 /
        s/\([0-9]*  *\)\{2\}/\1/g
        s/[ -]*$//
' <<\IN
-1  2  3  4 -5  9
 2  3 -4  5 -6  11
IN
-2  4 -9
3 -5 -11

Nota come evito qualsiasi riferimento al segno - quando l'input viene elaborato l'automa accetterà solo spazi o numeri perché non comprende nient'altro - tutto il resto viene ignorato completamente e rimarrà al suo posto.

Quando si specifica un \{intervallo di ripetizione numerica \}per una \(sottoespressione \), viene \1rimandata indietro solo l'ultima occorrenza di quell'espressione . Quindi puoi semplicemente stringere - o troncare - un intervallo di ripetizione così facilmente. E poiché stringiamo la ripetizione dietro il segno - se ce n'è uno - la seconda occorrenza di quel modello seguirà qualsiasi segno che precedeva il primo.

Il comportamento sopra descritto è specificato da POSIX per tutte le applicazioni compatibili con BRE, ma pochissimi sedriescono a farlo bene. GNU lo sedfa.

Infine, gli spazi servono solo a rendere regolare l' occorrenza del pattern .

Certo, questo non funzionerà mai per te. O, probabilmente più correttamente, funzionerà sempre per te, ma non restituirà mai alcun risultato. Come potrebbe se il modello è indefinito ?


Funzionerà solo se c'è un numero pari di campi.
terdon

@terdon - no - funziona per qualsiasi cosa.
Mikeserv,

No, provalo con un numero dispari di campi. L'ultimo è stampato e non dovrebbe esserlo.
terdon

@terdon - perché non dovrebbe essere? Non esiste un campo seguente per annullarlo? Il richiedente chiede di voler rimuovere le colonne dispari seguite da una colonna pari. L'ultima colonna non è seguita da una colonna pari: fa esattamente quello che dovrebbe e rimuove il meno possibile. Supporre che alcuni dati dovrebbero andare è una cattiva pratica secondo me.
Mikeserv,

No, non lo fanno: "Quindi ho bisogno che i valori delle colonne pari rimangano invariati e delle colonne dispari, se c'è un valore negativo, mantieni il - solo e se c'è un valore positivo, scartalo". I campi dispari non dovrebbero mai essere stampati, l'unica informazione che dovrebbero comunicare è se fossero negativi. Il tuo stampa campi dispari positivi.
terdon
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.