4

我有一个由计算机模拟生成的纵向数据集,可以用下表表示(“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) 解决方案,但这完全适合解析、过滤、

再次,我感谢大家 - 非常感谢。

4

5 回答 5

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.
于 2010-02-15T13:14:27.217 回答
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'

于 2010-02-15T07:58:50.980 回答
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";
}
于 2010-02-15T12:43:56.760 回答
1

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

于 2010-02-15T08:16:46.907 回答
1
awk '/time/{f=0}/subjectB/{f=1;next}f' file
于 2010-02-15T09:03:48.957 回答