¿Eliminar valores numéricos en ciertas columnas manteniendo los signos menos?


9

Tengo el siguiente marco de datos que continúa indefinidamente horizontal y verticalmente con números negativos solo en las columnas impares:

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

Y quiero las columnas completas segunda, cuarta y sexta (o cada columna par) y los signos menos solo de la primera, tercera y quinta (o cada columna impar), así que obtengo esto:

- 2   4 - 9
  3 - 5 - 11

Y finalmente termina con esto:

-2  4 -9
 3 -5 -11

Por lo tanto, necesito los valores de las columnas pares sin cambios y de las columnas impares, si hay un valor negativo, mantenga el - solo y si hay un valor positivo, deséchelo.

¿Hay alguna manera de hacer esto con awk / sed?

Esto es más o menos lo que consigo:

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

Cuando dice que su marco de datos continúa indefinidamente, ¿quiere decir horizontal o verticalmente? ¿Cuántas columnas tienes realmente?
terdon

Ambos. Mis datos de prueba son 3 filas por 3 columnas, pero los datos reales tienen números variables, diría que hasta 40 filas y 40 columnas.
Asustado el

Respuestas:


2

Aquí hay una manera:

$ 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

El awkscript recorre todas las columnas impares y establece su valor en -si son negativas y si no están vacías. Luego, sedelimina todos los espacios que siguen a -ay luego reemplaza múltiples espacios consecutivos con uno solo. Tenga en cuenta que esto significa que la alineación se romperá, ya que algunos campos tendrán dos caracteres o más y otros tendrán uno. Eso no será un problema si está trabajando con campos, simplemente no se ven bonitos.


4

El sedcamino:

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

Salida:

-2  4 -9
 3 -5 -11

La primera expresión mata la columna final si hay un número impar de columnas. Lo hace buscando 0 o más pares <number> <number>, donde el primer número puede ser negativo.

Editar: una sedsolución más corta , inspirada en @mikeserv:

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

Lo mismo con perl:

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

Otra forma con perl(probablemente la más limpia):

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

Esto funciona bien en mis datos reales siempre que agregue los puntos decimales en el script. ¡Gracias!
Asustado

@Asfound Ok, edité mi respuesta para admitir también puntos decimales.
lcd047

Espera, esto fallará si hay un valor negativo como el último campo (impar).
terdon

@terdon Falla si hay un número impar de columnas, sí. Pero hay exactamente 6 columnas, o "infinitamente muchas", y "infinitamente muchas" no es un número impar. :)
lcd047

El OP dijo que puede haber "hasta 40 columnas" :(
terdon

3

A perluno:

$ perl -anle 'BEGIN{$,=" "}
  print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2  4 -9
 3 -5 -11
  • -andividir la entrada a la @Fmatriz
  • BEGIN{$,=" "} establecer el separador de campo de salida en un espacio
  • grep{!($_%2)}0..$#Fobtener todos los índices pares en la @Fmatriz, que son índices de elementos impares
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}verifique si el elemento impar comienza con -, luego agregue -al siguiente elemento par, de lo contrario agregue un espacio

3

Como respuesta de @terdon pero sin el sed:

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

3

Una pythonsolucion

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 awksolución simple basada en las matemáticas :

$ 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
  • Pase del segundo campo ( i=2) al último campo ( i<=NF).
  • Multiplique el campo anterior ( $(i-1)) con -1 o 1.
  • Formatee la salida muy bien ( printf "%4s") e imprima una nueva línea final ( print "").

La única advertencia a esto es que si tiene un número impar de columnas, el último campo no mostrará nada en absoluto. Espero que esto sea lo que esperas. Aparentemente esto es lo que esperas. :)

(editado para trabajar con valores decimales y para que las condiciones del bucle estén más alineadas con la pregunta mientras se guardan 2 caracteres).


1

Necesitas olvidar lo negativo por completo, déjalo afuera. Desea consolidar dos campos, de izquierda a derecha. Eso es muy facil.

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

Observe cómo evito cualquier referencia al signo: cuando se procesa la entrada, el autómata aceptará solo espacios o números porque no entiende nada más; todo lo demás se ignora por completo y permanecerá en su lugar.

Cuando especifica un \{intervalo de repetición numérico \}para una \(subexpresión \), solo se \1hace referencia a la última aparición de esa expresión . Por lo tanto, puede simplemente apretar, o truncar, un intervalo de repetición tan fácilmente. Y debido a que exprimimos la repetición detrás del signo, si hay uno, la segunda aparición de ese patrón seguirá a cualquier signo que precediera al primero.

POSIX especifica el comportamiento descrito anteriormente para todas las aplicaciones compatibles con BRE, pero muy pocas sedlo hacen bien. GNU lo sedhace.

Por último, los espacios son solo para hacer que la aparición del patrón sea regular .

Por supuesto, esto nunca funcionará para ti. O, probablemente más correctamente, siempre funcionará para usted, pero nunca devolverá ningún resultado. ¿Cómo podría ser si el patrón es indefinido ?


Esto solo funcionará si hay un número par de campos.
terdon

@terdon - no - funciona para lo que sea.
mikeserv

No, pruébelo con un número impar de campos. El último está impreso y no debería estarlo.
terdon

@terdon: ¿por qué no debería ser así? ¿No hay un campo siguiente para cancelarlo? El autor de la pregunta afirma que desea eliminar las columnas impares seguidas de una columna par. A la última columna no le sigue una columna uniforme: hace exactamente lo que debería y elimina lo menos posible. Asumir que algunos datos deberían ir es una mala práctica en mi opinión.
mikeserv

No, no lo hacen: "Así que necesito los valores de las columnas pares sin cambios y de las columnas impares, si hay un valor negativo, mantenga el - solo y si hay un valor positivo, deséchelo". Los campos impares nunca deberían imprimirse, la única información que deberían transmitir es si eran negativos. El suyo imprime campos impares positivos.
terdon
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.