【问题标题】:Reading a large file into Perl array of arrays and manipulating the output for different purposes将大文件读入 Perl 数组并为不同目的操作输出
【发布时间】:2011-03-02 06:21:51
【问题描述】:

我对 Perl 比较陌生,仅将其用于将小文件转换为不同格式以及在程序之间提供数据。

现在,我需要加强一点。我有一个 5,905 行长的 DNA 数据文件,每行有 32 个字段。字段不受任何限制,并且在行内长度不同,但每个字段在所有 5905 行上的大小相同。

我需要将文件中的每一行输入到一个单独的数组中,并将该行中的每个字段存储为自己的变量。我在存储一行时没有问题,但是在整个文件中连续存储每一行​​时遇到了困难。

这就是我将整个数组的第一行分成单个变量的方式:

my $SampleID = substr("@HorseArray", 0, 7);
my $PopulationID = substr("@HorseArray", 9, 4);
my $Allele1A  = substr("@HorseArray", 14, 3);
my $Allele1B = substr("@HorseArray", 17, 3);
my $Allele2A  = substr("@HorseArray", 21, 3);
my $Allele2B = substr("@HorseArray", 24, 3);

...等等。

我的问题是:1)我需要将 5905 行中的每一行存储为一个单独的数组。 2) 我需要能够根据样本 ID 引用每一行,或者根据总体 ID 引用一组行并对其进行排序。

一旦在变量中定义数据,我就可以很好地对数据进行排序和操作,我只是在用这些字段中的每一个构造一个多维数组时遇到了麻烦,所以我可以随意引用每一行。非常感谢任何帮助或指导。我已经翻遍了这里的问答部分,但还没有找到我的问题的答案。

【问题讨论】:

  • 请发布一些您正在使用的数据。
  • 我不确定是否有stackoverflow上的文件上传小程序,但我会发布文本限制允许:00292-97 py17 97101 129129 152164136163131224461109 124024610109 1241246101C000000 000000 00293-97 py18 89 97 129139 148154 179179 84 90 132134 167169 222222 105105 126128 164170 284292 000000 000000 000000 00294-97 PY17 91 97 129133 152154 177183 100100 134140 161163 240240 103105 120128 164166 290292 000000 000000 000000 00295-97 py18 97 97 131133 148162 177179 84100 132134 161167 240252 111111 124128 164166 284290 000000 000000 000000
  • post cmets 部分不存在格式,但这是 4 行 DNA 数据。样本 ID 以行开头,分别为 00292-97、00293-97、00294-97、00295-97。下一个字段是人口 ID(动物居住的地方),其他 30 个字段中的每一个都是我研究的特定 DNA 数据。
  • 您可以编辑您的问题以在此处发布示例数据(带格式)。
  • @HorseArray 中有什么内容?它是文件中的一行吗?整个文件?

标签: perl arrays sorting


【解决方案1】:

将每一行存储在它自己的数组中。您需要构建一个数据结构。从 perldoc 中阅读以下教程开始:

这里有一些启动代码:

use strict;
use warnings;

# Array of data samples. We could use a hash as well; which is better 
# depends on how you want to use the data.
my @sample;

while (my $line = <DATA>) {
    chomp $line;

    # Parse the input line
    my ($sample_id, $population_id, $rest) = split(/\s+/, $line, 3);

    # extract A/B allele pairs
    my @pairs;
    while ($rest =~ /(\d{1,3})(\d{3})|(\d{1,3}) (\d{1,2})/g) {
        push @pairs, {
            A => defined $1 ? $1 : $3,
            B => defined $2 ? $2 : $4,
        };
    }

    # Add this sample to the list of samples. Store it as a hashref so
    # we can access attributes by name
    push @sample, {
        sample     => $sample_id,
        population => $population_id,
        alleles    => \@pairs,
    };
}


# Print out all the values of alleles 2A and 2B for the samples in
# population py18. Note that array indexing starts at 0, so allele 2
# is at index 1.
foreach my $sample (grep { $_->{population} eq 'py18' } @sample) {
    printf("%s: %d / %d\n",
        $sample->{sample},
        $sample->{alleles}[1]{A},
        $sample->{alleles}[1]{B},
    );
}

__DATA__
00292-97 py17 97101 129129 152164 177177 100100 134136 163165 240246 105109 124124 166166 292292 000000 000000 000000
00293-97 py18 89 97 129139 148154 179179 84 90 132134 167169 222222 105105 126128 164170 284292 000000 000000 000000
00294-97 py17 91 97 129133 152154 177183 100100 134140 161163 240240 103105 120128 164166 290292 000000 000000 000000
00295-97 py18 97 97 131133 148162 177179 84100 132134 161167 240252 111111 124128 164166 284290 000000 000000 000000

【讨论】:

  • 迈克尔,这就是我想要做的......设置一个数组数组。每一行都是它自己的数组,包含 32 个元素,所有元素都包含在整个数组中,在我的脚本中称为 @HorseArray。
  • Brian,我认为“数组”实际上可能是指“哈希”,但如果您以前没有使用过哈希,您可能不会意识到这一点。
  • 迈克尔,谢谢!这确实符合我想去的地方。然而,重要的是每个等位基因都可以单独访问。如果您查看输入和我发布的初始代码,每个等位基因长度为 3 个字符,并且必须存储在单独的变量中。我能想到的唯一方法是使用 substr。 Sample ID 为 7char,popID 为 4char,每个等位基因为 3char。我使用的程序的输出将两个三字符等位基因放在一起,没有空格,这是一个问题,否则我可以使用 ' ' 作为分隔符来分隔每个值。
  • 例如,我需要打印群体 py18 中所有样本的等位基因 2A 和 2B,以及它们相关的 SampleID。
  • 我已经根据您的 cmets 修改了示例,但不清楚应该如何处理某些事情。例如,第二个样本的89 97 是一对还是两个等位基因?如果是两个,A/B 值是多少?
【解决方案2】:

我首先循环遍历这些行并将每行解析为一个字段哈希,然后我会为每个索引构建一个哈希。

my %by_sample_id;           # this will be a hash of hashes
my %by_population_id;       # a hash of lists of hashes
foreach (<FILEHANDLE>) {
    chomp;  # remove newline
    my %h;  # new hash
    $h{SampleID} = substr($_, 0, 7);
    $h{PopulationID} = substr($_, 9, 4);
    # etc...

    $by_sample_id{ $h{SampleID} } = \%h;   # a reference to %h
    push @{$by_population_id{ $h{PopulationID} }}, \%h;  # pushes hashref onto list
}

然后,您可以使用任一索引来访问您感兴趣的数据:

say "Allele1A for sample 123123: ", $by_sample_id{123123}->{Allele1A};
say "all the Allele1A values for population 432432: ", 
     join(", ", map {$_->{Allele1A}} @{$by_population_id{432432}});

【讨论】:

  • 嗯,看起来 Michael 的 split() 行可能比原始样本中的 substr() 调用更符合您的要求。
【解决方案3】:

我将假设这不是一次性程序,因此我的方法会略有不同。 我已经完成了大量的数据混搭工作,一段时间后,我厌倦了针对数据结构编写查询。

所以-

我会将数据输入SQLite 数据库(或其他 sql DB),然后使用 Perl DBI 编写 Perl 查询。这使复杂性大大超过了简单的“解析和破解”,但是在您编写了几个对相同数据进行查询的脚本之后,很明显这很痛苦,必须有更好的方式

您将有一个类似于此的架构 create table brians_awesome_data (id integer, population_id varchar(32), chunk1 integer, chunk2 integer...);

然后,在您使用一些 mobrule 和 Michael 的出色解析之后,您将循环并执行一些 INSERT INTO 您的 awesome_data 表。

然后,您可以为您的 SQL 程序使用 CLI 并执行“select ... where ...”查询以快速获取您需要的数据。

或者,如果它更具分析性/流水线,您可以使用 DBI 编写一个脚本并将数据放入您的分析例程中。

相信我,这比一遍又一遍地针对数据结构编写查询要好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-14
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    • 2012-09-30
    相关资源
    最近更新 更多