随机选择一个区域并进行多次处理

时间:2019-02-12 22:34:34

标签: perl awk sed

我有这样的数据

>sp|Q96A73|P33MX_HUMAN Putative monooxygenase p33MONOX OS=Homo sapiens OX=9606 GN=KIAA1191 PE=1 SV=1
RNDDDDTSVCLGTRQCSWFAGCTNRTWNSSAVPLIGLPNTQDYKWVDRNSGLTWSGNDTCLYSCQNQTKGLLYQLFRNLFCSYGLTEAHGKWRCADASITNDKGHDGHRTPTWWLTGSNLTLSVNNSGLFFLCGNGVYKGFPPKWSGRCGLGYLVPSLTRYLTLNASQITNLRSFIHKVTPHR
>sp|P13674|P4HA1_HUMAN Prolyl 4-hydroxylase subunit alpha-1 OS=Homo sapiens OX=9606 GN=P4HA1 PE=1 SV=2
VECCPNCRGTGMQIRIHQIGPGMVQQIQSVCMECQGHGERISPKDRCKSCNGRKIVREKKILEVHIDKGMKDGQKITFHGEGDQEPGLEPGDIIIVLDQKDHAVFTRRGEDLFMCMDIQLVEALCGFQKPISTLDNRTIVITSHPGQIVKHGDIKCVLNEGMPIYRRPYEKGRLIIEFKVNFPENGFLSPDKLSLLEKLLPERKEVEE
>sp|Q7Z4N8|P4HA3_HUMAN Prolyl 4-hydroxylase subunit alpha-3 OS=Homo sapiens OX=9606 GN=P4HA3 PE=1 SV=1
MTEQMTLRGTLKGHNGWVTQIATTPQFPDMILSASRDKTIIMWKLTRDETNYGIPQRALRGHSHFVSDVVISSDGQFALSGSWDGTLRLWDLTTGTTTRRFVGHTKDVLSVAFSSDNRQIVSGSRDKTIKLWNTLGVCKYTVQDESHSEWVSCVRFSPNSSNPIIVSCGWDKLVKVWNLANCKLK
>sp|P04637|P53_HUMAN Cellular tumor antigen p53 OS=Homo sapiens OX=9606 GN=TP53 PE=1 SV=4
IQVVSRCRLRHTEVLPAEEENDSLGADGTHGAGAMESAAGVLIKLFCVHTKALQDVQIRFQPQL
>sp|P10144|GRAB_HUMAN Granzyme B OS=Homo sapiens OX=9606 GN=GZMB PE=1 SV=2
MQPILLLLAFLLLPRADAGEIIGGHEAKPHSRPYMAYLMIWDQKSLKRCGGFLIRDDFVLTAAHCWGSSINVTLGAHNIKEQEPTQQFIPVKRPIPHPAYNPKNFSNDIMLLQLERKAKRTRAVQPLRLPSNKAQVKPGQTCSVAGWGQTAPLGKHSHTLQEVKMTVQEDRKCES
>sp|Q9UHX1|PUF60_HUMAN Poly(U)-binding-splicing factor PUF60 OS=Homo sapiens OX=9606 GN=PUF60 PE=1 SV=1
MGKDYYQTLGLARGASDEEIKRAYRRQALRYHPDKNKEPGAEEKFKEIAEAYDVLSDPRKREIFDRYGEEGLKGSGPSGGSGGGANGTSFSYTFHGDPHAMFAEFFGGRNPFDTFFGQRNGEEGMDIDDPFSGFPMGMGGFTNVNFGRSRSAQEPARKKQDPPVTHDLRVSLEEIYSGCTKKMKISHK
>sp|Q06416|P5F1B_HUMAN Putative POU domain, class 5, transcription factor 1B OS=Homo sapiens OX=9606 GN=POU5F1B PE=5 SV=2
IVVKGHSTCLSEGALSPDGTVLATASHDGYVKFWQIYIEGQDEPRCLHEWKPHDGRPLSCLLFCDNHKKQDPDVPFWRFLITGADQNRELKMWCTVSWTCLQTIRFSPDIFSSVSVPPSLKVCLDLSAEYLILSDVQRKVLYVMELLQNQEEGHACFSSISEFLLTHPVLSFGIQVVSRCRLRHTEVLPAEEENDSLGADGTHGAGAMESAAGVLIKLFCVHTKALQDVQIRFQPQLNPDVVAPLPTHTAHEDFTFGESRPELGSEGLGSAAHGSQPDLRRIVELPAPADFLSLSSETKPKLMTPDAFMTPSASLQQITASPSSSSSGSSSSSSSSSSSLTAVSAMSSTSAVDPSLTRPPEELTLSPKLQLDGSLTMSSSGSLQASPRGLLPGLLPAPADKLTPKGPGQVPTATSALSLELQEVEP
>sp|O14683|P5I11_HUMAN Tumor protein p53-inducible protein 11 OS=Homo sapiens OX=9606 GN=TP53I11 PE=1 SV=2
MIHNYMEHLERTKLHQLSGSDQLESTAHSRIRKERPISLGIFPLPAGDGLLTPDAQKGGETPGSEQWKFQELSQPRSHTSLKVSNSPEPQKAVEQEDELSDVSQGGSKATTPASTANSDVATIPTDTPLKEENEGFVKVTDAPNKSEISKHIEVQVAQETRNVSTGSAENEEKSEVQAIIESTPELDMDKDLSGYKGSSTPTKGIENKAFDRNTESLFEELSSAGSGLIGDVDEGADLLGMGREVENLILENTQLLETKNALNIVKNDLIAKVDELTCEKDVLQGELEAVKQAKLKLEEKNRELEEELRKARAEAEDARQKAKDDDDSDIPTAQRKRFTRVEMARVLMERNQYKERLMELQEAVRWTEMIRASRENPAMQEKKRSSIWQFFSRLFSSSSNTTKKPEPPVNLKYNAPTSHVTPSVK

我想从中随机挑选一个包含10个字母的区域,然后计算F的数量,我想这样做一定次数,例如1000次甚至更多次

例如,我随机选择

LVPSLTRYLT    0

然后

ITNLRSFIHK    1

然后再次随机去接连续10个字母

AHSRIRKERP    0

此过程一直持续到达到要求的运行次数为止。我想将所有随机选择的值与它们的值一起存储,因为那样我想计算出看到F的次数

所以我要执行以下操作

# first I remove the header 
grep -v ">" data.txt > out.txt

然后随机获得一个我尝试使用shuf并没有成功的10个字母的区域,

shuf -n1000 data.txt 

然后我尝试使用awk,但也不成功

awk 'BEGIN {srand()} !/^$/ { if (rand() == 10) print $0}'

然后计算F的数量并将其保存在文件中

grep -i -e [F] |wc -l 
  

请注意,我们不应该在相同的地区接两次

4 个答案:

答案 0 :(得分:4)

我必须在这里承担一些事情,并保留一些限制

  • 要选择的随机区域不依赖于行;他们只是从文本中挑选出来的

  • 顺序无关紧要;文件中应该只有N个区域分布

  • 文件的大小可能达到千兆字节,因此无法先完整读取(会容易得多!)

  • 有未处理(边缘或不太可能)的情况,在代码后进行了讨论

首先建立一个随机数排序列表;这些是文件中区域开始的位置。然后,在读取每一行时,计算其在文件中的字符范围,并检查我们的数字是否在其中。如果有的话,它们会标记每个随机区域的开头:从这些字符开始,选择所需长度的子字符串。检查子字符串是否适合该行。

use warnings;
use strict;
use feature 'say';

use Getopt::Long;
use List::MoreUtils qw(uniq);

my ($region_len, $num_regions) = (10, 10);
my $count_freq_for = 'F';
#srand(10);

GetOptions(
    'num-regions|n=i' => \$num_regions, 
    'region-len|l=i'  => \$region_len, 
    'char|c=s'        => \$count_freq_for,
) or usage();

my $file = shift || usage();

# List of (up to) $num_regions random numbers, spanning the file size
# However, we skip all '>sp' lines so take more numbers (estimate)
open my $fh, '<', $file  or die "Can't open $file: $!";
$num_regions += int $num_regions * fraction_skipped($fh);
my @rand = uniq sort { $a <=> $b } 
    map { int(rand (-s $file)-$region_len) } 1..$num_regions;
say "Starting positions for regions: @rand";

my ($nchars_prev, $nchars, $chars_left) = (0, 0, 0); 

my $region;

while (my $line = <$fh>) { 
    chomp $line;
    # Total number of characters so far, up to this line and with this line
    $nchars_prev = $nchars;
    $nchars += length $line;
    next if $line =~ /^\s*>sp/;

    # Complete the region if there wasn't enough chars on the previous line 
    if ($chars_left > 0) {
        $region .= substr $line, 0, $chars_left;
        my $cnt = () = $region =~ /$count_freq_for/g;
        say "$region $cnt";
        $chars_left = -1; 
    };  

    # Random positions that happen to be on this line    
    my @pos = grep { $_ > $nchars_prev and $_ < $nchars } @rand;
    # say "\tPositions on ($nchars_prev -- $nchars) line: @pos" if @pos;

    for (@pos) { 
        my $pos_in_line = $_ - $nchars_prev;
        $region = substr $line, $pos_in_line, $region_len; 

        # Don't print if there aren't enough chars left on this line
        last if ( $chars_left = 
            ($region_len - (length($line) - $pos_in_line)) ) > 0;

        my $cnt = () = $region =~ /$count_freq_for/g;
        say "$region $cnt";
    }   
}


sub fraction_skipped {
    my ($fh) = @_;
    my ($skip_len, $data_len);
    my $curr_pos = tell $fh;
    seek $fh, 0, 0  if $curr_pos != 0;
    while (<$fh>) {
        chomp;
        if (/^\s*>sp/) { $skip_len += length }
        else           { $data_len += length }
    }
    seek $fh, $curr_pos, 0;  # leave it as we found it
    return $skip_len / ($skip_len+$data_len);
}

sub usage {
    say STDERR "Usage: $0 [options] file", "\n\toptions: ...";
    exit;
}

取消注释srand行,以便始终进行相同的运行以进行测试。 请注意。

一些极端情况

  • 如果10个长窗口从其随机位置开始不适合该行,则在下一行中完成-但是此行上的(任意)更多个随机位置被排除在外。因此,如果我们的随机列表有1120和1122,而一条线在1125结束,则跳过从1122开始的窗口。不太可能,没有任何后果(除了减少一个区域)。

  • 当下一行(if循环中的第一个while)填充不完整区域时,可能剩余的所需字符($chars_left)。这是极不可能的,需要在那里进行额外的检查。

  • 随机数被删除。这使顺序变得歪斜,但是在此刻不重要的是什么。而且我们可能留下的数量少于要求的数量,但是数量很少

处理有关随机性的问题

这里的“随机性”是非常基本的,似乎很合适。我们还需要考虑以下内容。

在跨越文件大小int(rand -s $file)(减去区域大小)的间隔内绘制随机数。但是,>sp行被跳过,并且可能落入这些行中的任何数字都不会被使用,因此,最终我们得到的区域可能少于绘制的数字。这些行较短,因此在其上出现数字的机会较小,因此丢失了很多数字,但是在某些运行中,我看到甚至跳过了十分之三的数字,最终得到了所需样本大小的70%。 / p>

如果这很麻烦,则有多种方法可以解决。为了不进一步分散发行版,它们都应涉及预处理文件。

上面的代码对文件进行了初始运行,以计算将被跳过的字符比例。然后将其用于增加绘制的随机点的数量。这当然是“平均”的措施,但仍应产生接近足够大文件所需区域的数量。

更详细的度量将需要查看(更大)分布的哪些随机点将丢失到跳过的行中,然后重新采样以解决这一问题。这可能仍然会影响发行版,在这里可以说不是问题,但更重要的是根本不需要。

在所有这些中,您两次读取了大文件。额外的处理时间应仅以秒为单位,但是如果不可接受,则将功能fraction_skipped更改为仅读取文件的10-20%。对于大文件,这仍应提供合理的估计。

有关特定测试用例的说明

使用srand(10)(靠近开头的注释行),我们得到了随机数,这样,在一行上,该区域在行尾之前开始了8个字符!因此,这种情况确实会测试代码以完成下一行的区域。


一个简单的驱动程序可以运行上述给定次数,以进行统计。

仅使用内置工具(systemqx)来进行操作既麻烦又挑剔;实际上,一个需要模块。所以我在这里使用IPC::Run。还有很多其他选项。

根据统计信息调整并添加代码以进行处理;输出在文件中。

use warnings;
use strict;
use feature 'say';

use Getopt::Long;
use IPC::Run qw(run);

my $outdir = 'rr_output';         # pick a directory name
mkdir $outdir if not -d $outdir;    
my $prog  = 'random_regions.pl';  # your name for the program
my $input = 'data_file.txt';      # your name for input file     
my $ch = 'F';

my ($runs, $regions, $len) = (10, 10, 10);    
GetOptions(
    'runs|n=i'  => \$runs, 
    'regions=i' => \$regions, 
    'length=i'  => \$len, 
    'char=s'    => \$ch, 
    'input=s'   => \$input
) or usage();

my @cmd = ( $prog, $input, 
    '--num-regions', $regions, 
    '--region-len', $len, 
    '--char', $ch
);    
say "Run: @cmd, $runs times.";

for my $n (1..$runs) {
    my $outfile = "$outdir/regions_r$n.txt";
    say "Run #$n, output in: $outdir/$outfile";
    run \@cmd, '>', $outfile  or die "Error with @cmd: $!";
}    

sub usage {
    say STDERR "Usage: $0 [options]", "\n\toptions: ...";
    exit;
}

请展开错误检查。例如,请参见this post和详细信息的链接。

最简单的用法:driver_random.pl -n 4,但是您可以提供所有主程序的参数。

注意:所调用的程序(上面的random_regions.pl必须是可执行的。


从简单到功能更强大的一些软件:IPC::System::SimpleCapture::TinyIPC::Run3。 (然后在这里使用IPC::Run。)另请参见String::ShellQuote,以准备命令而不引用问题,shell注入错误和其他问题。例如,请参阅以this post汇编的链接(示例)。

答案 1 :(得分:1)

awk来营救!

您没有指定,但是有两个随机动作在进行。我独立对待他们,可能并非如此。首先选择一行,然后从该行中随机选择一个10个字母的子字符串。

这假设文件(或文件的一半)可以容纳在内存中。否则,将文件分成相等的块,然后逐个运行。这样做会减少一些群集,但不确定这种情况下的重要性。 (如果您有一个大文件,则可能所有样本都可以从前半部分中抽取出来,通过拆分可以消除这种可能性)。在某些情况下,这是理想的属性。不知道你的情况。

$ awk 'BEGIN {srand()} 
       !/^>/ {a[++n]=$0} 
       END   {while(i++<1000) 
                {line=a[int(rand()*n)+1]; 
                 s=int(rand()*(length(line)-9));
                 print ss=substr(line,s,10), gsub(/F/,"",ss)}}' file

GERISPKDRC 0
QDEPRCLHEW 0
LLYQLFRNLF 2
GTHGAGAMES 0
TKALQDVQIR 0
FCVHTKALQD 1
SNKAQVKPGQ 0
CMECQGHGER 0
TRRFVGHTKD 1
...

答案 2 :(得分:0)

这是使用Perl的一种解决方案

它将整个文件插入内存。然后,以>开头的行将被删除。 在这里,我循环10次$i<10,您可以在此处增加计数。 然后,通过传递文件的长度并使用rand值来调用rand函数,计算substr为10。 $s!~/\n/的警惕是确保我们不要选择跨换行符的子字符串。

$ perl -0777 -ne '$_=~s/^>.+?\n//smg; while($i<10) { $x=rand(length($_)); $s=substr($_,$x,10); $f=()=$s=~/F/g; if($s!~/\n/) { print "$s $f\n" ;$i++} else { $i-- } } '
random10.txt
ENTQLLETKN 0
LSEGALSPDG 0
LRKARAEAED 0
RLWDLTTGTT 0
KWSGRCGLGY 0
TRRFVGHTKD 1
PVKRPIPHPA 0
GMVQQIQSVC 0
LTHPVLSFGI 1
KVNFPENGFL 2

$

要知道生成的随机数

$ perl -0777 -ne '$_=~s/^>.+?\n//smg; while($i<10) { $x=rand(length($_)); $s=substr($_,$x,10); $f=()=$s=~/F/g; if($s!~/\n/) { print "$s $f $x\n" ;$i++} else { $i-- } }
 ' random10.txt
QLDGSLTMSS 0 1378.61409368207
DLIAKVDELT 0 1703.46689004765
SGGGANGTSF 1 900.269562152326
PEELTLSPKL 0 1368.55540468164
TCLSEGALSP 0 1016.50744004085
NRTWNSSAVP 0 23.7868578293154
VNFPENGFLS 2 363.527933104776
NSGLTWSGND 0 48.656607650744
MILSASRDKT 0 422.67705815168
RRGEDLFMCM 1 290.828530365
AGDGLLTPDA 0 1481.78080339531

$

答案 3 :(得分:0)

由于您的输入文件很大,因此请按以下步骤操作:

  1. 从输入文件的每一行中随机选择10个字符的字符串
  2. 将它们随机排列以随机获取所需样本数量
  3. 计算Fs

例如

$ cat tst.sh
#!/bin/env bash
infile="$1"

sampleSize=10
numSamples=15

awk -v sampleSize="$sampleSize" '
    BEGIN { srand() }
    !/^>/ {
        begPos = int((rand() * sampleSize) + 1)
        endPos = length($0) - sampleSize
        for (i=begPos; i<=endPos; i+=sampleSize) {
            print substr($0,i,sampleSize)
        }
    }
' "$infile" |
shuf -n "$numSamples"

$ ./tst.sh file
HGDIKCVLNE
QDEPRCLHEW
SEVQAIIEST
THDLRVSLEE
SEWVSCVRFS
LTRYLTLNAS
KDGQKITFHG
SNSPEPQKAV
QGGSKATTPA
QLLETKNALN
LLFCDNHKKQ
DETNYGIPQR
IRFQPQLNPD
LQTIRFSPDI
SLKRCGGFLI

$ ./tst.sh file | awk '{print $0, gsub(/F/,"")}'
SPKLQLDGSL 0
IKLFCVHTKA 1
VVSRCRLRHT 0
SPEPQKAVEQ 0
AYNPKNFSND 1
FGESRPELGS 1
AGDGLLTPDA 0
VGHTKDVLSV 0
VTHDLRVSLE 0
PISLGIFPLP 1
ASQITNLRSF 1
LTRPPEELTL 0
FDRYGEEGLK 1
IYIEGQDEPR 0
WNTLGVCKYT 0

在对实际数据运行时,只需将numSamples从15更改为1000即可。

以上内容取决于shuf -n能够处理我们向其投入的任何输入,大概就像sort通过使用分页所做的那样。如果在这方面失败了,那么显然您必须为该部分选择/实施其他工具。 FWIW我尝试过seq 100000000 | shuf -n 10000(即,输入的行数是OP发布的最大文件长度10000000的10倍,以说明awk部分每1行输入产生N行输出,而输出的行数是OPs的10倍操作人员发布了1000条),并且运行正常,仅花费了几秒钟的时间。