App-Sandy
view release on metacpan or search on metacpan
lib/App/Sandy/Simulator.pm view on Meta::CPAN
$std_id => $id
);
# It is necessary to catch gene -> transcript relation
# # TODO: Make a hash tarit for indexed fasta
if (defined $fields[1]) {
my $pid = $fields[1];
# Chomp and remove id version
$self->_clean_fasta_id(\$pid);
$fasta_rtree{$id} = $pid;
}
} else {
die "Error reading fasta file '$fasta': Not defined id"
unless defined $id;
$indexed_fasta{$id}{seq} .= uc($_);
}
}
for (keys %indexed_fasta) {
$indexed_fasta{$_}{size} = length $indexed_fasta{$_}{seq};
}
unless (%indexed_fasta) {
die "Error parsing '$fasta'. Maybe the file is empty\n";
}
$fh->close
or die "Cannot close file $fasta: $!\n";
$self->_set_fasta_rtree(%fasta_rtree) if %fasta_rtree;
return \%indexed_fasta;
}
sub _build_fasta {
my $self = shift;
my $fasta = $self->fasta_file;
log_msg ":: Indexing fasta file '$fasta' ...";
my $indexed_fasta = $self->_index_fasta;
# Validate genome about the read size required
log_msg ":: Validating fasta file '$fasta' ...";
# Entries to remove
my @blacklist;
unless ($self->truncate) {
for my $id (keys %$indexed_fasta) {
my $index_size = $indexed_fasta->{$id}{size};
my $class = ref $self->seq;
if ($class eq 'App::Sandy::Seq::SingleEnd') {
my $read_mean = $self->seq->read_mean;
if ($index_size < $read_mean) {
log_msg ":: Parsing fasta file '$fasta': Seqid sequence length (>$id => $index_size) lesser than required read mean ($read_mean)";
delete $indexed_fasta->{$id};
push @blacklist => $id;
}
} elsif ($class eq 'App::Sandy::Seq::PairedEnd') {
my $fragment_mean = $self->seq->fragment_mean;
if ($index_size < $fragment_mean) {
log_msg ":: Parsing fasta file '$fasta': Seqid sequence length (>$id => $index_size) lesser than required fragment mean ($fragment_mean)";
delete $indexed_fasta->{$id};
push @blacklist => $id;
}
} else {
croak "Unknown option '$class' for sequencing type\n";
}
}
}
unless (%$indexed_fasta) {
die sprintf "Fasta file '%s' has no valid entry\n" => $self->fasta_file;
}
# If fasta_rtree has entries
unless ($self->_has_no_fasta_rtree) {
# Remove no valid entries from id -> pid relation
$self->_delete_fasta_rtree(@blacklist) if @blacklist;
}
return $indexed_fasta;
}
sub _populate_fasta_tree {
my $self = shift;
# If fasta_rtree has entries
unless ($self->_has_no_fasta_rtree) {
# Build parent -> child ids relation
my %fasta_tree;
# Reverse fasta_rtree to pid -> \@ids
for my $pair ($self->_fasta_rtree_pairs) {
my ($id, $pid) = (@$pair);
push @{ $fasta_tree{$pid} } => $id;
}
$self->_set_fasta_tree(%fasta_tree);
}
}
sub _build_fasta_blacklist {
my $self = shift;
log_msg ":: Index unidentified (NNN..) regions";
my $indexed_fasta = $self->_fasta;
my $class = ref $self->seq;
my %fasta_blacklist;
my @blacklist_id;
for my $id (keys %$indexed_fasta) {
my $seq = $indexed_fasta->{$id}{seq};
my $seq_len = $indexed_fasta->{$id}{size};
my $offset = 0;
my ($st, $en) = (0, 0);
my $pos = 0;
my $init = 0;
my $len = 0;
my @stack;
while (($pos = index $seq, "N", $offset) != -1) {
unless ($init) {
$st = $pos;
$en = $pos;
$init = 1;
}
if ($en < ($pos - 1)) {
push @stack => [$st, $en];
$len += $en - $st + 1;
$st = $pos;
}
$en = $pos;
$offset = $pos + 1;
}
if ($init) {
push @stack => [$st, $en];
$len += $en - $st + 1;
}
my $read_len = $class eq 'App::Sandy::Seq::SingleEnd'
? $self->seq->read_mean
: $self->seq->fragment_mean;
if ($len > ($seq_len - $read_len)) {
log_msg ":: Seqid '$id' has too much NNN and will not be used";
delete $indexed_fasta->{$id};
push @blacklist_id => $id;
} else {
$fasta_blacklist{$id} = \@stack;
}
}
unless (%$indexed_fasta) {
die sprintf "Fasta file '%s' has too much NNN regions\n" => $self->fasta_file;
}
# If fasta_rtree has entries
unless ($self->_has_no_fasta_rtree) {
# Remove no valid entries from id -> pid relation
$self->_delete_fasta_rtree(@blacklist_id) if @blacklist_id;
}
return \%fasta_blacklist;
}
sub _retrieve_expression_matrix {
my $self = shift;
my $expression = App::Sandy::DB::Handle::Expression->new;
my $indexed_file = $expression->retrievedb($self->expression_matrix);
# Remove transcript id version
for my $id (keys %$indexed_file) {
my $old_id = $id;
$self->_clean_fasta_id(\$id);
$indexed_file->{$id} = delete $indexed_file->{$old_id}
if $old_id ne $id;
}
return $indexed_file;
}
sub _build_seqid_raffle {
my $self = shift;
# Get the piece table
my $piece_table = $self->_piece_table;
# The builded function
my $seqid_sub;
if ($self->seqid_weight eq 'same') {
my ($keys, $weights) = $self->_populate_key_weight($piece_table, sub { 1 });
# If weight == 1 means that there are 2 keys for
# the same seq_id.
# If weight == 2 means that there is only one key
# for the seq_id, so I double that key
for (my $i = 0; $i < @$weights; $i++) {
if ($weights->[$i] > 1) {
push @$keys => $keys->[$i];
}
lib/App/Sandy/Simulator.pm view on Meta::CPAN
# Build piece table
my %piece_table;
# Let's construct the piece_table
log_msg ":: Build piece table ...";
while (my ($seq_id, $fasta_h) = each %$indexed_fasta) {
my $seq = \$fasta_h->{seq};
my $std_seq_id = $self->_get_seqname($seq_id);
# Initialize piece tables for $seq_id ref
$piece_table{$seq_id}{ref}{table} = App::Sandy::PieceTable->new(orig => $seq);
# If there is indexed_snv for seq_id, then construct the piece table with it
if (defined $indexed_snv && defined $indexed_snv->{$std_seq_id}) {
my $snvs = $indexed_snv->{$std_seq_id};
# Filter only the homozygotic snvs to feed reference seq_id
my @snvs_homo = grep { $_->{plo} eq 'HO' } @$snvs;
if (@snvs_homo) {
# Populate reference seq_id
$self->_populate_piece_table($piece_table{$seq_id}{ref}{table}, \@snvs_homo);
}
# Initialize piece tables for $seq_id alt
$piece_table{$seq_id}{alt}{table} = App::Sandy::PieceTable->new(orig => $seq);
# Populate alternative seq_id
$self->_populate_piece_table($piece_table{$seq_id}{alt}{table}, $snvs);
}
}
# Initialize the logical offsets and valodate the
# new size due to the genomic variation
my @blacklist;
for my $seq_id (keys %piece_table) {
my $type_h = delete $piece_table{$seq_id};
for my $type (keys %$type_h) {
my $table_h = delete $type_h->{$type};
my $table = $table_h->{table};
# Initialize the logical offset
$table->calculate_logical_offset;
# Get the new size
my $new_size = $table->logical_len;
unless ($self->truncate) {
my $class = ref $self->seq;
if ($class eq 'App::Sandy::Seq::SingleEnd') {
if ($new_size < $self->seq->read_mean) {
log_msg ":: Skip '$seq_id:$type': So many deletions resulted in a sequence lesser than the required read-mean";
next;
}
} elsif ($class eq 'App::Sandy::Seq::PairedEnd') {
if ($new_size < $self->seq->fragment_mean) {
log_msg ":: Skip '$seq_id:$type': So many deletions resulted in a sequence lesser than the required fragment mean";
next;
}
} else {
die "No valid options for 'seq'";
}
}
# If all's right
$table_h->{size} = $new_size;
$type_h->{$type} = $table_h;
}
# if there is at least one type,
# then return it to the piece_table
if (%$type_h) {
$piece_table{$seq_id} = $type_h;
# else, just remove it!
} else {
push @blacklist => $seq_id;
}
}
unless (%piece_table) {
die "All fasta entries were removed due to deletions. ",
"Please, verify the genomic variation '$genomic_variation'\n";
}
# If fasta_rtree has entries
unless ($self->_has_no_fasta_rtree) {
# Remove no valid entries from id -> pid relation
$self->_delete_fasta_rtree(@blacklist) if @blacklist;
}
# Make the id -> pid relationship
$self->_populate_fasta_tree;
# HASH -> SEQ_ID -> @(REF @ALT) -> @(TABLE SIZE)
return \%piece_table;
}
sub _populate_piece_table {
my ($self, $table, $snvs) = @_;
for my $snv (@$snvs) {
# If there is an ID, make sure that it is not a comma, colon
# separated list. Else, make sure to keep the ref/alt length
# to max 25+25+1=51
my $annot = defined $snv->{id} && $snv->{id} ne '.'
? sprintf "%d:%s" => $snv->{pos} + 1, (split(/[,;]/, $snv->{id}))[0]
: sprintf "%d:%.25s/%.25s" => $snv->{pos} + 1, $snv->{ref}, $snv->{alt};
# Insertion
if ($snv->{ref} eq '-') {
$table->insert(\$snv->{alt}, $snv->{pos}, $annot);
# Deletion
} elsif ($snv->{alt} eq '-') {
$table->delete($snv->{pos}, length $snv->{ref}, $annot);
lib/App/Sandy/Simulator.pm view on Meta::CPAN
close $fhs[$fh_idx];
}
# If it is a bam and it is the last loop, then
# write a eof marker
if ($output_format eq 'bam' && $tid == $number_of_jobs) {
$self->gen_eof_marker($files_t[0]);
}
# Child exit
log_msg " => Job $tid is finished";
$pm->finish(0, \%counter);
}
# Back to parent
# Interlace parent/child(s) processes
my $sig = App::Sandy::InterlaceProcesses->new(foreign_pid => \@child_pid);
$pm->wait_all_children;
if ($sig->signal_catched) {
log_msg ":: Termination signal received!";
}
log_msg ":: Saving the work ...";
# Concatenate all temporary files
log_msg ":: Concatenate all temporary files";
# Save time. Rename tmp_file (1,2)
for my $file (@{ $files{$file_class} }) {
my $tmp = shift @tmp_files;
log_msg " => Concatenating $tmp to $file ...";
rename $tmp => $file
or die "Cannot create '$file': $!\n";
}
# Append to renamed tmp files
my @fh = map { $self->with_open_a($_) } @{ $files{$file_class} };
for my $i (0..$#tmp_files) {
my $fh_idx = $i % scalar @fh;
log_msg " => Concatenating $tmp_files[$i] to $files{$file_class}[$fh_idx] ...";
cat $tmp_files[$i] => $fh[$fh_idx]
or die "Cannot concatenate $tmp_files[$i] to $files{$file_class}[$fh_idx]: $!\n";
# Clean up the mess
unlink $tmp_files[$i]
or die "Cannot remove temporary file '$tmp_files[$i]': $!\n";
}
# Close files
log_msg ":: Writing and closing output file: @{ $files{$file_class} }";
for my $fh_idx (0..$#fh) {
close $fh[$fh_idx]
or die "Cannot write file $files{$file_class}[$fh_idx]: $!\n";
}
if ($self->count_loops_by eq 'number-of-reads') {
# It is necessary to correct the abundance according to
# fragment sequencing end
my $count_factor = ref($self->seq) eq 'App::Sandy::Seq::PairedEnd'
? 2
: 1;
# Save transcripts
log_msg ":: Saving transcripts count";
my $fh = $self->with_open_w($count_file{transcripts}, 0);
log_msg " => Writing counts to $count_file{transcripts} ...";
for my $id (sort keys %counters) {
printf {$fh} "%s\t%d\n" => $id,
int($counters{$id} / $count_factor);
}
# Close transcripts file
log_msg ":; Writing and closing $count_file{transcripts} ...";
close $fh
or die "Cannot write file $count_file{transcripts}: $!\n";
# Calculate 'gene' like expression
my $parent_count = $self->_calculate_parent_count(\%counters);
if (%$parent_count) {
# Save genes
log_msg ":: Saving genes count";
my $fh = $self->with_open_w($count_file{genes}, 0);
log_msg " => Writing counts to $count_file{genes} ...";
for my $id (sort keys %$parent_count) {
printf {$fh} "%s\t%d\n" => $id,
int($parent_count->{$id} / $count_factor);
}
# Close genes file
log_msg ":; Writing and closing $count_file{genes} ...";
close $fh
or die "Cannot write file $count_file{genes}: $!\n";
}
} else {
# Save coverage
log_msg ":: Saving coverage count";
my $fh = $self->with_open_w($count_file{coverage}, 0);
log_msg " => Writing counts to $count_file{coverage} ...";
for my $id (sort keys %counters) {
printf {$fh} "%s\t%d\n" => $id, $counters{$id};
}
# Close coverage file
log_msg ":; Writing and closing $count_file{coverage} ...";
close $fh
or die "Cannot write file $count_file{coverage}: $!\n";
}
}
__END__
=pod
=encoding UTF-8
( run in 0.820 second using v1.01-cache-2.11-cpan-b16cb0d3907 )