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' 
Como encontrado
fonte
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.

terdon
fonte
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'
lcd047
fonte
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
cuonglm
fonte
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
     }'
meuh
fonte
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
iruvar
fonte
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.)

hjk
fonte
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 ?

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