2010-02-15 5 views
4

J'ai un ensemble de données longitudinales généré par une simulation par ordinateur qui peut être représenté par les tables suivantes ('var' sont des variables):Traitement du texte à partir d'un fichier non plat (pour extraire des informations comme s'il s'agissait d'un fichier plat)

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

et

subject name 
subjectA nameA 
subjectB nameB 

Cependant, le fichier généré enregistre un fichier de données dans un format similaire à ce qui suit:

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) 

J'ai utilisé un script (python) pour traiter ces données de sortie dans un fichier texte plat afin que je puisse l'importer dans R, python, SQL, ou awk/grep pour extraire des informations - un exemple du type d'information désiré à partir d'une seule requête (en notation SQL, après que les données sont converties à une table) est indiqué ci-dessous:

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

Je me demande s'il y a une solution plus efficace que chacun de ces fichiers de données peuvent être ~ 100 Mo chacun (et j'ai des centaines d'entre eux) et la création du fichier texte plat prend du temps et prend de l'espace disque supplémentaire avec des informations redondantes. Idéalement, j'interagirais directement avec l'ensemble de données original pour extraire l'information que je désire, sans créer le fichier texte extra plat ... Existe-t-il une solution awk/perl pour de telles tâches plus simple? Je suis très compétent en traitement de texte en python mais mes compétences en awk sont rudimentaires et je n'ai aucune connaissance pratique de perl; Je me demande si ces outils ou d'autres domaines spécifiques peuvent fournir une meilleure solution.

Merci!

Post-scriptum: Wow, merci à tous! Je suis désolé de ne pas pouvoir choisir les réponses de tout le monde @FM: merci. Mon script Python ressemble à votre code sans l'étape de filtrage. Mais votre organisation est propre. @PP: Je pensais que j'étais déjà compétent en grep mais apparemment pas! C'est très utile ... mais je pense que le grepping devient difficile quand on mélange le 'temps' dans la sortie (ce que j'ai omis d'inclure comme scénario d'extraction possible dans mon exemple! @ ghostdog74: C'est juste fantastique ... mais modifier la ligne pour obtenir 'subjectA' n'était pas simple ... (bien que je vais en lire plus sur awk en attendant et j'espère que je vais grok plus tard). @weismat: Bien indiqué. @ S.Lott: Ceci est extrêmement élégant et flexible - je ne demandais pas une solution python (ic) mais cela s'intègre parfaitement avec l'analyse, filtre, et cadre de sortie suggéré par PP, et est assez flexible pour accueillir un certain nombre de différentes requêtes pour extraire différents types d'informations de ce fichier hiérarchique.

Encore une fois, je suis reconnaissant à tous - merci beaucoup.

Répondre

4

Voici à quoi servent les générateurs 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 

Vous pouvez utiliser cela comme un itérateur standard Python

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

J'aime, très élégant. – mythz

2

Si tout ce que vous voulez est var1, var2, var3 sur correspondant à un sujet particulier, vous pourriez essayer la commande suivante:


    grep -A 1 'subjectB' 

L'argument de ligne de commande -A 1 indique grep d'imprimer la ligne associée et une ligne après la ligne correspondant (et dans ce cas les variables viennent sur une ligne après le sujet).

Vous pouvez utiliser l'option -E pour que grep recherche une expression régulière et ancre la recherche de sujet au début de la ligne (par exemple grep -A 1 -E '^subjectB').

Enfin, la sortie sera maintenant composée de la ligne d'objet et de la ligne de variable que vous voulez. Vous pouvez cacher la ligne d'objet:


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

Et vous pouvez traiter la ligne variable:


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

1

Si vous êtes paresseux et ont assez de RAM, alors je travaillerais sur un disque RAM au lieu du fichier système aussi longtemps que vous en avez besoin immédiatement.
Je ne pense pas que Perl ou awk sera plus rapide que Python si vous ne faites que recoder votre algorithme actuel dans une autre langue.

1
awk '/time/{f=0}/subjectB/{f=1;next}f' file 
2

La meilleure option serait de modifier la simulation par ordinateur pour produire une sortie rectangulaire. En supposant que vous ne pouvez pas faire cela, voici une approche:

Afin de pouvoir utiliser les données en R, SQL, etc., vous devez le convertir de hiérarchique à rectangulaire d'une manière ou d'une autre. Si vous avez déjà un analyseur qui peut convertir le fichier entier en un ensemble de données rectangulaire, vous y êtes pour la plupart. L'étape suivante consiste à ajouter une flexibilité supplémentaire à votre analyseur, afin qu'il puisse filtrer les enregistrements de données indésirables. Au lieu d'avoir un convertisseur de fichiers, vous aurez un utilitaire d'extraction de données.

L'exemple ci-dessous est en Perl, mais vous pouvez faire la même chose en Python. L'idée générale est de maintenir une séparation nette entre (a) l'analyse, (b) le filtrage et (c) la sortie. De cette façon, vous disposez d'un environnement flexible, ce qui facilite l'ajout de différentes méthodes de filtrage ou de sortie, en fonction de vos besoins immédiats de traitement des données. Vous pouvez également configurer les méthodes de filtrage pour accepter les paramètres (à partir de la ligne de commande ou d'un fichier de configuration) pour plus de flexibilité.

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"; 
} 
+0

+1: De l'expérience passée, je sais que l'analyse syntaxique des fichiers volumineux en hash peut consommer beaucoup de * * de mémoire. Je dois dire que cette solution sera probablement difficile à améliorer par rapport à l'avarice de la mémoire ... – Zaid

Questions connexes