“Perl:将数组元素从一个数组添加到另一个数组中”

时间:2012-06-20 15:31:38

标签: arrays perl bioinformatics

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

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

基本上,我有两个看起来像这样的数组:

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

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

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

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

例如,因为 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中的length元素添加到@ array2中?我需要使用splice命令,但是怎么做?

我想要的输出应该如下所示:

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

我还需要将此输出保存到一个文件中,稍后将对其进行分析,以查看长度与G-quadruplex数之间是否存在任何关联。

任何帮助或意见都将深表感谢。

感谢您抽出宝贵时间解决我的问题!


编辑:此编辑用于显示数据文件的外观。它们基本上是我写的其他程序的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个数字进入@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

4 个答案:

答案 0 :(得分:1)

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

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)。

答案 1 :(得分:1)

更新:我将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__部分允许我模拟从文件中读取,因为我不能在键盘上使用外部源)。

答案 2 :(得分: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,因此在针对该数据运行时,该程序没有输出。您的实际文件将更有效率。

答案 3 :(得分:0)

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

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

print join(":", @data2); 

查看数组中的实际内容。

在继续下一行之前让每一行都正常工作。然后,如果你无法弄清楚为什么一条线不起作用,你可以在这里发一个问题。

实际上,很难用它来表达你在代码中想说的内容,因为这些想法是不完整的。