Removendo valores numéricos em determinadas colunas enquanto mantém sinais de menos?


9

Eu tenho o seguinte quadro de dados que continua indefinidamente na horizontal e na vertical, com números negativos apenas nas colunas ímpares:

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

E eu quero a 2ª, 4ª e 6ª colunas completas (ou todas as colunas pares) e os sinais de menos apenas da 1ª, 3ª e 5ª (ou todas as colunas ímpares), então entendi o seguinte:

- 2   4 - 9
  3 - 5 - 11

E, eventualmente, acabe com isso:

-2  4 -9
 3 -5 -11

Então, eu preciso dos valores das colunas pares inalteradas e das colunas ímpares, se houver um valor negativo, mantenha o - only e se houver um valor positivo, descarte-o.

Existe uma maneira de fazer isso com o awk / sed?

Isso é o mais longe que eu entendo:

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

Quando você diz que seu quadro de dados continua indefinidamente, você quer dizer horizontal ou verticalmente? Quantas colunas você realmente tem?
terdon

Ambos. Meus dados de teste são 3 linhas por 3 colunas, mas os dados reais têm números variados, eu diria até 40 linhas e 40 colunas.
Asfound

Respostas:


2

Aqui está uma maneira:

$ 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

O awkscript passa por todas as colunas ímpares e define seu valor como -negativo ou vazio. Em seguida, sedremove todos os espaços após -ae substitui vários espaços consecutivos por um único. Observe que isso significa que o alinhamento será interrompido, pois alguns campos terão dois caracteres ou mais e outros terão um. Isso não será um problema se você estiver trabalhando com campos, eles simplesmente não parecem bonitos.


4

O sedcaminho:

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

Resultado:

-2  4 -9
 3 -5 -11

A primeira expressão mata a coluna à direita se houver um número ímpar de colunas. Faz isso procurando 0 ou mais pares <number> <number>, onde o primeiro número pode ser negativo.

Edit: Uma sedsolução mais curta , inspirada em @mikeserv:

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

A mesma coisa com perl:

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

Outra maneira com perl(provavelmente a mais limpa):

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

Isso funciona bem nos meus dados reais, desde que eu adicione os pontos decimais no script. Obrigado!
Asfound

@Asfound Ok, editei minha resposta para também suportar pontos decimais.
Lcd047

Espere, isso falhará se houver um valor negativo como o último campo (ímpar).
terdon

@terdon Falha se houver um número ímpar de colunas, sim. Mas existem exatamente 6 colunas, ou "inifinitely many", e "infinitely many" não é um número ímpar. :)
lcd047

O OP disse que pode haver "até 40 colunas" :(
terdon

3

Um perl:

$ perl -anle 'BEGIN{$,=" "}
  print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2  4 -9
 3 -5 -11
  • -andividir entrada para @Fmatriz
  • BEGIN{$,=" "} definir separador de campo de saída para um espaço
  • grep{!($_%2)}0..$#Fobtém todos os índices pares na @Fmatriz, que são índices de elementos ímpares
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}verifique se o elemento ímpar começa com -, em seguida, acrescente -ao próximo elemento par; caso contrário, adicione um espaço

3

Como resposta de @ terdon, mas sem o sed:

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

3

Uma pythonsolução

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

Uma awksolução simples baseada em matemática :

$ 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
  • Loop do segundo ( i=2) ao último campo ( i<=NF).
  • Multiplique o campo anterior ( $(i-1)) por -1 ou 1.
  • Formate a saída bem ( printf "%4s") e imprima uma nova linha à direita ( print "").

A única ressalva é que, se você tiver um número ímpar de colunas, o último campo não exibirá nada. Espero que seja isso que você espera. Aparentemente, é isso que você espera. :)

(editado para trabalhar com valores decimais e para tornar as condições do loop mais alinhadas com a pergunta e salvar 2 caracteres.)


1

Você precisa esquecer completamente o negativo - deixe de fora. Você deseja consolidar dois campos - da esquerda para a direita. Isso é muito fácil.

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 como evito qualquer referência ao sinal - quando a entrada é processada, o autômato aceita apenas espaços ou números porque não entende mais nada - todo o resto é completamente ignorado e permanece no local.

Quando você especifica um \{intervalo de repetição numérica \}para uma \(subexpressão \), apenas a última ocorrência dessa expressão é \1referenciada novamente. Assim, você pode simplesmente apertar - ou truncar - um intervalo de repetição tão facilmente. E porque apertamos a repetição atrás do sinal - se houver um - a segunda ocorrência desse padrão seguirá qualquer sinal que precede o primeiro.

O comportamento descrito acima é especificado pelo POSIX para todos os aplicativos compatíveis com BRE, mas muito poucos seds acertam. GNU sedfaz.

Por fim, os espaços são apenas para tornar a ocorrência do padrão regular .

Obviamente, isso nunca funcionará para você. Ou, provavelmente, mais corretamente, sempre funcionará para você, mas nunca retornará nenhum resultado. Como poderia se o padrão fosse indefinido ?


Isso funcionará apenas se houver um número par de campos.
terdon

@terdon - não - funciona para qualquer coisa.
mikeserv

Não, tente com um número ímpar de campos. O último é impresso e não deveria ser.
terdon

@terdon - por que não deveria ser? Não existe um campo a seguir para cancelá-lo? O solicitante afirma que deseja remover colunas ímpares seguidas por uma coluna par. A última coluna não é seguida por uma coluna uniforme - faz exatamente o que deveria e remove o mínimo possível. Presumir que alguns dados devam ir é uma prática na minha opinião.
mikeserv

Não, eles não precisam: "Então, eu preciso dos valores das colunas pares inalteradas e das colunas ímpares, se houver um valor negativo, mantenha o - somente e se houver um valor positivo, descarte-o". Os campos ímpares nunca devem ser impressos; a única informação que eles devem transmitir é se foram negativos. O seu imprime campos ímpares positivos.
terdon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.