abundance_estimates_to_matrix.pl 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use FindBin;
  5. use File::Basename;
  6. use Getopt::Long qw(:config no_ignore_case bundling pass_through);
  7. my $usage = <<__EOUSAGE__;
  8. ############################################################
  9. #
  10. # Usage: $0 --est_method <method> sample1.results sample2.results ...
  11. #
  12. # or $0 --est_method <method> --quant_files file.listing_target_files.txt
  13. #
  14. # Note, if only a single input file is given, it's expected to contain the paths to all the target abundance estimation files.
  15. #
  16. # Required:
  17. #
  18. # --est_method <string> featureCounts|RSEM|eXpress|kallisto|salmon (needs to know what format to expect)
  19. #
  20. # Options:
  21. #
  22. # --cross_sample_norm <string> TMM|UpperQuartile|none (default: TMM)
  23. #
  24. # --name_sample_by_basedir name sample column by dirname instead of filename
  25. # --basedir_index <int> default(-2)
  26. #
  27. # --out_prefix <string> default: 'matrix'
  28. #
  29. # --quant_files <string> file containing a list of all the target files.
  30. #
  31. ############################################################
  32. __EOUSAGE__
  33. ;
  34. my $help_flag;
  35. my $est_method;
  36. my $val_type;
  37. my $cross_sample_norm = "TMM";
  38. my $name_sample_by_basedir = 0;
  39. my $out_prefix = "matrix";
  40. my $basedir_index = -2;
  41. my $quant_files = "";
  42. &GetOptions('help|h' => \$help_flag,
  43. 'est_method=s' => \$est_method,
  44. 'cross_sample_norm=s' => \$cross_sample_norm,
  45. 'name_sample_by_basedir' => \$name_sample_by_basedir,
  46. 'out_prefix=s' => \$out_prefix,
  47. 'basedir_index=i' => \$basedir_index,
  48. 'quant_files=s' => \$quant_files,
  49. );
  50. unless ($est_method && (@ARGV || $quant_files)) {
  51. die $usage;
  52. }
  53. unless ($est_method =~ /^(featureCounts|RSEM|eXpress|kallisto|salmon)/i) {
  54. die "Error, dont recognize --est_method $est_method ";
  55. }
  56. unless ($cross_sample_norm =~ /^(TMM|UpperQuartile|none)$/i) {
  57. die "Error, dont recognize --cross_sample_norm $cross_sample_norm ";
  58. }
  59. my @files;
  60. if ($quant_files) {
  61. # allow for a file listing the various files.
  62. @files = `cat $quant_files`;
  63. chomp @files;
  64. }
  65. elsif (@ARGV) {
  66. @files = @ARGV;
  67. }
  68. else {
  69. die $usage;
  70. }
  71. =data_formats
  72. ## featureCounts
  73. 0 gene_id
  74. 1 counts
  75. 2 fpkm
  76. 3 tpm
  77. ## RSEM:
  78. 0 transcript_id
  79. 1 gene_id
  80. 2 length
  81. 3 effective_length
  82. 4 expected_count
  83. 5 TPM
  84. 6 FPKM
  85. 7 IsoPct
  86. ## eXpress v1.5:
  87. 1 target_id
  88. 2 length
  89. 3 eff_length
  90. 4 tot_counts
  91. 5 uniq_counts
  92. 6 est_counts
  93. 7 eff_counts
  94. 8 ambig_distr_alpha
  95. 9 ambig_distr_beta
  96. 10 fpkm
  97. 11 fpkm_conf_low
  98. 12 fpkm_conf_high
  99. 13 solvable
  100. 14 tpm
  101. ## kallisto:
  102. 0 target_id
  103. 1 length
  104. 2 eff_length
  105. 3 est_counts
  106. 4 tpm
  107. ## salmon:
  108. 0 Name
  109. 1 Length
  110. 2 EffectiveLength
  111. 3 TPM
  112. 4 NumReads
  113. =cut
  114. ;
  115. my ($acc_field, $counts_field, $fpkm_field, $tpm_field);
  116. if ($est_method =~ /^rsem$/i) {
  117. $acc_field = 0;
  118. $counts_field = "expected_count";
  119. $fpkm_field = "FPKM";
  120. $tpm_field = "TPM";
  121. }
  122. elsif ($est_method =~ /^express$/i) { # as of v1.5
  123. $acc_field = "target_id";
  124. $counts_field = "eff_counts";
  125. $fpkm_field = "fpkm";
  126. $tpm_field = "tpm";
  127. }
  128. elsif ($est_method =~ /^kallisto$/i) {
  129. $acc_field = "target_id";
  130. $counts_field = "est_counts";
  131. $fpkm_field = "tpm";
  132. $tpm_field = "tpm";
  133. }
  134. elsif ($est_method =~ /^salmon/) {
  135. $acc_field = "Name";
  136. $counts_field = "NumReads";
  137. $fpkm_field = "TPM";
  138. $tpm_field = "TPM";
  139. }
  140. elsif ($est_method =~ /^featureCounts/) {
  141. $acc_field = "gene_id";
  142. $counts_field = "counts";
  143. $fpkm_field = "fpkm";
  144. $tpm_field = "tpm";
  145. }
  146. else {
  147. die "Error, dont recognize --est_method [$est_method] ";
  148. }
  149. main: {
  150. my %data;
  151. foreach my $file (@files) {
  152. print STDERR "-reading file: $file\n";
  153. open (my $fh, $file) or die "Error, cannot open file $file";
  154. my $header = <$fh>;
  155. chomp $header;
  156. my %fields = &parse_field_positions($header);
  157. #use Data::Dumper; print STDERR Dumper(\%fields);
  158. while (<$fh>) {
  159. chomp;
  160. my @x = split(/\t/);
  161. my $acc = $x[ $fields{$acc_field} ];
  162. my $count = $x[ $fields{$counts_field} ];
  163. my $fpkm = $x[ $fields{$fpkm_field} ];
  164. my $tpm = $x[ $fields{$tpm_field} ];
  165. $data{$acc}->{$file}->{count} = $count;
  166. $data{$acc}->{$file}->{FPKM} = $fpkm;
  167. $data{$acc}->{$file}->{TPM} = $tpm;
  168. }
  169. close $fh;
  170. }
  171. my @filenames = @files;
  172. foreach my $file (@filenames) {
  173. if ($name_sample_by_basedir) {
  174. my @path = split(m|/|, $file);
  175. $file = $path[$basedir_index];
  176. }
  177. else {
  178. $file = basename($file);
  179. }
  180. }
  181. print STDERR "\n\n* Outputting combined matrix.\n\n";
  182. my $counts_matrix_file = "$out_prefix.counts.matrix";
  183. my $TPM_matrix_file = "$out_prefix.TPM.not_cross_norm";
  184. open (my $ofh_counts, ">$counts_matrix_file") or die "Error, cannot write file $counts_matrix_file";
  185. open (my $ofh_TPM, ">$TPM_matrix_file") or die "Error, cannot write file $TPM_matrix_file";
  186. { # check to see if they're unique
  187. my %filename_map = map { + $_ => 1 } @filenames;
  188. if (scalar keys %filename_map != scalar @filenames) {
  189. die "Error, the column headings: @filenames are not unique. Should you consider using the --name_sample_by_basedir parameter?";
  190. }
  191. }
  192. # clean up matrix headers
  193. foreach my $file (@filenames) {
  194. # also, get rid of the part of the filename that RSEM adds
  195. $file =~ s/\.(genes|isoforms)\.results$//;
  196. # featureCounts
  197. $file =~ s/\.count$//;
  198. }
  199. print $ofh_counts join("\t", "", @filenames) . "\n";
  200. print $ofh_TPM join("\t", "", @filenames) . "\n";
  201. foreach my $acc (keys %data) {
  202. print $ofh_counts "$acc";
  203. print $ofh_TPM "$acc";
  204. foreach my $file (@files) {
  205. my $count = $data{$acc}->{$file}->{count};
  206. unless (defined $count) {
  207. $count = "NA";
  208. }
  209. my $tpm = $data{$acc}->{$file}->{TPM};
  210. if (defined $tpm) {
  211. $tpm = $tpm/1;
  212. }
  213. else {
  214. $tpm = "NA";
  215. }
  216. print $ofh_counts "\t$count";
  217. print $ofh_TPM "\t$tpm";
  218. }
  219. print $ofh_counts "\n";
  220. print $ofh_TPM "\n";
  221. }
  222. close $ofh_counts;
  223. close $ofh_TPM;
  224. if (scalar @files > 1) {
  225. ## more than one sample
  226. if ($cross_sample_norm =~ /^TMM$/i) {
  227. my $cmd = "$FindBin::RealBin/support_scripts/run_TMM_scale_matrix.pl --matrix $TPM_matrix_file > $out_prefix.$cross_sample_norm.EXPR.matrix";
  228. &process_cmd($cmd);
  229. }
  230. elsif ($cross_sample_norm =~ /^UpperQuartile$/) {
  231. my $cmd = "$FindBin::RealBin/support_scripts/run_UpperQuartileNormalization_matrix.pl --matrix $TPM_matrix_file > $out_prefix.$cross_sample_norm.EXPR.matrix";
  232. &process_cmd($cmd);
  233. }
  234. elsif ($cross_sample_norm =~ /^none$/i) {
  235. print STDERR "-not performing cross-sample normalization.\n";
  236. }
  237. }
  238. else {
  239. unless (scalar @files == 1) {
  240. die "Error, no target samples. Shouldn't get here.";
  241. }
  242. print STDERR "Warning, only one sample, so not performing cross-sample normalization\n";
  243. }
  244. print STDERR "Done.\n\n";
  245. exit(0);
  246. }
  247. ####
  248. sub process_cmd {
  249. my ($cmd) = @_;
  250. print STDERR $cmd;
  251. my $ret = system($cmd);
  252. if ($ret) {
  253. die "Error, CMD: $cmd died with ret $ret";
  254. }
  255. return;
  256. }
  257. ####
  258. sub parse_field_positions {
  259. my ($header) = @_;
  260. my %field_pos;
  261. my @fields = split(/\s+/, $header);
  262. for (my $i = 0; $i <= $#fields; $i++) {
  263. $field_pos{$fields[$i]} = $i;
  264. $field_pos{$i} = $i; # for fixed column assignment
  265. }
  266. return(%field_pos);
  267. }