Inserir novas linhas com valores omissos (NA)

5

Eu gostaria de inserir novas linhas em arquivos de texto se houver valores ausentes. Eu tenho, por exemplo, o seguinte arquivo de texto (A.txt), para o qual a linha 5 está faltando. Além disso, como o arquivo deve ter 12 linhas, as linhas 11-12 também estão faltando.

1 2.30
2 3.01
3 3.22
4 3.34
6 3.01
7 2.90
8 2.99
9 3.00
10 3.02

Minha saída esperada é a seguinte. Para casos ausentes, uma linha deve ser adicionada com o número e o NA. Como você vê, isso aconteceu como desejado na linha 5, 11 e 12:

1 2.30
2 3.01
3 3.22
4 3.34
5 NA
6 3.01
7 2.90
8 2.99
9 3.00
10 3.02
11 NA
12 NA

Eu posso fazer isso usando o seguinte script:

f1=/my-directory/
echo "new file" > "$f1"/newfile.txt  

for i in {1..12}; do
l=$(awk '{print $1}' /"$f1"/A.txt | grep -wE ^$i /"$f1"/A.txt)
if grep --quiet -wE ^$i /"$f1"/A.txt; then echo "$l" >> "$f1"/newfile.txt; else echo "$i NA" >> "$f1"/newfile.txt; fi

done

Isso funciona bem. O problema é que eu preciso fazer isso para cerca de 600 arquivos contendo mais de 160000 linhas. A solução de loop, portanto, levaria muito tempo pesquisando em todas as linhas. Minha pergunta é: existe uma solução mais simples que poderia fazer isso?

    
por Ruthger Righart 17.03.2015 / 13:50

5 respostas

3

Você pode fazer isso com um script awk :

awk '{ while (NR + shift < $1) { print (NR + shift) " NA"; shift++ }; print } END { shift++; while (NR + shift < 13) { print (NR + shift) " NA"; shift++ } }' /tmp/test1

produzirá a saída necessária para /tmp/test1 (substitua isso por cada arquivo que você deseja processar).

De uma forma mais legível:

#!/usr/bin/awk -f
{
    while (NR + shift < $1) {
        print (NR + shift) " NA"
        shift++
    }
    print
}
END {
    shift++
    while (NR + shift < 13) {
        print (NR + shift) " NA"
        shift++
    }
}

Salve isto como um arquivo, diga fill-missing , torne-o executável, então você pode simplesmente executar

./fill-missing /tmp/test1

O script processa cada linha, acompanhando o delta esperado com o número da linha atual em shift . Portanto, para cada linha, se a linha atual ajustada não corresponder ao primeiro número da linha, ela imprime o número de linha apropriado seguido por NA e incrementa o delta; uma vez que os números de linha coincidam, imprime a linha atual. No final do processo, imprime todas as linhas faltantes necessárias para atingir 12.

    
por 17.03.2015 / 14:03
5

join funciona bem aqui:

join -a 1 -o 0,2.2 -e NA  <(seq 12)  A.txt  2>/dev/null

Eu jogo fora o stderr porque join reclama se o campo de junção não é lexicamente classificado.

    
por 17.03.2015 / 14:48
2

o arquivo awk

BEGIN { i=1 ; }
function upto(x) { while (i<x) printf "%d NA\n",i++ ;}
 { if ( $1 == i ) print ; upto($1) ; i++ ;}
END { upto(final+1) ;}

para ser chamado com

awk -f nl.awk -v final=12 /tmp/test1

todo o seu loop

cd /my/directory
ls | while read f
do
      awk -f ~/nl.awk -v final=12 $f > /an/other/dir/$f
done

onde

  • você coloca o programa awk no seu diretório $ HOME ( ~/nl.awk )
por 17.03.2015 / 14:09
2

Você também pode experimentar o Python:

#!/usr/bin/env python2
with open('file.txt') as f:
    check = 0
    for line in f:
        if int(line.split()[0]) == check + 1:
            check = int(line.split()[0])
            print line.rstrip()
        else:
            check = int(line.split()[0])
            print int(line.split()[0]) - 1, 'NA'
            print line.rstrip()
    print int(line.split()[0]) + 1, 'NA'
    print int(line.split()[0]) + 2, 'NA'

Aqui estamos comparando o número da linha int(line.split()[0]) com check + 1 , definimos o valor inicial de check como 0 . Se os valores forem iguais, imprimimos a linha, caso contrário, imprimimos o conteúdo desejado, ou seja, o número da linha ausente e NA . As duas últimas linhas são usadas para imprimir a 11ª e a 12ª linhas.

    
por 17.03.2015 / 14:33
0

Método de bash e join de Glenn Jackman com alguns ajustes, essa função obtém a contagem do arquivo de entrada e usa essa contagem como padrão:

# Usage: inlwmv file [ missing_value [ extra_lines ] ]
#      if unset, missing_value="NA", and extra_lines=0
inlwmv() { join -a 1  -o 0,2.2  -e "${2:-NA}" \
           <(seq $((${3:-0} + $(tail -n 1 "$1" | cut -d ' ' -f1))) | sort -k 1b,1)  \
           <(sort -k 1b,1 "$1") | \
           sort -g ; }

Para a pergunta do OP:

inlwmv A.file "" 2

Para esta pergunta duplicada a versão de Jackman falha no último valor de afile , ( join é bastante exigente com relação a pedidos, mas usar sort três vezes faz com que ela se comporte como obrigatório):

inlwmv afile 0
    
por 07.07.2016 / 11:04