处理来自非平面文件的文本(以提取信息,就像它*是*平面文件一样)

时间:2010-02-15 07:33:49

标签: python perl awk text-processing flat-file

我有一个由计算机模拟生成的纵向数据集,可以用下表表示('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'

我想知道是否有一个更有效的解决方案,因为每个数据文件每个大约100MB(我有数百个)并且创建平面文本文件非常耗时并占用额外的硬盘空间而且冗余信息。理想情况下,我会直接与原始数据集交互以提取我想要的信息,而无需创建额外的平面文本文件......是否有更简单的awk / perl解决方案来完成这些任务?我非常擅长python中的文本处理,但我在awk中的技能很简陋,而且我没有perl的工作知识;我想知道这些或其他特定领域的工具是否可以提供更好的解决方案。

谢谢!

后记: 哇,谢谢大家!对不起,我无法选择每个人的答案 @FM:谢谢。我的Python脚本类似于没有过滤步骤的代码。但是你的组织很干净。 @PP:我以为我已经精通grep但显然不是!这是非常有帮助的...但是我认为将'时间'混合到输出中时(在我的示例中我未能将其作为可能的提取方案包括在内)时,grepping变得困难!这是我的坏事。 @ ghostdog74:这真是太棒了......但修改线条以获得'subjectA'并不简单......(虽然我会在此期间阅读更多关于awk的内容,希望我稍后会知道)。 @weismat:说得好。 @ S.Lott:这非常优雅和灵活 - 我没有要求python(ic)解决方案,但这与PP建议的解析,过滤器和输出框架完全吻合,并且足够灵活,可以容纳一些不同的查询从此分层文件中提取不同类型的信息。

再一次,我感谢所有人 - 非常感谢。

5 个答案:

答案 0 :(得分:4)

这就是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.

答案 1 :(得分:2)

如果您想要的是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'

答案 2 :(得分:2)

最好的选择是修改计算机模拟以产生矩形输出。假设你不能这样做,这里有一种方法:

为了能够使用R,SQL等中的数据,您需要将其从层次结构转换为矩形。如果您已经有一个可以将整个文件转换为矩形数据集的解析器,那么您就可以使用它。下一步是为解析器添加额外的灵活性,以便它可以过滤掉不需要的数据记录。您将拥有一个数据提取实用程序,而不是文件转换器。

以下示例位于Perl中,但您可以在Python中执行相同的操作。一般的想法是在(a)解析,(b)过滤和(c)输出之间保持清晰的分离。这样,您就拥有了一个灵活的环境,可以轻松添加不同的过滤或输出方法,具体取决于您的即时数据处理需求。您还可以设置过滤方法以接受参数(来自命令行或配置文件)以获得更大的灵活性。

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

答案 3 :(得分:1)

如果你很懒并且有足够的内存,那么只要你需要它就可以在RAM磁盘而不是文件系统上工作。
如果您只是将当前算法重新编码为另一种语言,我认为Perl或awk不会比Python快。

答案 4 :(得分:1)

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