Remover os mesmos valores de coluna

6

Eu tenho um arquivo muito grande e quero removê-lo se o valor da coluna for 9.

Amostra:

O valor do meu arquivo é o seguinte:

1 5 8 3 5 9 5 7 6 9
2 5 7 4 2 9 7 6 3 1
5 9 7 4 1 9 5 7 9 1

Eu quero apagar todas as colunas onde o valor, em todas as linhas, é 9 (o tamanho da minha coluna é muito grande, então eu não posso verificar a primeira coluna = 9 segunda coluna = 9 ... etc). Eu preciso de um script dinâmico.

O resultado deve ser assim:

1 5 8 3 5 5 7 6 9
2 5 7 4 2 7 6 3 1
5 9 7 4 1 5 7 9 1

Sou novo e experimentei muitas coisas e não o fiz.

Como posso fazer isso?

Obrigado pela sua ajuda

    
por John 23.03.2015 / 13:10

5 respostas

1

Em python:

#! /usr/bin/env python3

import sys
# Get the numbers
numbers = [[int(x) for x in line.strip().split()] for line in sys.stdin] 
# Get indexes of 9 in sets for each row
index_9 = (set(x for x, num in enumerate(line) if num == 9) for line in numbers)  

common_column = next(index_9).intersection(*index_9)

for line in numbers:
    print(' '.join((str(num) for x, num in enumerate(line) if x not in common_column)))
    
por 23.03.2015 / 18:22
1

Esse método awk pressupõe que cada linha tenha o mesmo número de campos ... (como mostrado no exemplo dado na pergunta). Também pressupõe que não haja campos vazios.

cat <<EOF >file
1 5 8 3 5 9 5 7 6 9
2 5 7 4 2 9 7 6 3 1
5 9 7 4 1 9 5 7 9 1
EOF

awk '{ for (c=1; c<=NF; c++) a[NR,c]=$c }
 END { for (c=1; c<=NF; c++) { 
         vc="" # values in column
         for (r=1; r<=NR; r++) { 
           vc = vc " " a[r,c]  }
         if ( ! gensub( /[9 ]/,"","g",vc) ) {
           for (r=1; r<=NR; r++) {
             a[r,c] = "" } }
       }
       for (r=1; r<=NR; r++) {
         for (c=1; c<=NF; c++) {
           if ( a[r,c] ) printf a[r,c]" " } 
         print "" }
     }' file

# output
1 5 8 3 5 5 7 6 9 
2 5 7 4 2 7 6 3 1 
5 9 7 4 1 5 7 9 1
    
por 23.03.2015 / 18:56
1

Aqui está uma possível abordagem usando bash / GNU coreutils, que não requer muito armazenamento:

  1. cut o arquivo coluna-a-coluna e registra os índices de quaisquer colunas que não consistam inteiramente de 9s; Se você souber quantas colunas seu arquivo tem (neste caso 10), pode ser tão simples quanto

    for ((i=1;i<11;i++)); do 
      [[ $(cut -d' ' -f${i} file | sed '/^9$/d' | wc -l) -eq 0 ]] || a+=($i)
    done
    

    (usando o fato de que apenas colunas consistindo inteiramente de 9s têm comprimento 0 após todos os 9s serem deletados); então

  2. passe a lista de colunas a serem retidas para outro comando cut , usando uma alteração de IFS para transformar a matriz em uma lista separada por vírgulas

    (IFS=, ; cut -d' ' -f"${a[*]}" file)
    

Se a sua versão de cut suportar o sinal --complement , você poderá registrar as colunas que fazem contêm todos os 9s e cut , exceto os:

    for ((i=1;i<11;i++)); do
      [[ $(cut -d' ' -f${i} file | sed '/^9$/d' | wc -l) -eq 0 ]] && a+=($i)
    done

    (IFS=, ; cut -d' ' --complement -f"${a[*]}" file)
    
por 23.03.2015 / 19:53
1

Você pode tentar isso com o awk:

awk '{ t[NR] = $0; if (NR == 1) { for (i = 1; i <= NF; i++) if ($i == 9) { met[i] = 1 } } else { for (i = 1; i <= NF; i++) { if (met[i] != 1 || $i != 9) { met[i] = 0; } } } }
END { for (i = 1; i <= NR; i++) { n = split(t[i], a); for (z = 1; z <= NF; z++) if (met[z] != 1) { printf("%s ", a[z]); } print "" } }' file

ou a partir de esta resposta podemos construir params para cortar o que é mais rápido:

awk '{ t[NR] = $0; if (NR == 1) { for (i = 1; i <= NF; i++) if ($i == 9) { met[i] = 1 } } else { for (i = 1; i <= NF; i++) { if (met[i] != 1 || $i != 9) { met[i] = 0; } } } }
END { c = 0; s = " -f"; for ( i = 1; i <= NF; i++) { if (met[i] == 1) { if (c == 0) s = s " " i; else s = s "," i; c++; } }  s = s " -d\" \" "; if (c != 0) { system("cut --complement " s " " FILENAME); } else { system("cat " FILENAME) } }' file

E, claro, ambos estão abertos a críticas.

    
por 23.03.2015 / 14:42
0

Dada a informação na pergunta que posso apresentar:

awk '{for (i=1; i<NF; i++){ a[i]+=$i; b[i]=b[i]" " $i}} END{for (i=1; i<NF; i++) if (a[i]/NR!=9) {printf "%s\n", b[i]}}' same-column-values

Quando a função percorre o arquivo, calcula a soma na variável 'a' e acrescenta o valor a uma matriz de índice 'b'. Depois que o arquivo é completamente lido, o array de somas é iterado e se a soma dividida pelo número de registros (NR) não for igual a 9 do que a linha correspondente no array 'b' é impressa.

Isso me dá uma saída de 1 2 5 5 5 9 8 7 7 3 4 4 5 2 1 5 7 5 7 6 7 6 3 9

O inconveniente é que o resultado deve ser lido em cima- > em baixo e deve ser traduzido de cima para baixo e para a esquerda e para a direita.

Como alternativa, você pode obter uma lista de colunas que contém apenas valores de 9 com o seguinte comando:

awk '{for (i=1; i<NF; i++){ a[i]+=$i; b[i]=b[i]" " $i}} END{for (i=1; i<NF; i++) if (a[i]/NR==9){print i; }}' same-column-values

    
por 23.03.2015 / 14:31

Tags