【问题标题】:"Perl : Add an Array Element from one Array into another Array"“Perl:将一个数组元素从一个数组添加到另一个数组”
【发布时间】:2012-06-22 19:00:47
【问题描述】:

我是 Perl 以及一般编程的绝对新手(不到一个月的经验)。

如果我要解决更大的问题,我会遇到一个需要解决的问题。

基本上,我有 2 个如下所示的数组:

@array1 = ('NM_1234' , '1452' , 'NM_345' , '5008' , 'NR_6145' , '256');
@array2 = ('NM_5673' , '2' , 'NM_345' , '5' , 'NR_6145' , '10');

@array1 包含 id 编号,后跟长度。 id号是核苷酸序列,length是序列的长度。

@array2 包含 id 编号,后跟 G-Quadruplex 结构的数量,因此某些序列仅包含 2 个此类结构,而其他序列包含 10 个或更多。

基本问题是,我需要在@array1(例如5008、256)中为每个匹配的id 号添加“长度数字”。

所以例如 NM_345 在两个数组中都匹配,我需要在其中添加 5008,这样最终的结果就会变成像 NM_345,5, 5008.

NR_6145 和其他类似匹配(@array2 中有超过 20,000 个 id 号码)

到目前为止,我已经能够编写可以在两个数组中搜索相同 ID 号的代码。这是代码:

#Enter file name
print "Enter file name: ";
$in =<>;
chomp $in;

open(FASTA,"$in") or die;

@data = <FASTA>; #Read in data        
$data = join ('',@data); #Convert to string
@data2 = split('\n',$data); #Explode along newlines

#Enter 2nd file name
print "\n\nEnter 2nd file name: ";
$in2=<>;
chomp $in2;

open(FASTA,"$in2") or die;
@entry =<FASTA>; #Read in data

$entry = join('',@entry); #Convert to string
@entry2 = split('\n',$entry); #Explode along newlines

my %seen;
for  $item (@data2) {
    if($item =~ /([0-9]+)/){
        push @{$seen{$key}}, $item;#WHAT IS THIS DOING? HOW?
    }
}

for my $item (@entry2) {
    if ($item =~ /([0-9]+)/){
        if (exists $seen{$key}) {
            print $item,"\n";
        };        
    }
}
exit;

我从这里的解决方案中派生了从 2 个数组中找到相同元素的代码,因此完全归功于 Chas.Owens:https://stackoverflow.com/a/1064929/1468737。 当然,我还不太了解这部分:

push @{$seen{$key}}, $item;#WHAT IS THIS DOING? HOW?

它似乎是一个散列值或什么的数组?

那么,现在如何将@array1 中的长度元素添加到@array2 中?我需要使用我认为的拼接命令,但是如何?

我想要的输出应该是这样的:

NM_345,5,5008 <br>
NM_6145,10,256<br>
etc

我还需要将此输出保存到一个文件中,然后分析该文件以查看长度和 G-四链体数之间是否存在任何相关性。

任何帮助或意见将不胜感激。

感谢您花时间解决我的问题!


编辑:此编辑是为了显示数据文件的外观。它们基本上是我编写的其他程序的 putput 文件。

我的第一个文件名为 Transcriptlength.fa,其中有超过 40,000 个 ID 号进入 @array1,如下所示:

NR_037701
3353

NM_198399
2414

NR_026816
601

NR_027917
658

NR_002777
1278

我的第二个文件,名为 Quadcount.AllGtranscripts.fa,有超过 20,000 个 ID 号码进入 @array2,如下所示:

NM_000014   
1

NM_000016   
3

NM_000017   
19

NM_000018   
2

NM_000019   
3

NM_000020   
30

NM_000021   
1

NM_000022   
2

NM_000023   
5

NM_000024   
1

NM_000025   
15

NM_000029   
5

【问题讨论】:

  • 每个文件中数据的格式是什么?
  • 有什么理由不能将这些数组声明为哈希?这将使我输入的解决方案更容易。
  • 我想第一个数组不能轻易转换为哈希。至少我在构建我的解决方案时完全假设:可以为每个序列存储多个长度。 )
  • 无论如何,你想要像my %data; $data{ 'a_sequence' } = ( $count, $length ); 这样的东西,它将一个数组引用分配给一个哈希键。阅读perldoc perreftut
  • ($count, $length) 不是参考,它是一个列表。应该改用[$count, $length]

标签: arrays perl bioinformatics


【解决方案1】:

看起来您在读取数据文件以及生成所需的输出时遇到了问题。除非您向我们展示文件数据的示例,否则我们无法帮助解决这部分问题,但这里有一个正确生成输出的解决方案。

最好将数据存储在散列中,因为这样可以直接访问给定序列 ID 的长度和结构计数。幸运的是,您所描述的数组可以通过简单的赋值轻松转换为哈希,因此这个简短的程序可以从您显示的数组中完成您想要的操作。

循环中的grep /\D/, @array2 列表仅通过仅选择那些包含非小数字符的元素来从@array2 中选择所有序列ID。我已经这样做了,以防序列显示的顺序很重要。在您的最终程序中,您可能应该直接从文件中处理数据,而不是将其读取到数组中,这样就不会成为问题。

use strict;
use warnings;

my @array1 = ( NM_1234 1452   NM_345 5008   NR_6145 256 );
my @array2 = ( NM_5673    2   NM_345    5   NR_6145  10 );

my %lengths = @array1;
my %counts = @array2;

for my $id (grep /\D/, @array2) {
  my $length = $lengths{$id};
  printf "%s,%s,%s\n", $id, $length, $counts{$id} if $length;
}

输出

NM_345,5008,5
NR_6145,256,10

更新

您的文件数据非常适合设置段落模式,其中记录在数据文件中由空行分隔。为此,您将 输入记录分隔符 变量 $/ 设置为空字符串 ""

这个修改后的程序从第一个文件中读取记录,将它们拆分为空格(空格包括空格、制表符和换行符等)并构建一个哈希%lengths,它将每个序列 ID 与其长度相关联。

对第二个文件做同样的事情,这次检查序列 ID 是否出现在哈希中。如果是,则输出完整的记录。

use strict;
use warnings;

my $fh;
my %lengths;

$/ = "";

open $fh, '<', 'Transcriptlength.fa'
    or die qq(Unable to open "Transcriptlength.fa": $!);

while (<$fh>) {

  my ($id, $length) = split;
  next unless $id;

  $lengths{$id} = $length;
}

open $fh, '<', 'Quadcount.AllGtranscripts.fa'
    or die qq(Unable to open "Quadcount.AllGtranscripts.fa": $!);

while (<$fh>) {

  my ($id, $count) = split;
  next unless $id;

  my $length = $lengths{$id};
  next unless $length;

  print join(',', $id, $count, $length), "\n";
}

很遗憾,您选择的样本数据不包含匹配的序列 ID,因此在针对该数据运行时,该程序没有输出。您的实际文件将更有效率。

【讨论】:

  • 哇鲍罗丁!感谢您的精彩回复和如此令人愉快的代码!确实,您是对的,我在读取数据文件时遇到了麻烦。我已经编辑了原始帖子,现在我已经包含了 2 个数据文件的样子。
  • 如果我的数据可以放入数组中的特定形式中,那么您提到的代码就可以很好地工作。不幸的是,情况似乎并非如此 :( 如何将我的数据从 2 个文件中获取到哈希表中?
  • 我怀疑您的文件有一个序列 ID,后跟同一行的相应数据。事实证明,您的代码比我想象的要近得多。我正在更新我的答案以从您的文件而不是从数组中读取。
  • @Neal:像这样的应用程序将从使用数据库中受益匪浅。我知道如果您以前没有使用过 SQL 或 DBI 模块,这可能是一个很大的飞跃,但是一个简单的 SQLite 数据库(不需要安装数据库引擎)可以轻松解决这个问题并保留可用数据为进一步的工作。
  • @鲍罗丁:先生您好!首先,由于过去 2 天以来我一直感到不适,因此为迟到的回复道歉。好的,先生的回答太简洁了!我的意思是我只是想知道我使用的是什么复杂的方法。在我所指的书“Beginning Perl for Bioinformatics”中没有任何内容可以让我准备好编写像您这样的算法。事实上,我看到你和raina77ow都使用了类似的逻辑。这两个程序都有一些全新的东西让我学习。例如,“next”和“unless”的使用对我来说是全新的!
【解决方案2】:

一个问题的问题太多了......但我们还是要走了:

push @{$seen{$key}}, $item;

%seen 是一个哈希(或关联数组),因此$seen{$key}%seen 恢复与该键$key 关联的值。然后将此值视为数组引用并使用@{} 运算符将其转换为数组。最后$item被添加到这个数组的末尾。

我不明白你说的长度是什么意思...你的意思是前面的数组长度?

并且要将其保存在文件中,您只需在脚本中 print() 并在执行脚本时重定向到文件,例如:

./my_perl_script.pl > my_output_file

文件输入也是如此,您并不需要open()close() 等。这更灵活,编码更快:

./my_perl_script.pl < my_input_file

这允许您以更简单的方式进行管道传输,并将数据从/传递到其他脚本/进程。当然两种重定向可以同时使用:

./my_perl_script.pl < my_input_file > my_output_file

此外,您甚至不需要保存到文件中(无论如何,拥有已处理数据的副本总是明智的)并且您可以将结果直接通过管道传输到其他进程,例如

./my_perl_script.pl | my_other_script

这适用于我使用过的所有操作系统(Windows、Linux、OS X、BSD)。

【讨论】:

  • 哇 m0skit0!非常感谢您花时间解决我的问题并耐心地给我写一个答案。 push @{{$seen{$key}} 的解释太棒了!非常感谢!对我来说,这是一个全新的数据结构,至少我所指的书:Beginning Perl for Bioinformatics 中没有涉及到它。我可以理解 {$seen{$key} 部分,但将其视为数组引用并将其转换为数组,哇!那是全新的!
  • “length”指的是与每个 id 关联的长度值,例如 id 'NM_345' 的长度值在 @array1 中是 '5008'
  • 那些节省输出的漂亮线条是如此时尚!当然,输出文件也保存了“输入文件名”和“输入第二个文件名”的用户提示。这是预期的吗?输入文件命令就像做梦一样!
  • 好的,但是如果您不介意的话,您的代码有点……混乱。例如,拥有@data 和@data2 数组是多余的,并且没有使用内存。检查raina77ow 的答案,它提供了更紧凑和有效的代码。关于输出文件中不需要的字符串,您可以使用 STDOUT 和 STDERR 来区分输出文件中您想要的内容。例如,使用print(STDERR "Hello\n"); 您不会将此 print() 重定向到您的文件。如果需要,您可以使用 2> 重定向运算符重定向 STDERR,即./my_perl_script.pl 2&gt; my_output_file。两者都可以使用。
【解决方案3】:

这个

 $data = join ('',@data); #转换成字符串
@data2 = split('\n',$data); #沿换行符展开 

不像你想象的那样创建你的数组。它只是重新创建了您开始使用的线条结构。我认为你的意思是用“,”逗号分开。使用调试工具。至少插入这样的打印块

打印连接(“:”,@data2); 

查看数组中的实际内容。

在继续下一条之前,让每一行都正常工作。然后,如果您无法弄清楚为什么某条线路无法正常工作,您可以在此处发布问题。

事实上,你很难在代码中说出你想要表达的意思,因为这些想法是不完整的。

【讨论】:

    【解决方案4】:

    更新:我将the link 留给原始答案的代码来说明抽象不同子任务(尤其是处理子任务)的概念。但是,如果您确定输入文件中的内容,您的问题可以更容易地解决:

    use warnings;
    use strict;
    
    my $lengths_filename = 'Transcriptlength.fa';
    my $counts_filename  = 'Quadcount.AllGtranscripts.fa';
    
    my %sequence;   # it will be the basic data repository
    
    local $/ = ''; 
    # ...by this we ensure that files will be read by logical blocks instead of lines. 
    # Might need some tweaking, if 'empty line' in your sample is not really empty.
    
    # we start processing from 'counts' file, as only those records present in it
    # should actually be in our output:
    open my $cfh, '<', $counts_filename 
      or die $!, "\n";
    while (<$cfh>) {
      # each logical block consists of two parts, divided by whitespace
      my ($name, $count) = split; 
    
      # here goes magic: we simultaneously create a new record in our repository...
      # ... and set its 'count' property to the value, extracted from the scanned fileblock
      $sequence{$name}{count} = $count;
    }
    close $cfh;
    
    # now we go for lengths, approach is almost the same
    open my $lfh, '<', $lengths_filename or die $!, "\n";
    while (<$lfh>) {
      my ($name, $length) = split;
    
      # here we check that the sequence was in 'counts' file
      if (exists $sequence{$name}) { 
        $sequence{$name}{length} = $length;
      } 
    }
    close $lfh;
    
    # and now the output block: it's mostly the same as in the original answer:
    for my $name (sort keys %sequence) {
      print "$name, $sequence{$name}{count}, $sequence{$name}{length}", "\n";
    }
    

    这里有另一个codepad 来说明它是如何工作的。不要介意奇怪的__DATA__ 东西,它只是针对这个特定版本的程序(使用__DATA__ 部分允许我模拟从文件读取,因为我不能在键盘上使用外部源)。

    【讨论】:

    • 你帮他做作业是不是意味着你得到了分数?
    • 嗯,我经常用详细的解释和一堆代码行来帮助'遗传学'的人来说明它。我不认为这是一项家庭作业,我认为这是一项任务,需要由一个不知道如何去做的人来解决——但愿意学习。这就是为什么我不仅给他一些基本原则或文档链接,而是给他真正的思考。
    • 你写的很好,不值得我嘲讽。有些帖子很难弄清楚要放弃多少,而且我更像是一个顽固的老程序员,而不是耐心的老师类型;-)
    • @raina77ow:IMO 为每条记录构建哈希而不是数组会使事情变得不必要地复杂化。通过假设每个序列有多个长度,而问题中没有任何内容可以这么说,您还使事情变得更加困难。我的猜测是一个序列只有一个长度。
    • @raina77ow: map 不是为集合的每个元素做某事 - foreach 做到了。 mapmappinglist 的函数,可从另一个列表创建一个列表。
    猜你喜欢
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多