diff mayachemtools/bin/AnalyzeSDFilesData.pl @ 0:73ae111cf86f draft

Uploaded
author deepakjadmin
date Wed, 20 Jan 2016 11:55:01 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mayachemtools/bin/AnalyzeSDFilesData.pl	Wed Jan 20 11:55:01 2016 -0500
@@ -0,0 +1,1306 @@
+#!/usr/bin/perl -w
+#
+# $RCSfile: AnalyzeSDFilesData.pl,v $
+# $Date: 2015/02/28 20:46:04 $
+# $Revision: 1.27 $
+#
+# Author: Manish Sud <msud@san.rr.com>
+#
+# Copyright (C) 2015 Manish Sud. All rights reserved.
+#
+# This file is part of MayaChemTools.
+#
+# MayaChemTools is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 3 of the License, or (at your option) any
+# later version.
+#
+# MayaChemTools is distributed in the hope that it will be useful, but without
+# any warranty; without even the implied warranty of merchantability of fitness
+# for a particular purpose.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
+# write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
+# Boston, MA, 02111-1307, USA.
+#
+
+use strict;
+use FindBin; use lib "$FindBin::Bin/../lib";
+use Getopt::Long;
+use File::Basename;
+use Text::ParseWords;
+use Benchmark;
+use FileUtil;
+use SDFileUtil;
+use TextUtil;
+use StatisticsUtil;
+
+my($ScriptName, %Options, $StartTime, $EndTime, $TotalTime);
+
+# Autoflush STDOUT
+$| = 1;
+
+# Starting message...
+$ScriptName = basename($0);
+print "\n$ScriptName: Starting...\n\n";
+$StartTime = new Benchmark;
+
+# Get the options and setup script...
+SetupScriptUsage();
+if ($Options{help} || @ARGV < 1) {
+  die GetUsageFromPod("$FindBin::Bin/$ScriptName");
+}
+
+my(@SDFilesList);
+@SDFilesList = ExpandFileNames(\@ARGV, "sd sdf");
+
+print "Processing options...\n";
+my(%OptionsInfo);
+ProcessOptions();
+
+# Collect information about SD files...
+print "Checking input SD file(s)...\n";
+my(%SDFilesInfo);
+RetrieveSDFilesInfo();
+ProcessSDFilesDataLabelsInfo();
+
+# Generate output files...
+my($FileIndex);
+if (@SDFilesList > 1) {
+  print "\nProcessing SD files...\n";
+}
+for $FileIndex (0 .. $#SDFilesList) {
+  if ($SDFilesInfo{FileOkay}[$FileIndex]) {
+    print "\nProcessing file $SDFilesList[$FileIndex]...\n";
+    AnalyzeSDFile($FileIndex);
+  }
+}
+print "\n$ScriptName:Done...\n\n";
+
+$EndTime = new Benchmark;
+$TotalTime = timediff ($EndTime, $StartTime);
+print "Total time: ", timestr($TotalTime), "\n";
+
+###############################################################################
+
+# Analyze data...
+sub AnalyzeSDFile {
+  my($Index) = @_;
+  my($SDFile, $DataLabel, $DataValue, @DataLabelsToAnalyze, %DataFieldValuesToAnalyzeMap);
+
+  $SDFile = $SDFilesList[$Index];
+  @DataLabelsToAnalyze = @{$SDFilesInfo{UniqueDataLabelsToAnalyze}[$Index]};
+  %DataFieldValuesToAnalyzeMap = ();
+  for $DataLabel (@DataLabelsToAnalyze) {
+    @{$DataFieldValuesToAnalyzeMap{$DataLabel}} = ();
+  }
+
+  # Collect appropriate data field label values for analysis...
+  my($CmpdString, @CmpdLines, %DataFieldValues, $CmpdCount, $InvalidCmpdCount, @InvalidCmpdDataLabels);
+  open SDFILE, "$SDFile" or die "Error: Can't open $SDFile: $! \n";
+  $CmpdCount = 0;
+  $InvalidCmpdCount = 0;
+  while ($CmpdString = ReadCmpdString(\*SDFILE)) {
+    $CmpdCount++;
+    @CmpdLines = split "\n", $CmpdString;
+    %DataFieldValues = GetCmpdDataHeaderLabelsAndValues(\@CmpdLines);
+    @InvalidCmpdDataLabels = ();
+    DATALABEL: for $DataLabel (@DataLabelsToAnalyze) {
+      if (exists $DataFieldValues{$DataLabel}) {
+	$DataValue = $DataFieldValues{$DataLabel};
+	if ($OptionsInfo{CheckData}) {
+	  if (!IsNumerical($DataValue)) {
+	    push @InvalidCmpdDataLabels, $DataLabel;
+	    next DATALABEL;
+	  }
+	}
+	push @{$DataFieldValuesToAnalyzeMap{$DataLabel}}, $DataValue;
+      }
+    }
+    if (@InvalidCmpdDataLabels) {
+      $InvalidCmpdCount++;
+      if ($OptionsInfo{DetailLevel} >=4 ) {
+	print "Compound record $CmpdCount contains ", scalar(@InvalidCmpdDataLabels)," non-numerical or empty value(s) for data field(s) - ", JoinWords(\@InvalidCmpdDataLabels, ", ", 0)," - to be analyzed:\n$CmpdString \n";
+      }
+      elsif ($OptionsInfo{DetailLevel} >= 3) {
+	print "Compound record $CmpdCount contains ", scalar(@InvalidCmpdDataLabels)," non-numerical or empty value(s) for data field(s) - ", JoinWords(\@InvalidCmpdDataLabels, ", ", 0)," - to be analyzed...\n";
+      }
+      elsif ($OptionsInfo{DetailLevel} >= 2) {
+	print "Compound record $CmpdCount contains ", scalar(@InvalidCmpdDataLabels)," non-numerical or empty value(s) for data field to be analyzed...\n";
+      }
+    }
+  }
+  if ($InvalidCmpdCount && ($OptionsInfo{DetailLevel} >= 1)) {
+    print "Non-numerical or empty data present in $InvalidCmpdCount compound record(s)...\n";
+  }
+  close SDFILE;
+
+  # Perform the analysis...
+  my(@SpecifiedFunctionNames, $SpecifiedFunction);
+  @SpecifiedFunctionNames = ();
+
+  for $SpecifiedFunction (@{$OptionsInfo{SpecifiedStatisticalFunctions}}) {
+    if ($SpecifiedFunction !~ /^(Covariance|Correlation|Frequency|Rsquare|StandardScores|StandardScoresN)$/i) {
+      push @SpecifiedFunctionNames, $OptionsInfo{SpecifiedStatisticalFunctionsMap}{lc($SpecifiedFunction)};
+    }
+  }
+  if (@SpecifiedFunctionNames) {
+    PerformAnalysis($Index, \@SpecifiedFunctionNames, \%DataFieldValuesToAnalyzeMap)
+  }
+  if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{covariance}) || exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{correlation}) || exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{rsquare})) {
+    if ($OptionsInfo{AllDataLabelPairs} || $OptionsInfo{CommonDataLabelPairs}) {
+      PerformMatrixAnalysis($Index, \%DataFieldValuesToAnalyzeMap);
+    }
+    else {
+      # Perform pairwise analysis for specified columns and write out calculated values - correlation
+      # rsquare, or covariance - in the same file.
+      PerformDataLabelPairAnalysis($Index, \%DataFieldValuesToAnalyzeMap);
+    }
+  }
+  if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{standardscores}) || exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{standardscoresn}) ) {
+    PerformStandardScoresAnalysis($Index, \%DataFieldValuesToAnalyzeMap);
+  }
+  if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{frequency})) {
+    PerformFrequencyAnalysis($Index, \%DataFieldValuesToAnalyzeMap);
+  }
+
+}
+
+# Calculate values for various statistical functions...
+sub PerformAnalysis {
+  my($Index, $SpecifiedFunctionNamesRef, $DataValuesToAnalyzeMapRef) = @_;
+  my($NewTextFile, $Line, $SpecifiedFunction, $Label, @ColLabels, @DataLabelsToAnalyze);
+
+  $NewTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . $OptionsInfo{FileNameMode} . "." . $SDFilesInfo{NewTextFileExt}[$Index];
+
+  print "Generating new text file $NewTextFile...\n";
+  open NEWTEXTFILE, ">$NewTextFile" or die "Error: Can't open $NewTextFile: $! \n";
+
+  # Write out column labels...
+  @ColLabels = ();
+  push @ColLabels, "DataLabel";
+  for $SpecifiedFunction (@{$SpecifiedFunctionNamesRef}) {
+    $Label = $SpecifiedFunction;
+    if ($SpecifiedFunction =~ /^(KLargest|KSmallest)$/i) {
+      my($KthValue);
+      $KthValue = ($SpecifiedFunction =~ /^KLargest$/i) ? $OptionsInfo{KLargest} : $OptionsInfo{KSmallest};
+      $Label = AddNumberSuffix($KthValue) . "$SpecifiedFunction";
+      $Label =~ s/K//g;
+    }
+    elsif ($SpecifiedFunction =~ /^TrimMean$/i) {
+      $Label = "${SpecifiedFunction}($OptionsInfo{TrimFraction})";
+    }
+    push @ColLabels, $Label;
+  }
+  $Line = JoinWords(\@ColLabels, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+  print NEWTEXTFILE "$Line\n";
+
+  # Go over each column to be analyzed...
+  @DataLabelsToAnalyze = @{$SDFilesInfo{DataLabelsToAnalyze}[$Index]};
+
+  # Turn off "strict"; otherwise, invoking statistical functions using function name string
+  # is problematic.
+  no strict;
+
+  my($DataValuesRef, $DataLabel, $Value, @RowValues, %CalculatedValues);
+  %CalculatedValues = ();
+  for $DataLabel (@DataLabelsToAnalyze) {
+    @RowValues = ();
+    # Setup column id...
+    push @RowValues, $DataLabel;
+    $DataValuesRef =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel}};
+    FUNCTIONNAME: for $SpecifiedFunction (@{$SpecifiedFunctionNamesRef}) {
+      $Value = "";
+      if (!@{$DataValuesToAnalyzeMapRef->{$DataLabel}}) {
+	# Invalid column values...
+	push @RowValues, $Value;
+	next FUNCTIONNAME;
+      }
+      if ($SpecifiedFunction =~ /^Count$/i) {
+	$Value = @{$DataValuesToAnalyzeMapRef->{$DataLabel}};
+      }
+      elsif ($SpecifiedFunction =~ /^KLargest$/i) {
+	$Value = &$SpecifiedFunction($DataValuesRef, $OptionsInfo{KLargest});
+      }
+      elsif ($SpecifiedFunction =~ /^KSmallest$/i) {
+	$Value = &$SpecifiedFunction($DataValuesRef, $OptionsInfo{KSmallest});
+      }
+      elsif ($SpecifiedFunction =~ /^StandardDeviation$/i) {
+	if (exists($CalculatedValues{$DataLabel}{StandardDeviation})) {
+	  $Value = $CalculatedValues{$DataLabel}{StandardDeviation};
+	}
+	else {
+	  $Value = &$SpecifiedFunction($DataValuesRef);
+	  $CalculatedValues{$DataLabel}{StandardDeviation} = $Value;
+	}
+      }
+      elsif ($SpecifiedFunction =~ /^StandardError$/i) {
+	if (!exists($CalculatedValues{$DataLabel}{StandardDeviation})) {
+	  $Value = StandardDeviation($DataValuesRef);
+	  $CalculatedValues{$DataLabel}{StandardDeviation} = $Value;
+	}
+	if (defined $CalculatedValues{$DataLabel}{StandardDeviation}) {
+	  $Value = &$SpecifiedFunction($CalculatedValues{$DataLabel}{StandardDeviation}, @{$DataValuesToAnalyzeMapRef->{$DataLabel}});
+	}
+      }
+      elsif ($SpecifiedFunction =~ /^TrimMean$/i) {
+	$Value = &$SpecifiedFunction($DataValuesRef, $OptionsInfo{TrimFraction});
+      }
+      else {
+	$Value = &$SpecifiedFunction($DataValuesRef);
+      }
+      # Format the output value. And add zero to get rid of tariling zeros...
+      $Value = (defined($Value) && length($Value)) ? (sprintf("%.$OptionsInfo{Precision}f", $Value) + 0) : "";
+      push @RowValues, $Value;
+    }
+    $Line = JoinWords(\@RowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+    print NEWTEXTFILE "$Line\n";
+  }
+  close NEWTEXTFILE;
+}
+
+# Calculate covariance, correlation, rsquare for specified data field label pairs....
+sub PerformDataLabelPairAnalysis {
+  my($Index, $DataValuesToAnalyzeMapRef) = @_;
+  my($NewTextFile, @ColLabels, $Line, $CalculateCorrelation, $CalculateRSquare, $CalculateCovariance);
+
+  $CalculateCorrelation = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{correlation}) ? 1 : 0;
+  $CalculateRSquare = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{rsquare}) ? 1 : 0;
+  $CalculateCovariance = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{covariance}) ? 1 : 0;
+
+  $NewTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . "DataFieldPairsAnalysis." .  $SDFilesInfo{NewTextFileExt}[$Index];
+  print "Generating new text file $NewTextFile...\n";
+  open NEWTEXTFILE, ">$NewTextFile" or die "Error: Can't open $NewTextFile: $! \n";
+
+  # Write out the column labels...
+  @ColLabels = ();
+  push @ColLabels, ("DataLabel1", "DataLabel2");
+  if ($CalculateCorrelation || $CalculateRSquare) {
+    push @ColLabels, "Correlation";
+    if ($CalculateRSquare) {
+      push @ColLabels, "RSquare";
+    }
+  }
+  if ($CalculateCovariance) {
+    push @ColLabels, "Covariance";
+  }
+  $Line = JoinWords(\@ColLabels, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+  print NEWTEXTFILE "$Line\n";
+
+  # Go over each data field pair...
+  my($CorrelationValue, $RSquareValue, $CovarianceValue,  $LabelIndex, $DataLabel1, $DataLabel2, $DataValues1, $DataValues2, @DataLabelPairs1ToAnalyze, @DataLabelPairs2ToAnalyze, @RowValues, $Value);
+
+  @DataLabelPairs1ToAnalyze = @{$SDFilesInfo{DataLabelPairs1ToAnalyze}[$Index]};
+  @DataLabelPairs2ToAnalyze = @{$SDFilesInfo{DataLabelPairs2ToAnalyze}[$Index]};
+  for $LabelIndex (0 .. $#DataLabelPairs1ToAnalyze) {
+    @RowValues = ();
+    $DataLabel1 = $DataLabelPairs1ToAnalyze[$LabelIndex];
+    $DataLabel2 = $DataLabelPairs2ToAnalyze[$LabelIndex];
+    $DataValues1 =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel1}};
+    $DataValues2 =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel2}};
+
+    # Setup column ids...
+    push @RowValues, $DataLabel1;
+    push @RowValues, $DataLabel2;
+
+    if (@$DataValues1 != @$DataValues2) {
+      # Print a warning...
+      warn "Warning: Skipping analysis for data field pair $DataLabel1, $DataLabel2: Number of valid data values must be same.\n";
+      if ($CalculateCorrelation || $CalculateRSquare) {
+	push @RowValues, "";
+	if ($CalculateRSquare) {
+	  push @RowValues, "";
+	}
+      }
+      if ($CalculateCovariance) {
+	push @RowValues, "";
+      }
+    }
+    else {
+      # Calculate appropriate value...
+      if ($CalculateCorrelation || $CalculateRSquare) {
+	$CorrelationValue = Correlation($DataValues1, $DataValues2);
+	$Value = (defined($CorrelationValue) && length($CorrelationValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $CorrelationValue) + 0) : "";
+	push @RowValues, $Value;
+	if ($CalculateRSquare) {
+	  $RSquareValue = (defined($CorrelationValue) && length($CorrelationValue)) ? ($CorrelationValue ** 2) : "";
+	  $Value = (length($RSquareValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $RSquareValue) + 0) : "";
+	  push @RowValues, $Value;
+	}
+      }
+      if ($CalculateCovariance) {
+	$CovarianceValue = Covariance($DataValues1, $DataValues2);
+	$Value = (defined($CovarianceValue) && length($CovarianceValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $CovarianceValue) + 0) : "";
+	push @RowValues, $Value;
+      }
+    }
+    $Line = JoinWords(\@RowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+    print NEWTEXTFILE "$Line\n";
+  }
+  close NEWTEXTFILE;
+}
+
+# Generate histogram numbers...
+sub PerformFrequencyAnalysis {
+  my($Index, $DataValuesToAnalyzeMapRef) = @_;
+  my($NewTextFile, $ColLabel, @ColLabels, @RowValues, $Line, $DataLabel, @DataLabelsToAnalyze, $DataValuesRef, $BinValue, $FrequencyValue, $Value, %FrequencyMap);
+
+  @DataLabelsToAnalyze = @{$SDFilesInfo{DataLabelsToAnalyze}[$Index]};
+  for $DataLabel (@DataLabelsToAnalyze) {
+    $NewTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . $DataLabel . "FrequencyAnalysis." .  $SDFilesInfo{NewTextFileExt}[$Index];
+    print "Generating new text file $NewTextFile...\n";
+    open NEWTEXTFILE, ">$NewTextFile" or die "Error: Can't open $NewTextFile: $! \n";
+
+    # Write out the column labels...
+    @ColLabels = ();
+    push @ColLabels , ("Bins", "Frequency");
+    $Line = JoinWords(\@ColLabels, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+    print NEWTEXTFILE "$Line\n";
+
+    #Calculate and write out frequency values...
+    %FrequencyMap = ();
+    $DataValuesRef =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel}};
+    if (@$DataValuesRef) {
+      if (@{$OptionsInfo{BinRange}}) {
+	%FrequencyMap = Frequency($DataValuesRef, \@{$OptionsInfo{BinRange}});
+      }
+      else {
+	%FrequencyMap = Frequency($DataValuesRef, $OptionsInfo{NumOfBins});
+      }
+    }
+    for $BinValue (sort { $a <=> $b }  keys %FrequencyMap) {
+      $FrequencyValue = $FrequencyMap{$BinValue};
+
+      @RowValues = ();
+      $Value = (length($BinValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $BinValue) + 0) : "";
+      push @RowValues, $Value;
+      $Value = (length($FrequencyValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $FrequencyValue) + 0) : "";
+      push @RowValues, $Value;
+
+      $Line = JoinWords(\@RowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+      print NEWTEXTFILE "$Line\n";
+    }
+    close NEWTEXTFILE;
+  }
+}
+
+# Calculate covariance, correlation/rsquare matrices....
+sub PerformMatrixAnalysis {
+  my($Index, $DataValuesToAnalyzeMapRef) = @_;
+  my($CorrelationTextFile, $CovarianceTextFile, $RSquareTextFile, $CalculateCorrelation, $CalculateRSquare, $CalculateCovariance);
+
+  $CalculateCorrelation = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{correlation}) ? 1 : 0;
+  $CalculateRSquare = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{rsquare}) ? 1 : 0;
+  $CalculateCovariance = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{covariance}) ? 1 : 0;
+
+  $CorrelationTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . "CorrelationMatrix." .  $SDFilesInfo{NewTextFileExt}[$Index];
+  $RSquareTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . "RSquareMatrix." .  $SDFilesInfo{NewTextFileExt}[$Index];
+  $CovarianceTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . "CovarianceMatrix." .  $SDFilesInfo{NewTextFileExt}[$Index];
+
+  my($TextFilesList, $Delimiter);
+  $TextFilesList =  "";
+  if ($CalculateCorrelation || $CalculateRSquare) {
+    $TextFilesList = $CorrelationTextFile;
+    if ($CalculateRSquare) {
+      $TextFilesList .= ", $CorrelationTextFile";
+    }
+  }
+  $Delimiter = length($TextFilesList) ? "," : "";
+  if ($CalculateCovariance) {
+    $TextFilesList .= "${Delimiter} ${CorrelationTextFile}";
+  }
+  if ($TextFilesList =~ /\,/) {
+    print "Generating new text files $TextFilesList...\n"
+  }
+  else {
+    print "Generating new text file $TextFilesList...\n"
+  }
+  if ($CalculateCorrelation || $CalculateRSquare) {
+    open CORRELATIONTEXTFILE, ">$CorrelationTextFile" or die "Error: Can't open $CorrelationTextFile: $! \n";
+    if ($CalculateRSquare) {
+      open RSQUARETEXTFILE, ">$RSquareTextFile" or die "Error: Can't open $RSquareTextFile: $! \n";
+    }
+  }
+  if ($CalculateCovariance) {
+    open COVARIANCETEXTFILE, ">$CovarianceTextFile" or die "Error: Can't open $CovarianceTextFile: $! \n";
+  }
+
+  my($Line, $Value, $CorrelationValue, $RSquareValue, $CovarianceValue, $DataLabel, $DataLabel1, $DataLabel2, $DataValuesRef1, $DataValuesRef2, @ColLabels, @CovarianceRowValues, @CorrelationRowValues, @RSquareRowValues);
+
+  # Write out the column labels...
+  @ColLabels = ();
+  push @ColLabels, @{$SDFilesInfo{AllDataLabels}[$Index]};
+  $Line = JoinWords(\@ColLabels, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+  if ($CalculateCorrelation || $CalculateRSquare) {
+    print CORRELATIONTEXTFILE "$Line\n";
+    if ($CalculateRSquare) {
+      print RSQUARETEXTFILE "$Line\n";
+    }
+  }
+  if ($CalculateCovariance) {
+    print COVARIANCETEXTFILE "$Line\n";
+  }
+
+  # Due to symmetric nature of these matrices, only one half needs to be
+  # calculated. So, just calculate the lower half and copy it to upper half...
+  my(%CorrelationMatrixMap, %RSquareMatrixMap, %CovarianceMatrixMap, $LabelIndex1, $LabelIndex2, @DataLabelsToAnalyze);
+
+  %CorrelationMatrixMap = (); %RSquareMatrixMap = (); %CovarianceMatrixMap = ();
+  @DataLabelsToAnalyze = ();
+  @DataLabelsToAnalyze = $OptionsInfo{AllDataLabelPairs} ? @{$SDFilesInfo{AllDataLabels}[$Index]} : @{$SDFilesInfo{CommonDataLabels}[$Index]};
+
+  for $LabelIndex1 (0 .. (@DataLabelsToAnalyze - 1)) {
+    $DataLabel1 = $DataLabelsToAnalyze[$LabelIndex1];
+    for $LabelIndex2 (0 .. $LabelIndex1) {
+      $DataLabel2 = $DataLabelsToAnalyze[$LabelIndex2];
+      $DataValuesRef1 =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel1}};
+      $DataValuesRef2 =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel2}};
+      if ($CalculateCorrelation || $CalculateRSquare) {
+	$CorrelationValue = Correlation($DataValuesRef1, $DataValuesRef2);
+	$CorrelationValue = (defined($CorrelationValue) && length($CorrelationValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $CorrelationValue) + 0) : "";
+	$CorrelationMatrixMap{$DataLabel1}{$DataLabel2} = $CorrelationValue;
+	if ($DataLabel1 ne $DataLabel2) {
+	  $CorrelationMatrixMap{$DataLabel2}{$DataLabel1} = $CorrelationValue;
+	}
+	if ($CalculateRSquare) {
+	  $RSquareValue = (defined($CorrelationValue) && length($CorrelationValue)) ? ($CorrelationValue ** 2) : "";
+	  $RSquareValue = (length($RSquareValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $RSquareValue) + 0) : "";
+	  $RSquareMatrixMap{$DataLabel1}{$DataLabel2} = $RSquareValue;
+	  if ($DataLabel1 ne $DataLabel2) {
+	    $RSquareMatrixMap{$DataLabel2}{$DataLabel1} = $RSquareValue;
+	  }
+	}
+      }
+      if ($CalculateCovariance) {
+	$CovarianceValue = Covariance($DataValuesRef1, $DataValuesRef2);
+	$CovarianceValue = (defined($CovarianceValue) && length($CovarianceValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $CovarianceValue) + 0) : "";
+	$CovarianceMatrixMap{$DataLabel1}{$DataLabel2} = $CovarianceValue;
+	if ($DataLabel1 ne $DataLabel2) {
+	  $CovarianceMatrixMap{$DataLabel2}{$DataLabel1} = $CovarianceValue;
+	}
+      }
+    }
+  }
+
+  # Write out the matrices...
+  for $LabelIndex1 (0 .. (@DataLabelsToAnalyze - 1)) {
+    $DataLabel1 = $DataLabelsToAnalyze[$LabelIndex1];
+    @CorrelationRowValues = ();
+    @RSquareRowValues = ();
+    @CovarianceRowValues = ();
+    if ($CalculateCorrelation || $CalculateRSquare) {
+      push @CorrelationRowValues, $DataLabel1;
+      if ($CalculateRSquare) {
+	push @RSquareRowValues, $DataLabel1;
+      }
+    }
+    if ($CalculateCovariance) {
+      push @CovarianceRowValues, $DataLabel;
+    }
+    for $LabelIndex2 (0 .. (@DataLabelsToAnalyze - 1)) {
+      $DataLabel2 = $DataLabelsToAnalyze[$LabelIndex2];
+      if ($CalculateCorrelation || $CalculateRSquare) {
+	push @CorrelationRowValues, $CorrelationMatrixMap{$DataLabel1}{$DataLabel2};
+	if ($CalculateRSquare) {
+	  push @RSquareRowValues, $RSquareMatrixMap{$DataLabel1}{$DataLabel2};
+	}
+      }
+      if ($CalculateCovariance) {
+	push @CovarianceRowValues, $CovarianceMatrixMap{$DataLabel1}{$DataLabel2};
+      }
+    }
+    if ($CalculateCorrelation || $CalculateRSquare) {
+      $Line = JoinWords(\@CorrelationRowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+      print CORRELATIONTEXTFILE "$Line\n";
+      if ($CalculateRSquare) {
+	$Line = JoinWords(\@RSquareRowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+	print RSQUARETEXTFILE "$Line\n";
+      }
+    }
+    if ($CalculateCovariance) {
+      $Line = JoinWords(\@CovarianceRowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+      print COVARIANCETEXTFILE "$Line\n";
+    }
+  }
+  if ($CalculateCorrelation || $CalculateRSquare) {
+    close CORRELATIONTEXTFILE;
+    if ($CalculateRSquare) {
+      close RSQUARETEXTFILE;
+    }
+  }
+  if ($CalculateCovariance) {
+    close COVARIANCETEXTFILE;
+  }
+}
+
+# Calculate standard scores...
+sub PerformStandardScoresAnalysis {
+  my($Index, $DataValuesToAnalyzeMapRef) = @_;
+  my($StandardScores, $StandardScoresN, $NewTextFile, @ColLabels, $Label, $NewLine);
+
+  $StandardScores = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{standardscores}) ? 1 : 0;
+  $StandardScoresN = exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{standardscoresn}) ? 1 : 0;
+
+  $NewTextFile = $SDFilesInfo{NewTextFileRoot}[$Index] . "StandardScores." .  $SDFilesInfo{NewTextFileExt}[$Index];
+  print "Generating new text file $NewTextFile...\n";
+  open NEWTEXTFILE, ">$NewTextFile" or die "Error: Can't open $NewTextFile: $! \n";
+
+  my($DataLabel, @DataLabelsToAnalyze);
+  # Write out column labels...
+  @ColLabels = ();
+  @DataLabelsToAnalyze = @{$SDFilesInfo{DataLabelsToAnalyze}[$Index]};
+  for $DataLabel (@DataLabelsToAnalyze) {
+    if ($StandardScores) {
+      push @ColLabels, "${DataLabel}\(StandardScores)";
+    }
+    if ($StandardScoresN) {
+      push @ColLabels, "${DataLabel}\(StandardScoresN)";
+    }
+  }
+  $NewLine = JoinWords(\@ColLabels, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+  print NEWTEXTFILE "$NewLine\n";
+
+  # Go over each column to be analyzed and calculate standard deviation
+  # and mean values...
+  my($DataValuesRef, %StandardDeviationMap, %StandardDeviationNMap, %MeanMap);
+  %StandardDeviationMap = ();
+  %StandardDeviationNMap = ();
+  %MeanMap = ();
+  for $DataLabel (@DataLabelsToAnalyze) {
+    $DataValuesRef =  \@{$DataValuesToAnalyzeMapRef->{$DataLabel}};
+    if (!exists($MeanMap{$DataLabel})) {
+      $MeanMap{$DataLabel} = Mean($DataValuesRef);
+    }
+    if ($StandardScores) {
+      if (!exists($StandardDeviationMap{$DataLabel})) {
+	$StandardDeviationMap{$DataLabel} = StandardDeviation($DataValuesRef);
+      }
+    }
+    if ($StandardScoresN) {
+      if (!exists($StandardDeviationNMap{$DataLabel})) {
+	$StandardDeviationNMap{$DataLabel} = StandardDeviationN($DataValuesRef);
+      }
+    }
+  }
+  #
+  # Go over each data field and calculate standard scores for each column
+  # using (x[i] - mean) / (n - 1) for StandardScores and (x[i] - mean) / n
+  # for StandardScoresN; write out the calculated values as well...
+
+  my($SDFile, $Value, $ValueOkay, $ScoreValue, @RowValues, $CmpdString, @CmpdLines, %DataFieldValues);
+  $SDFile = $SDFilesList[$Index];
+
+  open SDFILE, "$SDFile" or die "Error: Can't open $SDFile: $! \n";
+  while ($CmpdString = ReadCmpdString(\*SDFILE)) {
+    @CmpdLines = split "\n", $CmpdString;
+    %DataFieldValues = GetCmpdDataHeaderLabelsAndValues(\@CmpdLines);
+    @RowValues = ();
+    for $DataLabel (@DataLabelsToAnalyze) {
+      $Value = "";
+      if (exists $DataFieldValues{$DataLabel}) {
+	$Value = $DataFieldValues{$DataLabel};
+      }
+      $ValueOkay = ($OptionsInfo{CheckData} && !IsNumerical($Value)) ? 0 : 1;
+      if ($StandardScores) {
+	$ScoreValue = $ValueOkay ? (($Value - $MeanMap{$DataLabel})/$StandardDeviationMap{$DataLabel}) : "";
+	$ScoreValue = (defined($ScoreValue) && length($ScoreValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $ScoreValue) + 0) : "";
+	push @RowValues, $ScoreValue;
+      }
+      if ($StandardScoresN) {
+	$ScoreValue = $ValueOkay ? (($Value - $MeanMap{$DataLabel})/$StandardDeviationNMap{$DataLabel}) : "";
+	$ScoreValue = (defined($ScoreValue) && length($ScoreValue)) ? (sprintf("%.$OptionsInfo{Precision}f", $ScoreValue) + 0) : "";
+	push @RowValues, $ScoreValue;
+      }
+    }
+    $NewLine = JoinWords(\@RowValues, $OptionsInfo{OutDelim}, $OptionsInfo{OutQuote});
+    print NEWTEXTFILE "$NewLine\n";
+  }
+  close SDFILE;
+  close NEWTEXTFILE;
+
+}
+
+# Make sure the specified data field labels exists in SD files...
+sub ProcessSDFilesDataLabelsInfo {
+  my($Index, $DataFieldIndex, $SDFile, $DataLabel, @DataLabelsToAnalyze, %UniqueDataLabelsToAnalyzeMap);
+
+  @{$SDFilesInfo{DataLabelsToAnalyze}} = ();
+  @{$SDFilesInfo{DataLabelPairs1ToAnalyze}} = ();
+  @{$SDFilesInfo{DataLabelPairs2ToAnalyze}} = ();
+  @{$SDFilesInfo{UniqueDataLabelsToAnalyze}} = ();
+
+  FILELIST: for $Index (0 .. $#SDFilesList) {
+    $SDFile = $SDFilesList[$Index];
+
+    @{$SDFilesInfo{DataLabelsToAnalyze}[$Index]} = ();
+    @{$SDFilesInfo{DataLabelPairs1ToAnalyze}[$Index]} = ();
+    @{$SDFilesInfo{DataLabelPairs2ToAnalyze}[$Index]} = ();
+    @{$SDFilesInfo{UniqueDataLabelsToAnalyze}[$Index]} = ();
+
+    %UniqueDataLabelsToAnalyzeMap = ();
+
+    if ($SDFilesInfo{FileOkay}[$Index]) {
+      @DataLabelsToAnalyze = ();
+      if (@{$OptionsInfo{SpecifiedDataLabels}}) {
+	for $DataLabel (@{$OptionsInfo{SpecifiedDataLabels}}) {
+	  if (exists($SDFilesInfo{AllDataLabelsMap}[$Index]{$DataLabel})) {
+	    push @DataLabelsToAnalyze, $DataLabel;
+	  }
+	}
+      }
+      elsif (defined($OptionsInfo{DataFields}) && $OptionsInfo{DataFields} =~ /^All$/i) {
+	push @DataLabelsToAnalyze, @{$SDFilesInfo{AllDataLabels}[$Index]};
+      }
+      else {
+	push @DataLabelsToAnalyze, @{$SDFilesInfo{CommonDataLabels}[$Index]};
+      }
+      if (@DataLabelsToAnalyze) {
+	push @{$SDFilesInfo{DataLabelsToAnalyze}[$Index]}, @DataLabelsToAnalyze;
+	# Set up unique data field label map as well...
+	for $DataLabel (@DataLabelsToAnalyze) {
+	  if (!exists $UniqueDataLabelsToAnalyzeMap{$DataLabel}) {
+	    $UniqueDataLabelsToAnalyzeMap{$DataLabel} = $DataLabel;
+	  }
+	}
+      }
+      else {
+	warn "Warning: Ignoring file $SDFile: None of the data field labels specified, @{$OptionsInfo{SpecifiedDataLabels}}, using \"--datafields\" option exist.\n";
+	$SDFilesInfo{FileOkay}[$Index] = 0;
+	next FILELIST;
+      }
+      if (!$OptionsInfo{Overwrite} && exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{frequency})) {
+	# Make sure specific frequency files don't exist...
+	my($FrequencyFile);
+	for $DataLabel (@DataLabelsToAnalyze) {
+	  $FrequencyFile = $SDFilesInfo{NewTextFileRoot}[$Index] . $SDFilesInfo{AllDataLabelsMap}[$Index]{$DataLabel} . "FrequencyAnalysis." .  $SDFilesInfo{NewTextFileExt}[$Index];
+	  if (-e $FrequencyFile) {
+	    warn "Warning: Ignoring file $SDFile: The file $FrequencyFile already exists.\n";
+	    $SDFilesInfo{FileOkay}[$Index] = 0;
+	    next FILELIST;
+	  }
+	}
+      }
+      # Setup specified data field label pairs...
+      if (exists $OptionsInfo{SpecifiedStatisticalFunctionsMap}{correlation} || exists $OptionsInfo{SpecifiedStatisticalFunctionsMap}{covariance} || exists $OptionsInfo{SpecifiedStatisticalFunctionsMap}{rsquare}) {
+	my(@DataLabelPairsToAnalyze, $DataLabel1, $DataLabel2);
+	if (@{$OptionsInfo{SpecifiedDataLabelPairs}}) {
+	  # Make sure both data field labels exist...
+	  my($DataFieldIndex);
+	  for ($DataFieldIndex = 0; (($DataFieldIndex + 1) < @{$OptionsInfo{SpecifiedDataLabelPairs}}); $DataFieldIndex += 2 ) {
+	    $DataLabel1 = $OptionsInfo{SpecifiedDataLabelPairs}[$DataFieldIndex];
+	    $DataLabel2 = $OptionsInfo{SpecifiedDataLabelPairs}[$DataFieldIndex + 1];
+	    if (exists($SDFilesInfo{AllDataLabelsMap}[$Index]{$DataLabel1}) && exists($SDFilesInfo{AllDataLabelsMap}[$Index]{$DataLabel2})) {
+	      push @DataLabelPairsToAnalyze, ($DataLabel1, $DataLabel2);
+	    }
+	  }
+	}
+	elsif ($OptionsInfo{AllDataLabelPairs}) {
+	  for $DataLabel1 (@{$SDFilesInfo{AllDataLabels}[$Index]}) {
+	    for $DataLabel2 (@{$SDFilesInfo{AllDataLabels}[$Index]}) {
+	      push @DataLabelPairsToAnalyze, ($DataLabel1, $DataLabel2);
+	    }
+	  }
+	}
+	else {
+	  for $DataLabel1 (@{$SDFilesInfo{CommonDataLabels}[$Index]}) {
+	    for $DataLabel2 (@{$SDFilesInfo{CommonDataLabels}[$Index]}) {
+	      push @DataLabelPairsToAnalyze, ($DataLabel1, $DataLabel2);
+	    }
+	  }
+	}
+	if (@DataLabelPairsToAnalyze) {
+	  if (@DataLabelPairsToAnalyze % 2) {
+	    warn "Warning: Ignoring file $SDFile: Invalid number  values specified using \"--datafieldpairs\" option: It must contain even number of valid values.\n";
+	    $SDFilesInfo{FileOkay}[$Index] = 0;
+	    next FILELIST;
+	  }
+	  else {
+	    for ($DataFieldIndex = 0; $DataFieldIndex < @DataLabelPairsToAnalyze; $DataFieldIndex += 2) {
+	      push @{$SDFilesInfo{DataLabelPairs1ToAnalyze}[$Index]}, $DataLabelPairsToAnalyze[$DataFieldIndex];
+	      push @{$SDFilesInfo{DataLabelPairs2ToAnalyze}[$Index]}, $DataLabelPairsToAnalyze[$DataFieldIndex + 1];
+	    }
+	    # Set up unique data field labe map as well...
+	    for $DataLabel (@DataLabelPairsToAnalyze) {
+	      if (!exists $UniqueDataLabelsToAnalyzeMap{$DataLabel}) {
+		$UniqueDataLabelsToAnalyzeMap{$DataLabel} = $DataLabel;
+	      }
+	    }
+	  }
+	}
+      }
+      # Setup unique data field label array...
+      push @{$SDFilesInfo{UniqueDataLabelsToAnalyze}[$Index]}, (sort keys %UniqueDataLabelsToAnalyzeMap);
+    }
+  }
+}
+
+# Retrieve information about input SD files...
+sub RetrieveSDFilesInfo {
+  my($SDFile, $Index, $FileDir, $FileExt, $FileName, $OutFile, $OutFileRoot, $OutFileExt, $CmpdCount);
+
+  %SDFilesInfo = ();
+
+  @{$SDFilesInfo{FileOkay}} = ();
+  @{$SDFilesInfo{CmpdCount}} = ();
+  @{$SDFilesInfo{NewTextFileRoot}} = ();
+  @{$SDFilesInfo{NewTextFileExt}} = ();
+
+  @{$SDFilesInfo{AllDataFieldLabels}} = ();
+  @{$SDFilesInfo{AllDataFieldLabelsMap}} = ();
+  @{$SDFilesInfo{CommonDataLabels}} = ();
+
+  FILELIST: for $Index (0 .. $#SDFilesList) {
+    $SDFile = $SDFilesList[$Index];
+
+    $SDFilesInfo{FileOkay}[$Index] = 0;
+
+    $SDFilesInfo{CmpdCount}[$Index] = 0;
+    $SDFilesInfo{NewTextFileRoot}[$Index] = "";
+    $SDFilesInfo{NewTextFileExt}[$Index] = "";
+
+    @{$SDFilesInfo{AllDataLabels}[$Index]} = ();
+    %{$SDFilesInfo{AllDataLabelsMap}[$Index]} = ();
+    @{$SDFilesInfo{CommonDataLabels}[$Index]} = ();
+
+    if (!(-e $SDFile)) {
+      warn "Warning: Ignoring file $SDFile: It doesn't exist\n";
+      next FILELIST;
+    }
+    if (!CheckFileType($SDFile, "sd sdf")) {
+      warn "Warning: Ignoring file $SDFile: It's not a SD file\n";
+      next FILELIST;
+    }
+
+    # Generate appropriate name for the new text files...
+    $FileDir = ""; $FileName = ""; $FileExt = "";
+    ($FileDir, $FileName, $FileExt) = ParseFileName($SDFile);
+    $OutFileExt = "csv";
+    if ($Options{outdelim} =~ /^tab$/i) {
+      $OutFileExt = "tsv";
+    }
+    if ($Options{root} && (@SDFilesList == 1)) {
+      my ($RootFileDir, $RootFileName, $RootFileExt) = ParseFileName($Options{root});
+      if ($RootFileName && $RootFileExt) {
+	$FileName = $RootFileName;
+      }
+      else {
+	$FileName = $Options{root};
+      }
+      $OutFileRoot = $FileName;
+    }
+    else {
+      $OutFileRoot = $FileName;
+    }
+    $OutFile = $OutFileRoot . $OptionsInfo{FileNameMode} . ".$OutFileExt";
+
+    if (!$OptionsInfo{Overwrite}) {
+      if (-e $OutFile) {
+	warn "Warning: Ignoring file $SDFile: The file $OutFile already exists\n";
+	next FILELIST;
+      }
+      if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{covariance}) || exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{correlation}) || exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{rsquare})) {
+	if ($OptionsInfo{AllDataLabelPairs}) {
+	  if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{covariance}) && (-e "${OutFileRoot}CovarianceMatrix.${FileExt}")) {
+	    warn "Warning: Ignoring file $SDFile: The file ${OutFileRoot}Covariance.${FileExt} already exists.\n";
+	    next FILELIST;
+	  }
+	  if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{correlation}) && (-e "${OutFileRoot}CorrelationMatrix.${FileExt}")) {
+	    warn "Warning: Ignoring file $SDFile: The file ${OutFileRoot}CorrelationMatrix.${FileExt} already exists.\n";
+	    next FILELIST;
+	  }
+	  if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{rsquare}) && (-e "${OutFileRoot}RSquareMatrix.${FileExt}")) {
+	    warn "Warning: Ignoring file $SDFile: The file ${OutFileRoot}RSquareMatrix.${FileExt} already exists.\n";
+	    next FILELIST;
+	  }
+	}
+	else {
+	  if (-e "${OutFileRoot}ColumnPairsAnalysis.${FileExt}") {
+	    warn "Warning: Ignoring file $SDFile: The file ${OutFileRoot}ColumnPairsAnalysis.${FileExt} already exists.\n";
+	    next FILELIST;
+	  }
+	}
+      }
+      if (exists($OptionsInfo{SpecifiedStatisticalFunctionsMap}{standardscores}) && (-e "${OutFileRoot}StandardScores.${FileExt}")) {
+	warn "Warning: Ignoring file $SDFile: The file ${OutFileRoot}StandardScores.${FileExt} already exists.\n";
+	next FILELIST;
+      }
+    }
+
+    if (!open SDFILE, "$SDFile") {
+      warn "Warning: Ignoring file $SDFile: Couldn't open it: $! \n";
+      next FILELIST;
+    }
+
+    my($CmpdCount, $Label, $DataFieldLabelsRef, $CommonDataFieldLabelsRef, @DataFieldLabels, @CommonDataFieldLabels);
+    $CmpdCount = 0;
+    @DataFieldLabels = ();
+    @CommonDataFieldLabels = ();
+    ($CmpdCount, $DataFieldLabelsRef, $CommonDataFieldLabelsRef) = GetAllAndCommonCmpdDataHeaderLabels(\*SDFILE);
+    push @DataFieldLabels, @{$DataFieldLabelsRef};
+    push @CommonDataFieldLabels, @{$CommonDataFieldLabelsRef};
+    close SDFILE;
+
+    $SDFilesInfo{FileOkay}[$Index] = 1;
+    $SDFilesInfo{NewTextFileRoot}[$Index] = "$OutFileRoot";
+    $SDFilesInfo{NewTextFileExt}[$Index] = "$OutFileExt";
+
+    $SDFilesInfo{CmpdCount}[$Index] = $CmpdCount;
+    push @{$SDFilesInfo{AllDataLabels}[$Index]}, @DataFieldLabels;
+    push @{$SDFilesInfo{CommonDataLabels}[$Index]}, @CommonDataFieldLabels;
+    for $Label (@DataFieldLabels) {
+      $SDFilesInfo{AllDataLabelsMap}[$Index]{$Label} = $Label;
+    }
+  }
+}
+
+# Process option values...
+sub ProcessOptions {
+  %OptionsInfo = ();
+
+  $OptionsInfo{Mode} = $Options{mode};
+
+  $OptionsInfo{DataFields} = defined $Options{datafields} ? $Options{datafields} : undef;
+
+  $OptionsInfo{DetailLevel} = $Options{detail};
+
+  # Setup supported statistical functions...
+  my($SupportedFunction, @SupportedStatisticaFunctions, %SupportedStatisticaFunctionsMap);
+
+  %SupportedStatisticaFunctionsMap = ();
+  @SupportedStatisticaFunctions = qw(Average AverageDeviation Correlation Count Covariance GeometricMean Frequency HarmonicMean KLargest KSmallest Kurtosis Maximum Minimum Mean Median Mode RSquare Skewness Sum SumOfSquares StandardDeviation StandardDeviationN StandardError StandardScores StandardScoresN TrimMean Variance VarianceN);
+
+  for $SupportedFunction (@SupportedStatisticaFunctions) {
+    $SupportedStatisticaFunctionsMap{lc($SupportedFunction)} = $SupportedFunction;
+  }
+
+  # Setup a list of functions to use for analysis...
+  my($SpecifiedFunction);
+
+  %{$OptionsInfo{SpecifiedStatisticalFunctionsMap}} = ();
+  @{$OptionsInfo{SpecifiedStatisticalFunctions}} = ();
+
+  # Check mode values...
+  if ($Options{mode} =~ /^DescriptiveStatisticsBasic$/i ) {
+    $OptionsInfo{FileNameMode} = "DescriptiveStatisticsBasic";
+    @{$OptionsInfo{SpecifiedStatisticalFunctions}} = qw(Count Maximum Minimum Mean Median StandardDeviation StandardError Variance Sum);
+  }
+  elsif ($Options{mode} =~ /^DescriptiveStatisticsAll$/i ) {
+    $OptionsInfo{FileNameMode} = "DescriptiveStatisticsAll";
+    @{$OptionsInfo{SpecifiedStatisticalFunctions}} = qw(Count Maximum Minimum Mean GeometricMean HarmonicMean TrimMean Median Mode StandardDeviation Kurtosis Skewness StandardError Variance  RSquare Frequency  KLargest KSmallest Sum);
+  }
+  elsif ($Options{mode} =~ /^All$/i ) {
+    $OptionsInfo{FileNameMode} = "AllStatistics";
+    @{$OptionsInfo{SpecifiedStatisticalFunctions}} = @SupportedStatisticaFunctions;
+  }
+  else {
+    $OptionsInfo{FileNameMode} = "SpecifiedStatistics";
+
+    # Comma delimited list of functions...
+    my($Mode, @SpecifiedFunctions, @UnsupportedSpecifiedFunctions);
+
+    $Mode = $Options{mode};
+    $Mode =~ s/ //g;
+    @SpecifiedFunctions = split ",", $Mode;
+    @UnsupportedSpecifiedFunctions = ();
+    for $SpecifiedFunction (@SpecifiedFunctions) {
+      if (exists($SupportedStatisticaFunctionsMap{lc($SpecifiedFunction)})) {
+	push @{$OptionsInfo{SpecifiedStatisticalFunctions}}, $SpecifiedFunction;
+      }
+      else {
+	push @UnsupportedSpecifiedFunctions, $SpecifiedFunction;
+      }
+    }
+    if (@UnsupportedSpecifiedFunctions) {
+      if (@UnsupportedSpecifiedFunctions > 1) {
+	warn "Error: The values specified - ", JoinWords(\@UnsupportedSpecifiedFunctions, ", ", 0)," - for option \"-m --mode\" are not valid.\n";
+      }
+      else {
+	warn "Error: The value specified, @UnsupportedSpecifiedFunctions , for option \"-m --mode\" is not valid.\n";
+      }
+      die "Allowed values:", JoinWords(\@SupportedStatisticaFunctions, ", ", 0), "\n";
+    }
+  }
+
+  FUNCTION: for $SpecifiedFunction (@{$OptionsInfo{SpecifiedStatisticalFunctions}}) {
+    if (exists $OptionsInfo{SpecifiedStatisticalFunctionsMap}{lc($SpecifiedFunction)} ) {
+      next FUNCTION;
+    }
+    $OptionsInfo{SpecifiedStatisticalFunctionsMap}{lc($SpecifiedFunction)} = $SupportedStatisticaFunctionsMap{lc($SpecifiedFunction)};
+  }
+
+  # Setup delimiter and quotes...
+  $OptionsInfo{OutDelim} = ($Options{outdelim} =~ /tab/i ) ? "\t" : (($Options{outdelim} =~ /semicolon/i) ? "\;" : "\,");
+  $OptionsInfo{OutQuote} = ($Options{quote} =~ /yes/i ) ? 1 : 0;
+
+  $OptionsInfo{Overwrite} = defined $Options{overwrite} ? $Options{overwrite} : undef;
+  $OptionsInfo{Root} = defined $Options{root} ? $Options{root} : undef;
+
+  # Setup miscellaneous options...
+  $OptionsInfo{CheckData} = $Options{fast} ? 0 : 1;
+  $OptionsInfo{Precision} = $Options{precision};
+
+  $OptionsInfo{KLargest} = $Options{klargest};
+  $OptionsInfo{KSmallest} = $Options{ksmallest};
+
+  $OptionsInfo{TrimFraction} = $Options{trimfraction};
+
+  # Setup frequency bin values...
+  $OptionsInfo{NumOfBins} = 10;
+  @{$OptionsInfo{BinRange}} = ();
+  if ($Options{frequencybins} =~ /\,/) {
+    my($BinValue, @SpecifiedBinRange);
+    @SpecifiedBinRange = split /\,/,  $Options{frequencybins};
+    if (@SpecifiedBinRange < 2) {
+      die "Error: The value specified, $Options{frequencybins}, for option \"--frequencybins\" is not valid: Must contain at least two values. \n";
+    }
+    for $BinValue (@SpecifiedBinRange) {
+      if (!IsNumerical($BinValue)) {
+	die "Error: The value specified, $Options{frequencybins}, for option \"--frequencybins\" is not valid: Contains non numeric values. \n";
+      }
+    }
+    my($Index1, $Index2);
+    for $Index1 (0 .. $#SpecifiedBinRange) {
+      for $Index2 (($Index1 + 1) .. $#SpecifiedBinRange) {
+	if ($SpecifiedBinRange[$Index1] >= $SpecifiedBinRange[$Index2]) {
+	  die "Error: The value specified, $Options{frequencybins}, for option \"--frequencybins\" is not valid: Must contain values in ascending order. \n";
+	}
+      }
+    }
+    push @{$OptionsInfo{BinRange}}, @SpecifiedBinRange;
+  }
+  else {
+    $OptionsInfo{NumOfBins} = $Options{frequencybins};
+    if (!IsPositiveInteger($OptionsInfo{NumOfBins})) {
+      die "Error: The value specified, $Options{frequencybins}, for option \"--frequencybins\" is not valid. Allowed values: positive integer or \"number,number,[number]...\". \n";
+    }
+  }
+
+  # Setup specified data field labels...
+  @{$OptionsInfo{SpecifiedDataLabels}} = ();
+  if (defined $Options{datafields} && $Options{datafields} !~ /^(All|Common)$/i ) {
+    my(@SpecifiedValues) = split ",", $Options{datafields};
+    push @{$OptionsInfo{SpecifiedDataLabels}}, @SpecifiedValues;
+  }
+  @{$OptionsInfo{SpecifiedDataLabelPairs}} = ();
+  $OptionsInfo{AllDataLabelPairs} = (defined($Options{datafieldpairs}) && $Options{datafieldpairs} =~ /^AllPairs$/i) ? 1 : 0;
+  $OptionsInfo{CommonDataLabelPairs} = (defined($Options{datafieldpairs}) && $Options{datafieldpairs} =~ /^CommonPairs$/i) ? 1 : 0;
+  if (defined($Options{datafieldpairs}) && !$OptionsInfo{AllDataLabelPairs} && !$OptionsInfo{CommonDataLabelPairs}) {
+    my(@SpecifiedValues) = split ",", $Options{datafieldpairs};
+    if (@SpecifiedValues % 2) {
+      die "Error: Invalid number of values specified using \"--datafieldpairs\" option: It must contain even number of values.\n";
+    }
+    push @{$OptionsInfo{SpecifiedDataLabelPairs}}, @SpecifiedValues;
+  }
+
+}
+
+# Setup script usage  and retrieve command line arguments specified using various options...
+sub SetupScriptUsage {
+
+  # Retrieve all the options...
+  %Options = ();
+  $Options{detail} = 0;
+  $Options{datafields} = "Common";
+  $Options{datafieldpairs} = "CommonPairs";
+  $Options{frequencybins} = 10;
+  $Options{klargest} = 2;
+  $Options{ksmallest} = 2;
+  $Options{mode} = "DescriptiveStatisticsBasic";
+  $Options{outdelim} = "comma";
+  $Options{precision} = 2;
+  $Options{quote} = "yes";
+  $Options{trimfraction} = 0.1;
+
+  if (!GetOptions(\%Options, "datafields=s", "datafieldpairs=s", "detail|d=i", "frequencybins=s", "fast|f", "help|h", "klargest=i", "ksmallest=i", "mode|m=s", "outdelim=s", "overwrite|o", "precision|p=i", "quote|q=s", "root|r=s", "trimfraction=f", "workingdir|w=s")) {
+    die "\nTo get a list of valid options and their values, use \"$ScriptName -h\" or\n\"perl -S $ScriptName -h\" command and try again...\n";
+  }
+  if ($Options{workingdir}) {
+    if (! -d $Options{workingdir}) {
+      die "Error: The value specified, $Options{workingdir}, for option \"-w --workingdir\" is not a directory name.\n";
+    }
+    chdir $Options{workingdir} or die "Error: Couldn't chdir $Options{workingdir}: $! \n";
+  }
+  if (!IsInteger($Options{detail})) {
+    die "Error: The value specified, $Options{detail}, for option \"-d --detail\" is not valid. Allowed values: >= 0\n";
+  }
+  if ($Options{outdelim} !~ /^(comma|semicolon|tab)$/i) {
+    die "Error: The value specified, $Options{outdelim}, for option \"--outdelim\" is not valid. Allowed values: comma, tab, or semicolon\n";
+  }
+  if ($Options{quote} !~ /^(yes|no)$/i) {
+    die "Error: The value specified, $Options{quote}, for option \"-q --quote\" is not valid. Allowed values: yes or no\n";
+  }
+  if (!IsPositiveInteger($Options{precision})) {
+    die "Error: The value specified, $Options{precision}, for option \"-p --precision\" is not valid. Allowed values: > 0 \n";
+  }
+  if (!IsPositiveInteger($Options{klargest})) {
+    die "Error: The value specified, $Options{klargest}, for option \"--klargest\" is not valid. Allowed values: > 0 \n";
+  }
+  if (!IsPositiveInteger($Options{ksmallest})) {
+    die "Error: The value specified, $Options{ksmallest}, for option \"--ksmallest\" is not valid. Allowed values: > 0 \n";
+  }
+  if (IsFloat($Options{trimfraction})) {
+    if ($Options{trimfraction} <= 0 || $Options{trimfraction} >= 1.0) {
+      die "Error: The value specified, $Options{trimfraction}, for option \"--trimfraction\" is not valid. Allowed values: > 0 and < 1.0\n";
+    }
+  }
+  else {
+    die "Error: The value specified, $Options{trimfraction}, for option \"--trimfraction\" is not valid. Allowed values: > 0 and < 1.0\n";
+  }
+}
+
+__END__
+
+=head1 NAME
+
+AnalyzeSDFilesData.pl - Analyze numerical data field values in SDFile(s)
+
+=head1 SYNOPSIS
+
+AnalyzeSDFilesData.pl SDFile(s)...
+
+AnalyzeSDFilesData.pl [B<--datafields> "fieldlabel,[fieldlabel,...]" | All]
+[B<--datafieldpairs> "fieldlabel,fieldlabel,[fieldlabel,fieldlabel,...]" | AllPairs] [B<-d, --detail> infolevel]
+[B<-f, --fast>] [B<--frequencybins> number | "number,number,[number,...]"]
+[B<-h, --help>] [B<--klargest> number] [B<--ksmallest> number]
+[B<-m, --mode> DescriptiveStatisticsBasic | DescriptiveStatisticsAll | All | "function1, [function2,...]"]
+[B<--trimfraction> number] [B<-w, --workingdir> dirname] SDFiles(s)...
+
+=head1 DESCRIPTION
+
+Analyze numerical data field values in I<SDFile(s)> using a combination of various statistical
+functions; Non-numerical values are simply ignored. For I<Correlation, RSquare, and
+Covariance> analysis, the count of valid values in specified data field pairs must be same;
+otherwise, column data field pair is ignored. The file names are separated by space.The valid file
+extensions are I<.sdf> and I<.sd>. All other file names are ignored. All the SD files in a
+current directory can be specified either by I<*.sdf> or the current directory name.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--datafields> I<"fieldlabel,[fieldlabel,...]" | Common | All>
+
+Data fields to use for analysis. Possible values: list of comma separated data field
+labels, data fields common to all records, or all data fields. Default value: I<Common>.
+Examples:
+
+    ALogP,MolWeight,EC50
+    "MolWeight,PSA"
+
+=item B<--datafieldpairs> I<"fieldlabel,fieldlabel,[fieldlabel,fieldlabel,...]" | CommonPairs | AllPairs>
+
+This value is mode specific and is only used for I<Correlation, PearsonCorrelation, or
+Covariance> value of B<-m, --mode> option. It specifies data field label pairs to use
+for data analysis during I<Correlation> and I<Covariance> calculations. Possible values:
+comma delimited list of data field label pairs, data field label pairs common to all records,
+or all data field pairs. Default value:I<CommonPairs>. Example:
+
+    MolWeight,EC50,NumN+O,PSA
+
+For I<AllPairs> value of B<--datafieldpairs> option, all data field label pairs are used for
+I<Correlation> and I<Covariance> calculations.
+
+=item B<-d, --detail> I<infolevel>
+
+Level of information to print about column values being ignored. Default: I<0>. Possible values:
+0, 1, 2, 3, or 4.
+
+=item B<-f, --fast>
+
+In this mode, all the data field values specified for analysis are assumed to contain numerical
+data and no checking is performed before analysis. By default, only numerical data is
+used for analysis; other types of column data is ignored.
+
+=item B<--frequencybins> I<number | "number,number,[number,...]">
+
+Specify number of bins or bin range to use for frequency analysis. Default value: I<10>
+
+Number of bins value along with the smallest and largest value for a column is used to
+group the column values into different groups.
+
+The bin range list is used to group values for a column into different groups; It must contain
+values in ascending order. Examples:
+
+    10,20,30
+    0.1,0.2,0.3,0.4,0.5
+
+The frequency value calculated for a specific bin corresponds to all the column values
+which are greater than the previous bin value and less than or equal to the current bin value.
+
+=item B<-h, --help>
+
+Print this help message.
+
+=item B<--klargest> I<number>
+
+Kth largest value to find by I<KLargest> function. Default value: I<2>. Valid values: positive
+integers.
+
+=item B<--ksmallest> I<number>
+
+Kth smallest value to find by I<KSmallest> function. Default values: I<2>. Valid values: positive
+integers.
+
+=item B<-m, --mode> I<DescriptiveStatisticsBasic | DescriptiveStatisticsAll | All | "function1, [function2,...]">
+
+Specify how to analyze data in SDFile(s): calculate basic or all descriptive statistics; or
+use a comma delimited list of supported statistical functions. Possible values:
+I<DescriptiveStatisticsBasic | DescriptiveStatisticsAll | "function1,[function2]...">. Default
+value: I<DescriptiveStatisticsBasic>
+
+I<DescriptiveStatisticsBasic> includes these functions: I<Count, Maximum, Minimum, Mean,
+Median, Sum, StandardDeviation, StandardError, Variance>.
+
+I<DescriptiveStatisticsAll>, in addition to  I<DescriptiveStatisticsBasic> functions, includes:
+I<GeometricMean, Frequency, HarmonicMean, KLargest, KSmallest, Kurtosis, Mode, RSquare,
+Skewness, TrimMean>.
+
+I<All> uses complete list of supported functions: I<Average, AverageDeviation, Correlation,
+Count, Covariance, GeometricMean, Frequency, HarmonicMean, KLargest, KSmallest, Kurtosis,
+Maximum, Minimum, Mean, Median, Mode, RSquare, Skewness, Sum,
+SumOfSquares, StandardDeviation, StandardDeviationN, StandardError, StandardScores,
+StandardScoresN, TrimMean, Variance, VarianceN>. The function names ending with N
+calculate corresponding values assuming an entire population instead of a population sample.
+Here are the formulas for these functions:
+
+Average: See Mean
+
+AverageDeviation: SUM( ABS(x[i] - Xmean) ) / n
+
+Correlation: See Pearson Correlation
+
+Covariance: SUM( (x[i] - Xmean)(y[i] - Ymean) ) / n
+
+GeometricMean: NthROOT( PRODUCT(x[i]) )
+
+HarmonicMean: 1 / ( SUM(1/x[i]) / n )
+
+Mean: SUM( x[i] ) / n
+
+Median: Xsorted[(n - 1)/2 + 1] for even values of n; (Xsorted[n/2] + Xsorted[n/2 + 1])/2
+for odd values of n.
+
+Kurtosis: [ {n(n + 1)/(n - 1)(n - 2)(n - 3)}  SUM{ ((x[i] - Xmean)/STDDEV)^4 } ] -
+{3((n - 1)^2)}/{(n - 2)(n-3)}
+
+PearsonCorrelation: SUM( (x[i] - Xmean)(y[i] - Ymean) ) / SQRT( SUM( (x[i] - Xmean)^2 )
+(SUM( (y[i] - Ymean)^2 ))   )
+
+RSquare: PearsonCorrelation^2
+
+Skewness: {n/(n - 1)(n - 2)} SUM{ ((x[i] - Xmean)/STDDEV)^3 }
+
+StandardDeviation: SQRT ( SUM( (x[i] - Mean)^2 ) / (n - 1) )
+
+StandardDeviationN: SQRT ( SUM( (x[i] - Mean)^2 ) / n )
+
+StandardError: StandardDeviation / SQRT( n )
+
+StandardScore: (x[i] - Mean) / (n - 1)
+
+StandardScoreN: (x[i] - Mean) / n
+
+Variance: SUM( (x[i] - Xmean)^2  / (n - 1) )
+
+VarianceN: SUM( (x[i] - Xmean)^2  / n )
+
+=item B<-o, --overwrite>
+
+Overwrite existing files.
+
+=item B<--outdelim> I<comma | tab | semicolon>
+
+Output text file delimiter. Possible values: I<comma, tab, or semicolon>
+Default value: I<comma>.
+
+=item B<-p, --precision> I<number>
+
+Precision of calculated values in the output file. Default: up to I<2> decimal places.
+Valid values: positive integers.
+
+=item B<-q, --quote> I<yes | no>
+
+Put quotes around column values in output text file. Possible values: I<yes or
+no>. Default value: I<yes>.
+
+=item B<-r, --root> I<rootname>
+
+New text file name is generated using the root: <Root>.<Ext>. Default new file
+name: <InitialSDFileName><Mode>.<Ext>. Based on the specified analysis,
+<Mode> corresponds to one of these values: DescriptiveStatisticsBasic,
+DescriptiveStatisticsAll, AllStatistics, SpecifiedStatistics, Covariance, Correlation,
+Frequency, or StandardScores. The csv, and tsv <Ext> values are used for
+comma/semicolon, and tab delimited text files respectively. This option is ignored for
+multiple input files.
+
+=item B<--trimfraction> I<number>
+
+Fraction of data to exclude from the top and bottom of the data set during
+I<TrimMean> calculation. Default value: I<0.1> Valid values: > 0 and < 1.
+
+=item B<-w --workingdir> I<text>
+
+Location of working directory. Default: current directory.
+
+=back
+
+=head1 EXAMPLES
+
+To calculate basic statistics for data in all common data fields and generate a
+NewSample1DescriptiveStatisticsBasic.csv file, type:
+
+    % AnalyzeSDFilesData.pl -o -r NewSample1 Sample1.sdf
+
+To calculate basic statistics for MolWeight data field and generate a
+NewSample1DescriptiveStatisticsBasic.csv file, type:
+
+    % AnalyzeSDFilesData.pl --datafields MolWeight -o -r NewSample1
+    Sample1.sdf
+
+To calculate all available statistics for MolWeight data field and all data field pairs,
+and generate NewSample1DescriptiveStatisticsAll.csv, NewSample1CorrelationMatrix.csv,
+NewSample1CorrelationMatrix.csv, and NewSample1MolWeightFrequencyAnalysis.csv
+files, type:
+
+    % AnalyzeSDFilesData.pl -m DescriptiveStatisticsAll --datafields
+    MolWeight -o --datafieldpairs AllPairs -r NewSample1 Sample1.sdf
+
+To compute frequency distribution of MolWeight data field into five bins and
+generate NewSample1MolWeightFrequencyAnalysis.csv, type:
+
+    % AnalyzeSDFilesData.pl -m Frequency --frequencybins 5 --datafields
+    MolWeight -o -r NewSample1 Sample1.sdf
+
+To compute frequency distribution of data in MolWeight data field into specified bin range
+values, and generate NewSample1MolWeightFrequencyAnalysis.csv, type:
+
+    % AnalyzeSDFilesData.pl -m Frequency --frequencybins "100,200,400"
+    --datafields MolWeight -o -r NewSample1 Sample1.sdf
+
+To calculate all available statistics for data in all data fields and pairs, type:
+
+    % AnalyzeSDFilesData.pl -m All --datafields  All --datafieldpairs
+    AllPairs -o -r NewSample1 Sample1.sdf
+
+=head1 AUTHOR
+
+Manish Sud <msud@san.rr.com>
+
+=head1 SEE ALSO
+
+FilterSDFiles.pl, InfoSDFiles.pl, SplitSDFiles.pl, MergeTextFilesWithSD.pl
+
+=head1 COPYRIGHT
+
+Copyright (C) 2015 Manish Sud. All rights reserved.
+
+This file is part of MayaChemTools.
+
+MayaChemTools is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free
+Software Foundation; either version 3 of the License, or (at your option)
+any later version.
+
+=cut