обработка текста из не плоского файла (для извлечения информации, как если бы это был * простой файл) - PullRequest
4 голосов
/ 15 февраля 2010

У меня есть набор продольных данных, сгенерированный компьютерным моделированием, который может быть представлен следующими таблицами (переменные 'var'):

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

и

subject   name
subjectA  nameA
subjectB  nameB

Однако созданный файл записывает файл данных в формате, подобном следующему:

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)

Я использовал (python) скрипт для обработки этих выходных данных в плоский текстовый файл, чтобы я мог импортировать их в R, python, SQL или awk / grep для извлечения информации - пример типа информация, требуемая из одного запроса (в нотации SQL после преобразования данных в таблицу), показана ниже:

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

Интересно, есть ли более эффективное решение, поскольку каждый из этих файлов данных может занимать ~ 100 МБ каждый (а их у меня сотни), а создание простого текстового файла занимает много времени и занимает дополнительное место на жестком диске с избыточным Информация. В идеале я бы взаимодействовал с исходным набором данных напрямую, чтобы извлечь нужную информацию, не создавая дополнительный плоский текстовый файл ... Существует ли решение awk / perl для таких задач, которое проще? Я довольно хорошо разбираюсь в обработке текста на python, но мои навыки в awk зачаточны, и у меня нет практических знаний по perl; Интересно, могут ли эти или другие доменные инструменты предоставить лучшее решение?

Спасибо!

Постскриптум: Вау, спасибо всем! Мне жаль, что я не могу выбрать ответы каждого @FM: спасибо. Мой скрипт Python напоминает ваш код без шага фильтрации. Но ваша организация чиста. @PP: Я думал, что уже хорошо владею grep, но, видимо, нет! Это очень полезно ... но я думаю, что grepping становится трудным при смешивании «времени» с выводом (который я не смог включить в качестве возможного сценария извлечения в моем примере! Это мое плохо). @ ghostdog74: Это просто фантастика ... но изменить строку, чтобы получить 'subjectA', было непросто ... (хотя в то же время я буду читать больше по awk и, надеюсь, позже у меня получится). @ Weismat: Хорошо сказано. @ S.Lott: Это чрезвычайно элегантно и гибко - я не просил решения на python (ic), но оно полностью вписывается в структуру синтаксического анализа, фильтрации и вывода, предложенную PP, и достаточно гибко, чтобы вместить несколько разные запросы для извлечения разных типов информации из этого иерархического файла.

Опять же, я благодарен всем - большое спасибо.

Ответы [ 5 ]

4 голосов
/ 15 февраля 2010

Это все, что представляют собой генераторы 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

Вы можете использовать это как стандартный итератор Python

for time, description, var1, var2, var3 in read_as_flat( someFile ):
    etc.
2 голосов
/ 15 февраля 2010

Лучшим вариантом было бы изменить компьютерное моделирование для получения прямоугольного вывода. Предполагая, что вы не можете сделать это, вот один из подходов:

Чтобы иметь возможность использовать данные в R, SQL и т. Д., Вам необходимо преобразовать их из иерархического в прямоугольный, так или иначе. Если у вас уже есть синтаксический анализатор, который может преобразовать весь файл в прямоугольный набор данных, вы в большинстве случаев там. Следующим шагом является добавление дополнительной гибкости вашему анализатору, чтобы он мог отфильтровывать нежелательные записи данных. Вместо того, чтобы иметь конвертер файлов, у вас будет утилита извлечения данных.

Пример ниже приведен на Perl, но вы можете сделать то же самое на Python. Общая идея состоит в том, чтобы поддерживать чистое разделение между (а) анализом, (б) фильтрацией и (в) выводом. Таким образом, вы получаете гибкую среду, позволяющую легко добавлять различные методы фильтрации или вывода в зависимости от ваших непосредственных потребностей в обработке данных. Вы также можете настроить методы фильтрации для приема параметров (из командной строки или из файла конфигурации) для большей гибкости.

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";
}
2 голосов
/ 15 февраля 2010

Если все, что вам нужно, это var1, var2, var3 при сопоставлении определенного предмета, то вы можете попробовать следующую команду:


  grep -A 1 'subjectB'

Аргумент командной строки -A 1 дает команду grep распечатать соответствующую строкуи одна строка после совпавшей строки (и в этом случае переменные идут в строке после темы).

Возможно, вы захотите использовать опцию -E, чтобы grep выполнял поиск регулярного выражения и привязывалпоиск по теме в начале строки (например, grep -A 1 -E '^subjectB').

Наконец, вывод теперь будет состоять из строки темы и строки переменной, которую вы хотите.Вы можете скрыть строку темы:


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

И вы можете обработать строку переменной:


  grep -A 1 'subjectB' |grep -v 'subjectB' |perl -pe 's/ /,/g'
1 голос
/ 15 февраля 2010
awk '/time/{f=0}/subjectB/{f=1;next}f' file
1 голос
/ 15 февраля 2010

Если вы ленивы и имеете достаточно ОЗУ, то я работаю на RAM-диске, а не на файловой системе, если вам это нужно немедленно.
Я не думаю, что Perl или awk будут быстрее, чем Python, если вы просто перекодируете свой текущий алгоритм на другой язык.

...