l'elaborazione del testo da un file non-flat (per estrarre le informazioni come se si fosse * * un file flat)

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

Domanda

Ho un insieme di dati longitudinali generati da una simulazione al computer che può essere rappresentato dalle seguenti tabelle ( 'var' sono variabili):

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

e

subject   name
subjectA  nameA
subjectB  nameB

Tuttavia, il file generato scrive un file di dati in un formato simile al seguente:

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)

Sono stato con uno script (python) a trattare tali dati in uscita in un file di testo piana, in modo che io possa importare in R, pitone, SQL, o awk / grep per estrarre informazioni - un esempio del tipo di informazioni desiderate da una singola query (in notazione SQL, dopo che i dati viene convertito in tabella) è la seguente:

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

Mi chiedo se c'è una soluzione più efficiente, come ognuno di questi file di dati possono essere ~ 100MB ciascuno (e ho centinaia di loro) e la creazione del file di testo piatta è in termini di tempo e occupa ulteriore spazio su disco rigido con ridondante informazione. Idealmente, vorrei interagire con i dati originali impostati direttamente per estrarre le informazioni che desidero, senza creare il file di testo extra piatto ... C'è una soluzione awk / perl per tali compiti che è più semplice? Sono abbastanza abile a testo-elaborazione in Python, ma le mie competenze in awk sono rudimentali e non ho conoscenza di Perl; Mi chiedo se questi o altri strumenti di dominio-specifici in grado di fornire una soluzione migliore.

Grazie!

Postscript: Wow, grazie a tutti! Mi dispiace che non posso scegliere le risposte di tutti @FM: grazie. Il mio script Python assomiglia il codice senza il passaggio filtrante. Ma la vostra organizzazione è pulito. @PP: Pensavo di essere già abile in grep, ma a quanto pare no! Questo è molto utile ... ma penso che grep diventa difficile quando mescolando il 'tempo' in uscita (che non sono riuscito a comprendere come un possibile scenario di estrazione nel mio esempio! Questo è il mio cattivo). @ Ghostdog74: Questo è semplicemente fantastico ... ma modificando la linea per ottenere 'subjectA' non è stato semplice ... (anche se sarò leggendo su più su awk nel frattempo e spero di Grok più tardi). @weismat: Ben detto. @ S. Lott: Questo è estremamente elegante e flessibile - Non stavo chiedendo una soluzione python (IC), ma questo si inserisce in modo pulito con il parse, filtri, e il quadro di uscita suggerita da PP, ed è sufficientemente flessibile per accogliere un certo numero di query diverse per estrarre diversi tipi di informazioni da questo file gerarchica.

Anche in questo caso, sono grato a tutti -. Ringraziamenti così tanto

È stato utile?

Soluzione

Questo è ciò che i generatori Python sono tutti circa.

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

È possibile utilizzare questo come un iteratore standard di Python

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

Altri suggerimenti

Se invece si è var1, var2, var3 su che corrispondono a un particolare argomento, allora si potrebbe provare il seguente comando:


  grep -A 1 'subjectB'

L'argomento della riga di comando -A 1 istruisce grep per stampare la linea abbinata e una linea dopo la linea abbinato (e in questo caso le variabili sono su una linea dopo che il soggetto).

Si potrebbe desiderare di utilizzare l'opzione -E per fare ricerca di grep per un'espressione regolare e ancorare la ricerca soggetto alla linea di inizio-di-(ad esempio grep -A 1 -E '^subjectB').

Infine l'uscita sarà ora composto da riga dell'oggetto e la linea di variabili che si desidera. Si consiglia di nascondere l'oggetto:


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

E si potrebbe desiderare di elaborare la linea di variabili:


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

L'opzione migliore sarebbe quella di modificare la simulazione al computer per produrre output rettangolare. Dando per scontato che non si può fare questo, ecco un approccio:

Al fine di essere in grado di utilizzare i dati in R, SQL, ecc avete bisogno di convertire da gerarchica a rettangolare un modo o nell'altro. Se si dispone già di un parser in grado di convertire l'intero file in un set di dati di forma rettangolare, si sono la maggior parte della strada. Il passo successivo è quello di aggiungere ulteriore flessibilità per il parser, in modo che possa filtrare i record di dati indesiderati. Invece di avere un convertitore di file, avrete un programma di utilità di estrazione di dati.

L'esempio che segue è in Perl, ma è possibile fare la stessa cosa in Python. L'idea generale è di mantenere una netta separazione tra (a) analisi, (b) il filtraggio, e (c) di uscita. In questo modo, si dispone di un ambiente flessibile, rendendo più semplice per aggiungere diversi metodi di filtraggio o di uscita, a seconda delle vostre esigenze immediate di dati-scricchiolio. È anche possibile impostare i metodi di filtraggio per accettare parametri (sia da linea di comando o un file di configurazione) per una maggiore flessibilità.

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 siete pigri e hanno abbastanza RAM, quindi avrei lavorato su un disco RAM al posto del file system finché ne ha bisogno subito.
Non credo che Perl o awk sarà più veloce di Python se sono solo ricodifica l'algoritmo corrente in una lingua diversa.

awk '/time/{f=0}/subjectB/{f=1;next}f' file
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top