Usando Perl para contar o número de números científicos em um arquivo

10

Como posso contar o número de números científicos em um arquivo? O arquivo também possui algumas linhas de cabeçalho que precisam ser ignoradas.

Uma parte do conteúdo do arquivo está abaixo.

FileHeaderLine1
FileHeaderLine2
FileHeaderLine3
FileHeaderLine4
2.91999996E-001 2.97030300E-001 3.02060604E-001 3.07090908E-001 3.12121212E-001 3.17151517E-001
3.22181821E-001 3.27212125E-001 3.32242429E-001 3.37272733E-001 3.42303038E-001 3.47333342E-001
3.52363646E-001 3.57393950E-001 3.62424254E-001 3.67454559E-001 3.72484863E-001 3.77515137E-001
3.82545441E-001 3.87575746E-001 3.92606050E-001 3.97636354E-001 4.02666658E-001 4.07696962E-001
4.12727267E-001 4.17757571E-001 4.22787875E-001 4.27818179E-001 4.32848483E-001 4.37878788E-001
4.42909092E-001 4.47939396E-001 4.52969700E-001

Então, como posso pular as quatro primeiras linhas do exemplo acima e contar o número de números científicos no arquivo?

AFP
fonte

Respostas:

14

Com o módulo principal Scalar::Util, você pode fazer:

$ perl -MScalar::Util=looks_like_number -anle '
    $count += grep { looks_like_number($_) } @F;
    END { print $count }
' file
33

Mais informações looks_like_numberpodem ver em perldoc perlapi.

cuonglm
fonte
+1 legal, eu não sabia sobrelooks_like_number
steeldriver
7

Usando o GNU grep

Você pode grepfazer isso usando as instalações do PCRE. Aliás, o mesmo padrão também pode ser usado no Perl:

$ grep -oP '\d+E[-+]?\d+' file.txt  | wc -l
33

Você também pode usar wc -wpara contar palavras, estou contando as linhas acima, mas grepretorna uma única correspondência em uma linha, para que realmente não importe nesse cenário.

Usando Perl

Para Perl, você pode usar este liner:

$ perl -lane '$c += grep /\d+E[-+]?\d+/, @F; END { print $c; }' file.txt 
33

Referências

slm
fonte
@StephaneChazelas - obrigado pela edição. Desculpe, eu só estou nos sistemas GNU, por isso tendem a esquecer esse ponto o tempo todo. Vou tentar não cometer esse erro.
slm
4

egrep irá funcionar:

egrep "[0-9].[0-9]E-[0-9]" YourFile | wc -w

ATUALIZAR:

se uma linha contiver um número e outra string, podemos usar awkpara resolver o problema:

awk -F' ' '{for(i=1;i<=NF;i++)if(!(i%1))$i=$i "\n"}1' YourFile | egrep "[0-9].[0-9]E-[0-9]" | wc -w ( or wc -l )
Nidal
fonte
Isso daria resultados incorretos se uma linha contivesse um número e outra string. A resposta acima que usa a opção -o do grep para gerar apenas correspondências é mais correta.
Johnny
Eu não sabia sobre -oPopção mencionado na resposta SLM antes, mas eu fixo meu problema usando awk@Johnny
Nidal
3

Supondo que você tenha apenas números científicos após a quarta linha, você pode fazer algo como abaixo.

tail -n +5 filename | wc - w

Para a entrada que você forneceu, a saída é 33 depois de executar o comando acima.

Ramesh
fonte
3

Se você precisar simplesmente contar o número de campos delimitados por espaço em branco após as linhas de cabeçalho em perl, acho que você poderia simplesmente

perl -lane '$sum += $#F+1 if $. > 4; END{print $sum}' file

Se você realmente precisar contar apenas números formatados cientificamente, uma abordagem pode ser procurar e substituir números de acordo com uma regex adequada e contar o número de substituições (a expressão de substituição perl retorna o número de substituições quando você a vincula a uma variável )

perl -lane '$sum += s/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?//g if $. > 4; END{print $sum}' file
chave de aço
fonte
2

Tudo se resume ao que você realmente deseja considerar um número científico , o que você pode esperar que sua entrada contenha e onde você pode aceitar encontrar esses números na entrada.

Por exemplo, em:

That's inferior to the LK2E2000 model.

Posso encontrar os números 0 ou 2 (inf e 2E2000) ou 3 (inf, 2E200, 0) (ou levados ao extremo, procurando todas as sequências de caracteres que formam um número válido: 17 (inf, 2, 2E2, 2E20, 2E200, 2E200, 2E2000, 2, 20, 200, 2000, 0, 00, 000, 0, 00, 0)).

Se você sabe que sua entrada possui apenas números no X.XXXXXXXXE-XXX, e que eles estão com suas próprias palavras, pode ser mais seguro procurar apenas isso em palavras inteiras como:

tr -s '[[:blank:]]' '[\n*]' | LC_ALL=C grep -xEc '[0-9]\.[0-9]{8}E-[0-9]{3}'

A idéia é obter uma palavra por linha e combinar a linha inteira ( -x) com o padrão desejado. Para permitir qualquer número de notação cientifico (-1,2e + 1234 ... contanto que haja um eou E), você pode alterar o padrão para:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])[eE][-+]?[0-9]+

Ou torne a e...peça opcional para permitir todos os tipos de números decimais de ponto flutuante:

[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9])([eE][-+]?[0-9]+)?

Isso tudo dá a mesma resposta para sua entrada específica, mas onde isso faria diferença é onde há entrada que se afasta do padrão estrito mostrado em sua amostra.

Stéphane Chazelas
fonte