Bio-ViennaNGS
view release on metacpan or search on metacpan
lib/Bio/ViennaNGS/Bam.pm view on Meta::CPAN
# -*-CPerl-*-
# Last changed Time-stamp: <2017-06-10 18:20:31 michl>
package Bio::ViennaNGS::Bam;
use strict;
use warnings;
use Exporter;
use Bio::ViennaNGS;
use Bio::DB::Sam 1.37;
use Data::Dumper;
use File::Basename qw(fileparse);
use File::Temp qw(tempfile);
use Path::Class;
use Carp;
use version; our $VERSION = version->declare("$Bio::ViennaNGS::VERSION");
our @ISA = qw(Exporter);
our @EXPORT = ();
our @EXPORT_OK = qw ( split_bam uniquify_bam uniquify_bam2 );
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
#^^^^^^^^^^^ Subroutines ^^^^^^^^^^#
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
sub split_bam {
my %data = ();
my @NHval = ();
my @processed_files = ();
my ($verbose,$nh_warning_issued) = (0)x2;
my ($bamfile,$reverse,$want_uniq,$want_bed,$dest_dir,$log) = @_;
my ($bam,$sam,$bn,$path,$ext,$header,$flag,$NH,$eff_strand,$tmp);
my ($bam_pos,$bam_neg,$tmp_bam_pos,$tmp_bam_neg,$bamname_pos,$bamname_neg);
my ($bed_pos,$bed_neg,$bedname_pos,$bedname_neg);
my ($seq_id,$start,$stop,$strand,$target_names,$id,$score);
my $this_function = (caller(0))[3];
my %count_entries = (
total => 0,
uniq => 0,
pos => 0,
neg => 0,
skip => 0,
cur => 0,
mult => 0,
se_alis => 0,
pe_alis => 0,
flag => 0,
unmapped => 0,
);
$data{count} = \%count_entries;
$data{flag} = ();
$data{nh_issues} = 0; # will be set to 1 if NH attribute is missing
$nh_warning_issued = 0;
croak "ERROR [$this_function] $bamfile does not exist\n"
unless (-e $bamfile);
croak "ERROR [$this_function] $dest_dir does not exist\n"
unless (-d $dest_dir);
(undef,$tmp_bam_pos) = tempfile('BAM_POS_XXXXXXX',UNLINK=>0);
(undef,$tmp_bam_neg) = tempfile('BAM_NEG_XXXXXXX',UNLINK=>0);
$bam = Bio::DB::Bam->open($bamfile, "r");
$header = $bam->header;
$target_names = $header->target_name;
($bn,$path,$ext) = fileparse($bamfile, qr /\..*/);
unless ($dest_dir =~ /\/$/){$dest_dir .= "/";}
$bamname_pos = $dest_dir.$bn.".pos".$ext;
$bamname_neg = $dest_dir.$bn.".neg".$ext;
$bam_pos = Bio::DB::Bam->open($tmp_bam_pos,'w')
or croak "ERROR [$this_function] Could not open bam_pos file for writing: $!";
$bam_neg = Bio::DB::Bam->open($tmp_bam_neg,'w')
or croak "ERROR [$this_function] Could not open bam_neg file for writing: $!";
if ($want_bed == 1){
$bedname_pos = $dest_dir.$bn.".pos.bed";
$bedname_neg = $dest_dir.$bn.".neg.bed";
open($bed_pos, ">", $bedname_pos); open($bed_neg, ">", $bedname_neg);
}
$bam_pos->header_write($header);$bam_neg->header_write($header);
if($reverse == 1) { # switch +/- strand mapping
$tmp = $bam_pos;$bam_pos = $bam_neg;$bam_neg = $tmp;
$tmp = $bed_pos;$bed_pos = $bed_neg;$bed_neg = $tmp;
}
while (my $read= $bam->read1() ) {
@NHval = ();
$data{count}{total}++;
# collect statistics of SAM flags
$flag = $read->flag;
unless (exists $data{flag}{$flag}){
$data{flag}{$flag} = 0;
}
lib/Bio/ViennaNGS/Bam.pm view on Meta::CPAN
printf STDERR "%20d unmapped reads\n",$data{count}{unmapped};
printf STDERR "%20d total alignment/read count\n",$data{count}{cur};
printf STDERR "ERROR: pos alignments + neg alignments != total alignments\n";
print Dumper(\%data);
croak $!;
}
foreach (keys %{$data{flag}}){
$data{count}{flag} += $data{flag}{$_};
}
unless ($data{count}{flag} == $data{count}{cur} + $data{count}{unmapped}){
printf STDERR "%20d alignments considered\n",$data{count}{cur};
printf STDERR "%20d unmapped reads\n",$data{count}{unmapped};
printf STDERR "%20d alignments found in flag statistics\n",$data{count}{flag};
printf STDERR "ERROR: alignments considered + unmapped reads != alignments from flag stat\n";
print Dumper(\%data);
croak $!;
}
# logging output
open(LOG, ">", $log) or croak $!;
printf LOG "# bam_split.pl log for file \'$bamfile\'\n";
printf LOG "#-----------------------------------------------------------------\n";
printf LOG "%20d total alignments (unique/multi/unmapped)\n",$data{count}{total};
printf LOG "%20d unique-mappers (%7.2f%% of total) ",
$data{count}{uniq},eval(100*$data{count}{uniq}/$data{count}{total});
if ($data{nh_issues}){printf LOG " *** NH attribute issues ***";}
printf LOG "\n";
printf LOG "%20d multi-mappers (%7.2f%% of total) ",
$data{count}{mult},eval(100*$data{count}{mult}/$data{count}{total});
if ($data{nh_issues}){printf LOG " *** NH attribute issues ***";}
printf LOG "\n";
printf LOG "%20d skipped\n", $data{count}{skip};
printf LOG "%20d alignments considered\n", $data{count}{cur};
printf LOG "%20d paired-end\n", $data{count}{pe_alis};
printf LOG "%20s single-end\n", $data{count}{se_alis};
if($data{count}{cur}>0){
printf LOG "%20d fragments on [+] strand (%7.2f%% of considered)\n",
$data{count}{pos},eval(100*$data{count}{pos}/$data{count}{cur});
printf LOG "%20d fragments on [-] strand (%7.2f%% of considered)\n",
$data{count}{neg},eval(100*$data{count}{neg}/$data{count}{cur});
}
else{
printf LOG "%20d fragments on [+] strand\n",$data{count}{pos};
printf LOG "%20d fragments on [-] strand\n",$data{count}{neg};
}
printf LOG "%20d unmapped\n", $data{count}{unmapped};
printf LOG "#-----------------------------------------------------------------\n";
printf LOG "Dumper output:\n". Dumper(\%data);
close(LOG);
return @processed_files;
}
sub uniquify_bam {
my %data = ();
my ($bamfile,$dest,$log) = @_;
my ($bam, $bn,$path,$ext,$read,$header);
my ($tmp_uniq,$tmp_mult,$fn_uniq,$fn_mult,$bam_uniq,$bam_mult,$NH);
my ($count_all,$count_uniq,$count_mult,$unmapped,$nh_warning_issued) = (0)x5;
my @processed_files = ();
my @NHval = ();
my $this_function = (caller(0))[3];
my %count_entries = (
total => 0,
uniq => 0,
mult => 0,
unmapped => 0,
nonhtag => 0,
);
$data{count} = \%count_entries;
$data{nh_issues} = 0; # will be set to 1 if NH attribute is missing
croak "ERROR [$this_function] Cannot find $bamfile\n"
unless (-e $bamfile);
croak "ERROR [$this_function] $dest does not exist\n"
unless (-d $dest);
($bn,$path,$ext) = fileparse($bamfile, qr /\..*/);
(undef,$tmp_uniq) = tempfile('BAM_UNIQ_XXXXXXX',UNLINK=>0);
(undef,$tmp_mult) = tempfile('BAM_MULT_XXXXXXX',UNLINK=>0);
$bam = Bio::DB::Bam->open($bamfile, "r");
$fn_uniq = file($dest,$bn.".uniq".$ext);
$fn_mult = file($dest,$bn.".mult".$ext);
$header = $bam->header; # TODO: modify header, leave traces ...
$bam_uniq = Bio::DB::Bam->open($tmp_uniq,'w')
or croak "ERROR [$this_function] Cannot open temp file for writing: $!";
$bam_mult = Bio::DB::Bam->open($tmp_mult,'w')
or croak "ERROR [$this_function] Cannot open temp file for writing: $!";
$bam_uniq->header_write($header);
$bam_mult->header_write($header);
while ($read = $bam->read1() ) {
$data{count}{total}++;
# skip unmapped reads
if ( $read->get_tag_values('UNMAPPED') ){
$data{count}{unmapped}++;
next;
}
# check if NH (the SAM tag used to indicate multiple mappings) is set
if ($read->has_tag("NH")) {
@NHval = $read->get_tag_values("NH");
$NH = $NHval[0];
if ($NH == 1) {
$bam_uniq->write1($read);
$data{count}{uniq}++;
}
else {
$bam_mult->write1($read);
$data{count}{mult}++;
}
}
else{ # no NH tag found
$data{nh_issues} = 1; # set this once and for all
unless ($nh_warning_issued == 1){
$data{count}{nonhtag}++;
carp "ERROR [$this_function] Read ".$read->query->name.
" does not have NH attribute\nCannot continue ...";
$nh_warning_issued = 1;
}
}
}
unless ($data{count}{uniq} + $data{count}{mult} ==
$data{count}{total} - $data{count}{unmapped} - $data{count}{nonhtag}){
printf STDERR "%20d unique alignments\n",$data{count}{uniq};
printf STDERR "%20d multiple alignments\n",$data{count}{mult};
printf STDERR "%20d total alignments\n",$data{count}{total};
printf STDERR "%20d unmapped reads\n",$data{count}{unmapped};
croak "ERROR [$this_function] Read counts don't match\n";
}
rename ($tmp_uniq, $fn_uniq);
rename ($tmp_mult, $fn_mult);
push (@processed_files, ($fn_uniq,$fn_mult));
if (defined $log){
my $lf = file($dest,$log);
open(LOG, ">", $lf) or croak $!;
printf LOG "%15d reads total\n%15d unique alignments\n%15d multiple alignments\n%15d unmapped\n",
$data{count}{total},$data{count}{uniq},$data{count}{mult},$data{count}{unmapped};
close(LOG);
}
}
sub uniquify_bam2 {
my %data = ();
my ($bamfile,$dest,$log) = @_;
my ($bam, $bn,$path,$ext,$read,$header,$ali);
my ($tmp_uniq,$tmp_mult,$fn_uniq,$fn_mult,$bam_uniq,$bam_mult,$NH);
my ($count_all,$count_uniq,$count_mult,$unmapped,$nh_warning_issued) = (0)x5;
my $pushme = 0;
my $allgomulti = 0;
my @processed_files = ();
my @NHval = ();
my $lastqname = undef;
my @band = ();
my $this_function = (caller(0))[3];
my %count_entries = (
total => 0,
uniq => 0,
mult => 0,
unmapped => 0,
nonhtag => 0,
);
$data{count} = \%count_entries;
$data{nh_issues} = 0; # will be set to 1 if NH attribute is missing
croak "ERROR [$this_function] Cannot find $bamfile\n"
unless (-e $bamfile);
croak "ERROR [$this_function] $dest does not exist\n"
unless (-d $dest);
($bn,$path,$ext) = fileparse($bamfile, qr /\..*/);
(undef,$tmp_uniq) = tempfile('BAM_UNIQ_XXXXXXX',UNLINK=>0);
(undef,$tmp_mult) = tempfile('BAM_MULT_XXXXXXX',UNLINK=>0);
$bam = Bio::DB::Bam->open($bamfile, "r");
$fn_uniq = file($dest,$bn.".uniq.band.".$ext);
$fn_mult = file($dest,$bn.".mult.band.".$ext);
$header = $bam->header; # TODO: modify header, leave traces ...
$bam_uniq = Bio::DB::Bam->open($tmp_uniq,'w')
or croak "ERROR [$this_function] Cannot open temp file for writing: $!";
$bam_mult = Bio::DB::Bam->open($tmp_mult,'w')
or croak "ERROR [$this_function] Cannot open temp file for writing: $!";
$bam_uniq->header_write($header);
$bam_mult->header_write($header);
while ($read = $bam->read1() ) {
$data{count}{total}++;
# skip unmapped reads
if ( $read->get_tag_values('UNMAPPED') ){
$data{count}{unmapped}++;
next;
}
my $qname = $read->qname;
$qname =~ s/\/[12]$//;
if (!defined($lastqname) || $qname eq $lastqname ){
#print "$qname\n";
push @band, $read;
}
else {
# band is now completely read, processing it ...
foreach $ali (@band){
# check if NH (the SAM attribute used to indicate multiple mappings) is set
if ($ali->has_tag("NH")) {
@NHval = $ali->get_tag_values("NH");
$NH = $NHval[0];
if ($NH > 1) {
$allgomulti = 1;
$data{count}{mult}++;
}
else {
( run in 1.980 second using v1.01-cache-2.11-cpan-6de40a662fe )