view rapsodyn/PrepareFastqLight.pl @ 14:93e6f2af1ce2 draft

Uploaded
author mcharles
date Mon, 26 Jan 2015 18:10:52 -0500
parents 0a6c1cfe4dc8
children 56d328bce3a7
line wrap: on
line source

#!/usr/bin/perl
#v1.1.0 manage empty files
#v1.0.4 bug correction, last read not considered
#v1.0.3 support rapsodyn header (.... 1:...  / .... 2:...)
#V1.0.2 added auto type detection
#V1.0.1 added log, option parameters
use strict;
use warnings;
use Getopt::Long;

my $read1_file;
my $read2_file;
my $log_file;
my $output1_file;
my $output2_file;

my $TYPE="sanger";
my $MIN_LENGTH=30;
my $MIN_QUALITY=30;

my $VERBOSE = "OFF";

GetOptions (
"read1_file=s" => \$read1_file,
"read2_file=s" => \$read2_file,
"log_file=s" => \$log_file,
"output1_file=s" => \$output1_file,
"output2_file=s" => \$output2_file,
"type=s" => \$TYPE,
"min_length=i" => \$MIN_LENGTH,
"min_quality=i" => \$MIN_QUALITY,
"verbose=s" => \$VERBOSE
) or die("Error in command line arguments\n");


my $nb_read1=0;
my $nb_base_read1=0;
my $nb_read2=0;
my $nb_base_read2=0;

my $nb_read1_t=0;
my $nb_base_read1_t=0;
my $nb_read2_t=0;
my $nb_base_read2_t=0;

my $nb_base_current_t=0;

open(READ1, $read1_file) or die ("Can't open $read1_file\n");
open(READ2, $read2_file) or die ("Can't open $read2_file\n");
open(OUT1, ">$output1_file") or die ("Can't open $output1_file\n");
open(OUT2, ">$output2_file") or die ("Can't open $output2_file\n");
open (LF,">$log_file") or die("Can't open $log_file\n");

if (( -z READ1)&&( -z READ2)){
	exit(0);
}
elsif (( -z READ1)||( -z READ2)){
	print STDERR "One empty File\n";
	exit(0);
}


my $error1=0;
my $error2=0;
my $error3=0;
my $error4=0;
my $error5=0;
my $error6=0;
my $error7=0;
my $error8=0;
my $error9=0;
my $error10=0;

my $auto_type="";
my %qual;
if ($TYPE eq "auto"){
	my $compt=0;
	open(DETECT, $read1_file) or die ("Can't open $read1_file\n"); 
	while (my $ligne1_r1 =<DETECT>){
		my $ligne2_r1 =<DETECT>;
		my $ligne3_r1 =<DETECT>;
		my $ligne4_r1 =<DETECT>;
		$compt++;
		if ($ligne4_r1 =~ /^(.*)\s*$/i){
			my $qual = $1;
			my @q = split(//,$qual);
			for (my $i=0;$i<=$#q;$i++){
				my $num = ord($q[$i]);
				if ($qual{$num}){
					$qual{$num}++;
				}
				else {
					$qual{$num} = 1;
				}
				#range sanger / illumina 1.8+ 	: 33->94
				#range illumina 1.3->1.7 	: 64->105
				if ($num > 94){$auto_type = "illumina";last;}
				if ($num < 64){$auto_type = "sanger";last;}
			}
		}
		else {
			print STDERR "Error in format detection : quality not recognized\n$ligne4_r1";
			exit(0);
		}
	
		if ($auto_type ne ""){
			last;
		}

	}
	close (DETECT);
	if ($auto_type eq ""){
		print STDERR "Error in format detection : type not recognized parsing read1\n";
		foreach my $key (sort {$a <=> $b} keys %qual){
			print "$key\t:\t",$qual{$key},"\n";
		}
		exit(0);
	}
	else {
		$TYPE = $auto_type;
	}
}



my $compt=0;
while (my $ligne1_r1 =<READ1>){
	my $ligne2_r1 =<READ1>;
	my $ligne3_r1 =<READ1>;
	my $ligne4_r1 =<READ1>;
	my $ligne1_r2 =<READ2>;
	my $ligne2_r2 =<READ2>;
	my $ligne3_r2 =<READ2>;
	my $ligne4_r2 =<READ2>;
	# chomp($ligne1_r1);
	# chomp($ligne2_r1);
	# chomp($ligne3_r1);
	# chomp($ligne4_r1);
	# chomp($ligne2_r1);
	
	$compt++;
	$nb_read1++;
	$nb_read2++;
	
#@ 1 sec
	if ((!$ligne1_r1)||(!$ligne2_r1)||(!$ligne3_r1)||(!$ligne4_r1)||(!$ligne1_r2)||(!$ligne2_r2)||(!$ligne3_r2)||(!$ligne4_r2)){
		if ($VERBOSE eq "ON"){
			print "Error in file format";
			if ($ligne1_r1){print $ligne1_r1;}
			if ($ligne2_r1){print $ligne2_r1;}
			if ($ligne3_r1){print $ligne3_r1;}
			if ($ligne4_r1){print $ligne4_r1;}
			if ($ligne1_r2){print $ligne1_r2;}
			if ($ligne2_r2){print $ligne2_r2;}
			if ($ligne3_r2){print $ligne3_r2;}
			if ($ligne4_r2){print $ligne4_r2;}
			print "\n";
		}
		$error1++;
	}
	elsif(($ligne1_r1 !~/^\@/)||($ligne1_r2 !~/^\@/)||($ligne3_r1 !~/^\+/)||($ligne3_r2 !~/^\+/)){
		if ($VERBOSE eq "ON"){
			print "Error in header : format\n";
			print $ligne1_r1;
			print $ligne2_r1;
			print $ligne3_r1;
			print $ligne4_r1;
			print $ligne1_r2;
			print $ligne2_r2;
			print $ligne3_r2;
			print $ligne4_r2;
			print "\n";
		}
		$error2++;
	}
#@ 1 - 2 sec
	else {
		
		my $length_seq1 = length(chomp($ligne2_r1));
		my $length_qual1 =length(chomp($ligne4_r1));
		my $seq1;
		my $qual1;
	
		my $length_seq2 = length(chomp($ligne2_r2));
		my $length_qual2 =length(chomp($ligne4_r2));
		my $seq2;
		my $qual2;
		my $header1="";
		my $header2="";
		my $repheader1="";
		my $repheader2="";

		
		if ($ligne1_r1 =~/^\@(.*?)[\s\/]/){
			$header1 = $1;
		}
		
		if ($ligne3_r1 =~/^\+(.*?)[\s\/]/){
                        $repheader1 = $1;
               	}

		if ($ligne1_r2 =~/^\@(.*?)[\s\/]/){
                        $header2 = $1;
               	}

		if ($ligne3_r2 =~/^\+(.*?)[\s\/]/){
                        $repheader2 = $1;
               	}
#@ 2 sec
		
		### Verification de la coherence sequence /qualité   @ 1 sec
		if (($TYPE eq "illumina")&&((!$header1)||(!$header2)||(!$repheader1)||(!$repheader2))){
			if ($VERBOSE eq "ON"){
				print "Error in header : empty\n";
				print $ligne1_r1;
				print $ligne2_r1;
				print $ligne3_r1;
				print $ligne4_r1;
				print $ligne1_r2;
				print $ligne2_r2;
				print $ligne3_r2;
				print $ligne4_r2;
				print "\n";
			}
			$error3++;
		}
		elsif (($TYPE eq "sanger")&&((!$header1)||(!$header2))){
			if ($VERBOSE eq "ON"){
				print "Error in header ref : empty\n";
				print $ligne1_r1;
				print $ligne2_r1;
				print $ligne3_r1;
				print $ligne4_r1;
				print $ligne1_r2;
				print $ligne2_r2;
				print $ligne3_r2;
				print $ligne4_r2;
				print "\n";
			}
			$error3++;
		}
		elsif (($TYPE eq "illumina")&&(($header1 ne $repheader1)||($header2 ne $repheader2)||($header1 ne $header2))){
			if ($VERBOSE eq "ON"){
				print "Error in header : different\n";
				print $ligne1_r1;
				print $ligne2_r1;
				print $ligne3_r1;
				print $ligne4_r1;
				print $ligne1_r2;
				print $ligne2_r2;
				print $ligne3_r2;
				print $ligne4_r2;
				print "\n";
			}
			$error4++;
		}
		elsif (($TYPE eq "sanger")&&($header1 ne $header2)){
			if ($VERBOSE eq "ON"){
				print "Error in header : different\n";
				print $ligne1_r1;
				print $ligne2_r1;
				print $ligne3_r1;
				print $ligne4_r1;
				print $ligne1_r2;
				print $ligne2_r2;
				print $ligne3_r2;
				print $ligne4_r2;
				print "\n";
			}
			$error4++;
		}
		elsif (($length_seq1 != $length_qual1)||($length_seq2 != $length_qual2)){
			if ($VERBOSE eq "ON"){
				print "Error in seq/qual length\n";
				print "$length_seq1 / $length_qual1 \t $length_seq2 / $length_qual2\n";
				print $ligne1_r1;
				print $ligne2_r1;
				print $ligne3_r1;
				print $ligne4_r1;
				print "\n";
				print $ligne1_r2;
				print $ligne2_r2;
				print $ligne3_r2;
				print $ligne4_r2;
				print "\n";
			}
			$error5++;
		}
#@ 1 - 2 sec	
		else {
			#print "TEST : $compt\n";
			### Parsing sequence & qualité
			if ($ligne2_r1 =~ /^([ATGCNX]+)\s*$/i){
				$seq1 = $1;
				$nb_base_read1 += length($seq1);
			}
			if ($ligne2_r2 =~ /^([ATGCNX]+)\s*$/i){
				$seq2 = $1;
				$nb_base_read2 += length($seq2);
			}
			if ($ligne4_r1 =~ /^(.*)\s*$/i){
				$qual1 = $1;
			}
			if ($ligne4_r2 =~ /^(.*)\s*$/i){
				$qual2 = $1;
			}
#@ 2 sec			
			### Verification du parsing et de la coherence sequence /qualité (n°2)
			if ((!$seq1)||(!$seq2)||(!$qual1)||(!$qual2)){
				if ($VERBOSE eq "ON"){
					print "Error parsing seq / quality \n";
					print $ligne1_r1;
					print $ligne2_r1;
					print $ligne3_r1;
					print $ligne4_r1;
					print $ligne1_r2;
					print $ligne2_r2;
					print $ligne3_r2;
					print $ligne4_r2;
					print "\n";
				}
				$error6++;
			}
			elsif ((length($seq1) != length($qual1))||(length($seq2) != length($qual2))){
				if ($VERBOSE eq "ON"){
					print "Error in seq/qual length after parsing\n";
					print $ligne1_r1;
					print $ligne2_r1;
					print $ligne3_r1;
					print $ligne4_r1;
					print $ligne1_r2;
					print $ligne2_r2;
					print $ligne3_r2;
					print $ligne4_r2;
					print "\n";
				}
				$error7++;
			}
#@ <1 sec
			else {
				my $fastq_lines_r1="";
				my $fastq_lines_r2="";
				my $nb_base_current_read1_t = 0;
				my $nb_base_current_read2_t = 0;
				
				$fastq_lines_r1 = &grooming_and_trimming($ligne1_r1,$seq1,$qual1);
				$nb_base_current_read1_t = $nb_base_current_t;
				if ($fastq_lines_r1){
					$fastq_lines_r2 = &grooming_and_trimming($ligne1_r2,$seq2,$qual2);
					$nb_base_current_read2_t = $nb_base_current_t;
				}
				if ($fastq_lines_r2){
					print OUT1 $fastq_lines_r1;
					print OUT2 $fastq_lines_r2;

					$nb_read1_t++;
					$nb_read2_t++;
					$nb_base_read1_t += $nb_base_current_read1_t;
					$nb_base_read2_t += $nb_base_current_read2_t;


				}
			}
		}
	

#@ 7 sec
	}
}

close (READ1);
close (READ2);
close (OUT1);
close (OUT2);

print LF "\n####\t Fastq preparation \n";
print LF "Fastq format : $TYPE\n";
print LF "## Before preparation\n";
print LF "#Read1 :\t$nb_read1\t#Base :\t$nb_base_read1\n";
print LF "#Read2 :\t$nb_read2\t#Base :\t$nb_base_read2\n";
print LF "## After preparation\n";
print LF "#Read1 :\t$nb_read1_t\t#Base :\t$nb_base_read1_t\n";
print LF "#Read2 :\t$nb_read2_t\t#Base :\t$nb_base_read2_t\n";
close (LF);
		

sub grooming_and_trimming{
	my $header = shift;
	my $seq = shift;
	my $quality = shift;
	my $quality_converted="";
	my $quality_ori=$quality;
	
	my $lengthseq = length($seq);
	my $startTrim = 0;
	my $stopTrim = length($quality)-1;
	my $startnoN = $startTrim;
	my $stopnoN = $stopTrim;
	
	
	my $chercheN = $seq;
	my @bad_position_N;
	my @bad_position_Q;
	my $current_index = index($chercheN,"N");
	my $abs_index = $current_index;
	while ($current_index >=0){
		push (@bad_position_N,$abs_index);

		if ($current_index<length($seq)){
			$chercheN = substr($chercheN,$current_index+1);
			$current_index = index($chercheN,"N");
			$abs_index = $current_index + $bad_position_N[$#bad_position_N]+1;
		}
		else {
			last;
		}
	}
		
	my @q = split(//,$quality);
	for (my $i=0;$i<=$#q;$i++){
		my $chr = $q[$i];
		my $num = ord($q[$i]);
		if ($TYPE eq "illumina"){
			$num = $num - 31; # 31 comme la difference entre la plage sanger (33-> 93 / 0->60) et illumina (64->104 / 0->40)
			$quality_converted .= chr($num);
		}
		
		if ($num < $MIN_QUALITY + 33){ #33 comme le départ de la plage sanger
			push(@bad_position_Q,$i);
		}
	}
	if ($quality_converted){$quality = $quality_converted;}
	
	my @bad_position = (@bad_position_N, @bad_position_Q);
	
	if ($#bad_position>=0){
		@bad_position = sort {$a <=> $b} @bad_position;
		my %coord=%{&extract_longer_string_coordinates_from_bad_position(0,$stopTrim,\@bad_position)};
		$startTrim = $coord{"start"};
		$stopTrim = $coord{"stop"};
#print "$startTrim .. $stopTrim\n";
		
	}
	my $lengthTrim = $stopTrim - $startTrim +1;

	#if ($stats_length{$lengthTrim}){
	#	$stats_length{$lengthTrim} = 1;		
	#}
	#else {
	#	$stats_length{$lengthTrim}++;
	#}
	my $fastq_lines="";
	
#	if ($header =~ /GA8\-EAS671_0005\:3\:1\:1043\:4432/){
#		print "HEAD:\t$header";	
#		print "SEQ:\n$seq\n";
#		print "$quality_ori\n";
#		print "$quality\n";
#		for (my $i=0;$i<=$#bad_position;$i++){
#			print $bad_position[$i]."(".$q[$bad_position[$i]]." : ".ord($q[$bad_position[$i]]).")"."\t";
#		}
#		print "\n";
#		print "$startTrim .. $stopTrim / $lengthTrim \n";
#		print $fastq_lines;
#		print "\n";
#	}

	#for (my $i=$startTrim;$i<=$stopTrim;$i++){
	#	if ($stats_quality{ord($q{$i])}){
	#		$stats_quality{ord($q{$i])}=1;
	#	}
	#	else {
	#		$stats_quality{ord($q{$i])}++;
	#	}
	#}

	if ($lengthTrim >= $MIN_LENGTH){
		$fastq_lines .= $header;
		my $new_seq = substr($seq,$startTrim,$lengthTrim);
		$nb_base_current_t = length($new_seq);
		$fastq_lines .= $new_seq."\n";
		$fastq_lines .= "+\n";
		my $new_q = substr($quality,$startTrim,$lengthTrim);
		$fastq_lines .= $new_q."\n";
		return $fastq_lines;
		
	}
	else {
		#print "Insufficient length after trimming\n";
		return "";
	}
}

sub extract_longer_string_coordinates_from_bad_position{
	my $start=shift;
	my $stop =shift;
	my $refbad = shift;
	my @bad_position = @$refbad;
	my %coord;
	
	my $current_start = $start;
	my $current_stop = $bad_position[0]-1;
	if ($current_stop < $start){$current_stop = $start;}
	
	
	#debut -> premier N
	my $current_length = $current_stop - $current_start +1;
	my $test_length;
	
	#entre les N
	for (my $i=1;$i<=$#bad_position;$i++){
		$test_length = $bad_position[$i]+1-$bad_position[$i-1]-1;
		if ( $test_length >  $current_length){
			$current_start = $bad_position[$i-1]+1;
			$current_stop = $bad_position[$i]-1;
			$current_length = $current_stop - $current_start +1;
		}
	}
	
	#dernier N -> fin
	$test_length = $stop-$bad_position[$#bad_position]+1;
	if ( $test_length >  $current_length){
		$current_start = $bad_position[$#bad_position]+1;
		if ($current_start > $stop){$current_start=$stop;}
		$current_stop = $stop;
	}	
	$coord{"start"}=$current_start;
	$coord{"stop"}= $current_stop;
	$coord{"lenght"}=$current_stop-$current_start+1;
	
	return \%coord;
}