processamento de texto a partir de um arquivo plano (para extrair informações de como se *foram* um arquivo simples)

StackOverflow https://stackoverflow.com/questions/2264504

Pergunta

Eu tenho um longitudinal do conjunto de dados gerado por uma simulação de computador que pode ser representado pelas seguintes tabelas ('var' são variáveis):

time subject var1 var2 var3
t1   subjectA  ...
t2   subjectB  ...

e

subject   name
subjectA  nameA
subjectB  nameB

No entanto, o arquivo gerado grava um arquivo de dados em um formato semelhante à seguinte:

time t1 
  description
subjectA nameA
  var1 var2 var3
subjectB nameB
  var1 var2 var3
time t2
  description
subjectA nameA
  var1 var2 var3
subjectB nameB
  var1 var2 var3
...(and so on)

Tenho vindo a utilizar uma (python) script para este processo os dados de saída para um ficheiro de texto simples, para que eu possa importar para o R, python, SQL, ou awk/grep-lo para extrair informações - um exemplo do tipo de informação desejada a partir de uma única consulta (SQL notação, depois que os dados são convertidos para uma tabela) é mostrado abaixo:

SELECT var1, var2, var3 FROM datatable WHERE subject='subjectB'

Gostaria de saber se existe uma solução mais eficiente como cada um destes arquivos de dados podem ser ~100MB cada um (e eu tenho centenas deles) e criar o ficheiro de texto simples é um processo demorado e ocupa espaço adicional de disco rígido com informações redundantes.Idealmente, eu gostaria de interagir com o conjunto de dados original diretamente para extrair as informações que eu desejo, sem criar extra de televisão arquivo de texto...Há um awk/perl solução para tais tarefas, que é mais simples?Eu sou bastante experiente em processamento de texto em python, mas minhas habilidades em awk são rudimentares e eu não tenho nenhum conhecimento de trabalho de perl;Pergunto-me se esses ou outros específicos de um domínio de ferramentas pode fornecer uma solução melhor.

Obrigado!

Postscript: Uau, obrigado a todos!Eu sou pesaroso que eu não posso escolher todos respostas @FM:obrigado.O meu script de Python é semelhante o seu código sem a etapa de filtragem.Mas a sua organização é limpa.@PP:Eu pensei que eu já estava proficiente em grep mas, aparentemente, não!Isto é muito útil...mas eu acho que grepping torna-se difícil quando se mistura o 'tempo' para a saída (que eu não incluiu como uma possível extração cenário no meu exemplo!Esse é o meu mau).@ghostdog74:Isso é simplesmente fantástico...mas a modificação da linha, para obter 'subjectA' não foi simples...(embora eu vou estar lendo mais sobre awk, entretanto, e espero grok mais tarde).@weismat:Bem indicado.@S. Lott:Isso é extremamente elegante e flexível - eu não estava pedindo um python(ic) solução, mas isso se encaixa na forma limpa com a analisar, filtrar e saída do quadro sugerido pelo PP, e é flexível o suficiente para acomodar um número de diferentes consultas para extrair diferentes tipos de informações a partir desta hierárquico de arquivo.

Novamente, agradeço a todos, muito obrigado.

Foi útil?

Solução

É disso que se trata os geradores do Python.

def read_as_flat( someFile ):
    line_iter= iter(someFile)
    time_header= None
    for line in line_iter:
        words = line.split()
        if words[0] == 'time':
            time_header = [ words[1:] ] # the "time" line
            description= line_iter.next()
            time_header.append( description )
        elif words[0] in subjectNameSet:
            data = line_iter.next()
            yield time_header + data

Você pode usar isso como um iterador de python padrão

for time, description, var1, var2, var3 in read_as_flat( someFile ):
    etc.

Outras dicas

Se tudo que você quer é var1, var2, var3, mediante correspondência a um determinado assunto, então você pode tentar o seguinte comando:


  grep -A 1 'subjectB'

O -A 1 argumento de linha de comando instrui o grep a impressão de equiparação de linha e uma linha depois da correspondência de linha (e, neste caso, as variáveis vêm em uma linha após o assunto).

Você pode querer usar o -E opção para fazer o grep procura por uma expressão regular e ancorar o sujeito de pesquisa para o início da linha (e.g. grep -A 1 -E '^subjectB').

Finalmente, a saída será agora consistem na linha de assunto e a variável de linha que você deseja.Você pode querer ocultar a linha de assunto:


  grep -A 1 'subjectB' |grep -v 'subjectB'

E você pode querer processar a variável de linha:


  grep -A 1 'subjectB' |grep -v 'subjectB' |perl -pe 's/ /,/g'

A melhor opção seria modificar a simulação de computador para produzir saída retangular. Supondo que você não possa fazer isso, aqui está uma abordagem:

Para poder usar os dados em R, SQL, etc., você precisa convertê -los de uma maneira ou de outra retangular ou de outra. Se você já possui um analisador que pode converter o arquivo inteiro em um conjunto de dados retangulares, está a maior parte do caminho. A próxima etapa é adicionar flexibilidade adicional ao seu analisador, para que possa filtrar registros de dados indesejados. Em vez de ter um conversor de arquivo, você terá um utilitário de extração de dados.

O exemplo abaixo está em Perl, mas você pode fazer a mesma coisa em Python. A idéia geral é manter uma separação limpa entre (a) análise, (b) filtragem e (c) saída. Dessa forma, você tem um ambiente flexível, facilitando a adição de diferentes métodos de filtragem ou saída, dependendo das suas necessidades imediatas de trituração de dados. Você também pode configurar os métodos de filtragem para aceitar parâmetros (da linha de comando ou de um arquivo de configuração) para obter maior flexibilidade.

use strict;
use warnings;

read_file($ARGV[0], \&check_record);

sub read_file {
    my ($file_name, $check_record) = @_;
    open(my $file_handle, '<', $file_name) or die $!;
    # A data structure to hold an entire record.
    my $rec = {
        time => '',
        desc => '',
        subj => '',
        name => '',
        vars => [],
    };
    # A code reference to get the next line and do some cleanup.
    my $get_line = sub {
        my $line = <$file_handle>;
        return unless defined $line;
        chomp $line;
        $line =~ s/^\s+//;
        return $line;
    };
    # Start parsing the data file.
    while ( my $line = $get_line->() ){
        if ($line =~ /^time (\w+)/){
            $rec->{time} = $1;
            $rec->{desc} = $get_line->();
        }
        else {
            ($rec->{subj}, $rec->{name}) = $line =~ /(\w+) +(\w+)/;
            $rec->{vars} = [ split / +/, $get_line->() ];

            # OK, we have a complete record. Now invoke our filtering
            # code to decide whether to export record to rectangular format.
            $check_record->($rec);
        }
    }
}

sub check_record {
    my $rec = shift;
    # Just an illustration. You'll want to parameterize this, most likely.
    write_output($rec)
        if  $rec->{subj} eq 'subjectB'
        and $rec->{time} eq 't1'
    ;
}

sub write_output {
    my $rec = shift;
    print join("\t", 
        $rec->{time}, $rec->{subj}, $rec->{name},
        @{$rec->{vars}},
    ), "\n";
}

Se você é preguiçoso e tem RAM suficiente, eu trabalharia em um disco RAM em vez do sistema de arquivos, desde que você precise deles imediatamente.
Eu não acho que Perl ou Awk serão mais rápidos que o Python se você estiver apenas recodrando seu algoritmo atual em um idioma diferente.

awk '/time/{f=0}/subjectB/{f=1;next}f' file
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top