コード例 #1
0
        /// <summary>
        /// Converts a DataTable of events to a datatable where one row = one minute of indices
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public DataTable ConvertEvents2Indices(DataTable dt, TimeSpan unitTime, TimeSpan sourceDuration, double scoreThreshold)
        {
            double units     = sourceDuration.TotalSeconds / unitTime.TotalSeconds;
            int    unitCount = (int)(units / 1); //get whole minutes

            if (units % 1 > 0.0)
            {
                unitCount += 1;                           //add fractional minute
            }
            int[] eventsPerUnitTime = new int[unitCount]; //to store event counts
            int[] bigEvsPerUnitTime = new int[unitCount]; //to store counts of high scoring events

            foreach (DataRow ev in dt.Rows)
            {
                double eventStart = (double)ev[AnalysisKeys.EventStartSec];
                double eventScore = (double)ev[AnalysisKeys.EventNormscore];
                int    timeUnit   = (int)(eventStart / unitTime.TotalSeconds);
                eventsPerUnitTime[timeUnit]++;
                if (eventScore > scoreThreshold)
                {
                    bigEvsPerUnitTime[timeUnit]++;
                }
            }

            string[] headers  = { AnalysisKeys.KeyStartMinute, AnalysisKeys.EventTotal, ("#Ev>" + scoreThreshold) };
            Type[]   types    = { typeof(int), typeof(int), typeof(int) };
            var      newtable = DataTableTools.CreateTable(headers, types);

            for (int i = 0; i < eventsPerUnitTime.Length; i++)
            {
                int unitID = (int)(i * unitTime.TotalMinutes);
                newtable.Rows.Add(unitID, eventsPerUnitTime[i], bigEvsPerUnitTime[i]);
            }
            return(newtable);
        }
コード例 #2
0
        } //DrawSonogram()

        public static DataTable WriteEvents2DataTable(double segmentStartMinute, TimeSpan tsSegmentDuration, List <AcousticEvent> predictedEvents)
        {
            if ((predictedEvents == null) || (predictedEvents.Count == 0))
            {
                return(null);
            }

            GetTableHeadersAndTypesForEvents();
            var dataTable = DataTableTools.CreateTable(EVENT_HEADERS, EVENT_COL_TYPES);

            //int count = 0;
            foreach (var ev in predictedEvents)
            {
                int    segmentStartSec       = (int)(segmentStartMinute * 60);
                int    eventStartAbsoluteSec = (int)(segmentStartSec + ev.TimeStart);
                int    eventStartMin         = eventStartAbsoluteSec / 60;
                int    eventStartSec         = eventStartAbsoluteSec % 60;
                string segmentDuration       = DataTools.Time_ConvertSecs2Mins(tsSegmentDuration.TotalSeconds);

                DataRow row = dataTable.NewRow();
                row[key_START_ABS]        = eventStartAbsoluteSec;          //EvStartAbsolute - from start of source ifle
                row[key_START_MIN]        = eventStartMin;                  //EvStartMin
                row[key_START_SEC]        = eventStartSec;                  //EvStartSec
                row[key_SEGMENT_DURATION] = tsSegmentDuration.TotalSeconds; //segment Duration in seconds
                row[key_CALL_DENSITY]     = predictedEvents.Count;          //Density
                row[key_CALL_SCORE]       = ev.Score;                       //Score
                dataTable.Rows.Add(row);
                // count++;
            }
            return(dataTable);
        }
コード例 #3
0
        } //DrawSonogram()

        public static DataTable WriteEvents2DataTable(List <AcousticEvent> predictedEvents)
        {
            if (predictedEvents == null)
            {
                return(null);
            }
            string[] headers = { AudioAnalysisTools.Keys.EVENT_COUNT,
                                 AudioAnalysisTools.Keys.EVENT_START_MIN,
                                 AudioAnalysisTools.Keys.EVENT_START_SEC,
                                 AudioAnalysisTools.Keys.EVENT_START_ABS,
                                 AudioAnalysisTools.Keys.SEGMENT_TIMESPAN,
                                 AudioAnalysisTools.Keys.EVENT_DURATION,
                                 AudioAnalysisTools.Keys.EVENT_INTENSITY,
                                 AudioAnalysisTools.Keys.EVENT_NAME,
                                 LSKiwiHelper.key_BANDWIDTH_SCORE,
                                 LSKiwiHelper.key_DELTA_SCORE,
                                 LSKiwiHelper.key_PEAKS_SNR_SCORE,
                                 LSKiwiHelper.key_PEAKS_STD_SCORE,
                                 AudioAnalysisTools.Keys.EVENT_SCORE,
                                 AudioAnalysisTools.Keys.EVENT_NORMSCORE };
            //                   1                2               3              4                5              6               7              8
            Type[] types = { typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(string),
                             typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double) };

            var dataTable = DataTableTools.CreateTable(headers, types);

            if (predictedEvents.Count == 0)
            {
                return(dataTable);
            }

            foreach (var ev in predictedEvents)
            {
                DataRow row = dataTable.NewRow();
                row[AudioAnalysisTools.Keys.EVENT_START_ABS] = (double)ev.TimeStart;           //Set now - will overwrite later
                row[AudioAnalysisTools.Keys.EVENT_START_SEC] = (double)ev.TimeStart;           //EvStartSec
                row[AudioAnalysisTools.Keys.EVENT_DURATION]  = (double)ev.Duration;            //duratio in seconds
                row[AudioAnalysisTools.Keys.EVENT_INTENSITY] = (double)ev.kiwi_intensityScore; //
                row[AudioAnalysisTools.Keys.EVENT_NAME]      = (string)ev.Name;                //
                row[LSKiwiHelper.key_BANDWIDTH_SCORE]        = (double)ev.kiwi_bandWidthScore;
                row[LSKiwiHelper.key_DELTA_SCORE]            = (double)ev.kiwi_deltaPeriodScore;
                row[LSKiwiHelper.key_PEAKS_SNR_SCORE]        = (double)ev.kiwi_snrScore;
                row[LSKiwiHelper.key_PEAKS_STD_SCORE]        = (double)ev.kiwi_sdPeakScore;
                row[AudioAnalysisTools.Keys.EVENT_NORMSCORE] = (double)ev.ScoreNormalised;
                row[AudioAnalysisTools.Keys.EVENT_SCORE]     = (double)ev.Score;  //Score
                dataTable.Rows.Add(row);
            }
            return(dataTable);
        }
コード例 #4
0
        } //DrawSonogram()

        public static DataTable WriteEvents2DataTable(List <AcousticEvent> predictedEvents)
        {
            if (predictedEvents == null)
            {
                return(null);
            }
            string[] headers = { AnalysisKeys.EventCount,         //1
                                 AnalysisKeys.EventStartMin,      //2
                                 AnalysisKeys.EventStartSec,      //3
                                 AnalysisKeys.EventStartAbs,      //4
                                 AnalysisKeys.KeySegmentDuration, //5
                                 AnalysisKeys.EventDuration,      //6
                                                                  //AudioAnalysisTools.Keys.EVENT_INTENSITY,
                                 AnalysisKeys.EventName,          //7
                                 AnalysisKeys.DominantFrequency,
                                 AnalysisKeys.OscillationRate,
                                 AnalysisKeys.EventScore,
                                 AnalysisKeys.EventNormscore, };
            //                   1                2               3              4                5              6               7              8
            Type[] types = { typeof(int),    typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(string), typeof(double),
                             typeof(double), typeof(double), typeof(double) };

            var dataTable = DataTableTools.CreateTable(headers, types);

            if (predictedEvents.Count == 0)
            {
                return(dataTable);
            }

            foreach (var ev in predictedEvents)
            {
                DataRow row = dataTable.NewRow();
                row[AnalysisKeys.EventStartAbs] = (double)ev.TimeStart;            //Set now - will overwrite later
                row[AnalysisKeys.EventStartSec] = (double)ev.TimeStart;            //EvStartSec
                row[AnalysisKeys.EventDuration] = (double)ev.EventDurationSeconds; //duration in seconds
                //row[AudioAnalysisTools.Keys.EVENT_INTENSITY] = (double)ev.kiwi_intensityScore;   //
                row[AnalysisKeys.EventName]         = (string)ev.Name;             //
                row[AnalysisKeys.DominantFrequency] = (double)ev.DominantFreq;
                row[AnalysisKeys.OscillationRate]   = 1 / (double)ev.Periodicity;
                row[AnalysisKeys.EventScore]        = (double)ev.Score; //Score
                row[AnalysisKeys.EventNormscore]    = (double)ev.ScoreNormalised;
                dataTable.Rows.Add(row);
            }
            return(dataTable);
        }
コード例 #5
0
        } // ProcessCsvFile()

        /// <summary>
        /// takes a data table of indices and normalises column values to values in [0,1].
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static DataTable NormaliseColumnsOfDataTable(DataTable dt)
        {
            string[] headers    = DataTableTools.GetColumnNames(dt);
            string[] newHeaders = new string[headers.Length];

            List <double[]> newColumns = new List <double[]>();

            for (int i = 0; i < headers.Length; i++)
            {
                double[] values = DataTableTools.Column2ArrayOfDouble(dt, headers[i]); //get list of values
                if ((values == null) || (values.Length == 0))
                {
                    continue;
                }

                double min = 0;
                double max = 1;
                if (headers[i].Equals(Keys.AV_AMPLITUDE))
                {
                    min = -50;
                    max = -5;
                    newColumns.Add(DataTools.NormaliseInZeroOne(values, min, max));
                    newHeaders[i] = headers[i] + "  (-50..-5dB)";
                }
                else //default is to normalise in [0,1]
                {
                    newColumns.Add(DataTools.normalise(values)); //normalise all values in [0,1]
                    newHeaders[i] = headers[i];
                }
            } //for loop

            //convert type int to type double due to normalisation
            Type[] types = new Type[newHeaders.Length];
            for (int i = 0; i < newHeaders.Length; i++)
            {
                types[i] = typeof(double);
            }
            var processedtable = DataTableTools.CreateTable(newHeaders, types, newColumns);

            return(processedtable);
        }
コード例 #6
0
        } //DrawSonogram()

        public static DataTable WriteEvents2DataTable(List <AcousticEvent> predictedEvents)
        {
            if (predictedEvents == null)
            {
                return(null);
            }
            string[] headers = { AnalysisKeys.EventCount,
                                 AnalysisKeys.EventStartMin,
                                 AnalysisKeys.EventStartSec,
                                 AnalysisKeys.EventStartAbs,
                                 AnalysisKeys.KeySegmentDuration,
                                 AnalysisKeys.EventDuration,
                                 AnalysisKeys.EventIntensity,
                                 AnalysisKeys.EventName,
                                 AnalysisKeys.EventScore,
                                 AnalysisKeys.EventNormscore, };
            //                   1                2               3              4                5              6               7              8
            Type[] types = { typeof(int),    typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(string),
                             typeof(double), typeof(double) };

            var dataTable = DataTableTools.CreateTable(headers, types);

            if (predictedEvents.Count == 0)
            {
                return(dataTable);
            }

            foreach (var ev in predictedEvents)
            {
                DataRow row = dataTable.NewRow();
                row[AnalysisKeys.EventStartAbs]  = (double)ev.TimeStart;            //Set now - will overwrite later
                row[AnalysisKeys.EventStartSec]  = (double)ev.TimeStart;            //EvStartSec
                row[AnalysisKeys.EventDuration]  = (double)ev.EventDurationSeconds; //duratio in seconds
                row[AnalysisKeys.EventIntensity] = (double)ev.kiwi_intensityScore;  //
                row[AnalysisKeys.EventName]      = (string)ev.Name;                 //
                row[AnalysisKeys.EventNormscore] = (double)ev.ScoreNormalised;
                row[AnalysisKeys.EventScore]     = (double)ev.Score;                //Score
                dataTable.Rows.Add(row);
            }
            return(dataTable);
        }
コード例 #7
0
        } //AddContext2Table()

        public Tuple <DataTable, DataTable> ProcessCsvFile(FileInfo fiCsvFile, FileInfo fiConfigFile)
        {
            DataTable dt = CsvTools.ReadCSVToTable(fiCsvFile.FullName, true); //get original data table

            if ((dt == null) || (dt.Rows.Count == 0))
            {
                return(null);
            }
            //get its column headers
            var dtHeaders = new List <string>();
            var dtTypes   = new List <Type>();

            foreach (DataColumn col in dt.Columns)
            {
                dtHeaders.Add(col.ColumnName);
                dtTypes.Add(col.DataType);
            }

            List <string> displayHeaders = null;

            //check if config file contains list of display headers
            if (fiConfigFile != null)
            {
                var configuration = new ConfigDictionary(fiConfigFile.FullName);
                Dictionary <string, string> configDict = configuration.GetTable();
                if (configDict.ContainsKey(Keys.DISPLAY_COLUMNS))
                {
                    displayHeaders = configDict[Keys.DISPLAY_COLUMNS].Split(',').ToList();
                }
            }
            //if config file does not exist or does not contain display headers then use the original headers
            if (displayHeaders == null)
            {
                displayHeaders = dtHeaders;                         //use existing headers if user supplies none.
            }
            //now determine how to display tracks in display datatable
            Type[] displayTypes = new Type[displayHeaders.Count];
            bool[] canDisplay   = new bool[displayHeaders.Count];
            for (int i = 0; i < displayTypes.Length; i++)
            {
                displayTypes[i] = typeof(double);
                canDisplay[i]   = false;
                if (dtHeaders.Contains(displayHeaders[i]))
                {
                    canDisplay[i] = true;
                }
            }

            DataTable table2Display = DataTableTools.CreateTable(displayHeaders.ToArray(), displayTypes);

            foreach (DataRow row in dt.Rows)
            {
                DataRow newRow = table2Display.NewRow();
                for (int i = 0; i < canDisplay.Length; i++)
                {
                    if (canDisplay[i])
                    {
                        newRow[displayHeaders[i]] = row[displayHeaders[i]];
                    }
                    else
                    {
                        newRow[displayHeaders[i]] = 0.0;
                    }
                }
                table2Display.Rows.Add(newRow);
            }

            //order the table if possible
            if (dt.Columns.Contains(AudioAnalysisTools.Keys.EVENT_START_ABS))
            {
                dt = DataTableTools.SortTable(dt, AudioAnalysisTools.Keys.EVENT_START_ABS + " ASC");
            }
            else if (dt.Columns.Contains(AudioAnalysisTools.Keys.EVENT_COUNT))
            {
                dt = DataTableTools.SortTable(dt, AudioAnalysisTools.Keys.EVENT_COUNT + " ASC");
            }
            else if (dt.Columns.Contains(AudioAnalysisTools.Keys.INDICES_COUNT))
            {
                dt = DataTableTools.SortTable(dt, AudioAnalysisTools.Keys.INDICES_COUNT + " ASC");
            }
            else if (dt.Columns.Contains(AudioAnalysisTools.Keys.START_MIN))
            {
                dt = DataTableTools.SortTable(dt, AudioAnalysisTools.Keys.START_MIN + " ASC");
            }

            table2Display = NormaliseColumnsOfDataTable(table2Display);
            return(System.Tuple.Create(dt, table2Display));
        } // ProcessCsvFile()
コード例 #8
0
        public Tuple <DataTable, DataTable> ProcessCsvFile(FileInfo fiCsvFile, FileInfo fiConfigFile)
        {
            DataTable dt = CsvTools.ReadCSVToTable(fiCsvFile.FullName, true); //get original data table

            if ((dt == null) || (dt.Rows.Count == 0))
            {
                return(null);
            }
            //get its column headers
            var dtHeaders = new List <string>();
            var dtTypes   = new List <Type>();

            foreach (DataColumn col in dt.Columns)
            {
                dtHeaders.Add(col.ColumnName);
                dtTypes.Add(col.DataType);
            }

            List <string> displayHeaders = null;

            //check if config file contains list of display headers
            if (fiConfigFile != null)
            {
                var configuration = new ConfigDictionary(fiConfigFile.FullName);
                Dictionary <string, string> configDict = configuration.GetTable();
                if (configDict.ContainsKey(AnalysisKeys.DisplayColumns))
                {
                    displayHeaders = configDict[AnalysisKeys.DisplayColumns].Split(',').ToList();
                }
            }
            //if config file does not exist or does not contain display headers then use the original headers
            if (displayHeaders == null)
            {
                displayHeaders = dtHeaders;                         //use existing headers if user supplies none.
            }
            //now determine how to display tracks in display datatable
            Type[] displayTypes = new Type[displayHeaders.Count];
            bool[] canDisplay   = new bool[displayHeaders.Count];
            for (int i = 0; i < displayTypes.Length; i++)
            {
                displayTypes[i] = typeof(double);
                canDisplay[i]   = false;
                if (dtHeaders.Contains(displayHeaders[i]))
                {
                    canDisplay[i] = true;
                }
            }

            DataTable table2Display = DataTableTools.CreateTable(displayHeaders.ToArray(), displayTypes);

            foreach (DataRow row in dt.Rows)
            {
                DataRow newRow = table2Display.NewRow();
                for (int i = 0; i < canDisplay.Length; i++)
                {
                    if (canDisplay[i])
                    {
                        newRow[displayHeaders[i]] = row[displayHeaders[i]];
                    }
                    else
                    {
                        newRow[displayHeaders[i]] = 0.0;
                    }
                }
                table2Display.Rows.Add(newRow);
            }

            //order the table if possible
            if (dt.Columns.Contains(AnalysisKeys.EventStartAbs))
            {
                dt = DataTableTools.SortTable(dt, AnalysisKeys.EventStartAbs + " ASC");
            }
            else if (dt.Columns.Contains(AnalysisKeys.EventCount))
            {
                dt = DataTableTools.SortTable(dt, AnalysisKeys.EventCount + " ASC");
            }
            else if (dt.Columns.Contains(AnalysisKeys.KeyRankOrder))
            {
                dt = DataTableTools.SortTable(dt, AnalysisKeys.KeyRankOrder + " ASC");
            }
            else if (dt.Columns.Contains(AnalysisKeys.KeyStartMinute))
            {
                dt = DataTableTools.SortTable(dt, AnalysisKeys.KeyStartMinute + " ASC");
            }

            //this depracted now that use class indexProperties to do normalisation
            //table2Display = NormaliseColumnsOfDataTable(table2Display);

            //add in column of weighted indices
            //bool addColumnOfweightedIndices = true;
            //if (addColumnOfweightedIndices)
            //{
            //    double[] comboWts = IndexCalculate.CalculateComboWeights();
            //    double[] weightedIndices = IndexCalculate.GetArrayOfWeightedAcousticIndices(dt, comboWts);
            //    string colName = "WeightedIndex";
            //    DataTableTools.AddColumnOfDoubles2Table(table2Display, colName, weightedIndices);
            //}
            return(Tuple.Create(dt, table2Display));
        } // ProcessCsvFile()
コード例 #9
0
        public static DataTable CalculateRecallPrecision(FileInfo fiPredictions, FileInfo fiGroundTruth)
        {
            string header_trueSex      = "truSex";
            string header_predictedSex = "preSex";
            string header_Harmonics    = "Harmonics";
            string header_Quality      = "Quality";

            string[] ROC_HEADERS =
            {
                AnalysisKeys.EventStartAbs,      //typeof(double)
                AnalysisKeys.EventStartMin,
                AnalysisKeys.EventStartSec,
                AnalysisKeys.EventIntensity,
                LSKiwiHelper.key_GRID_SCORE,
                LSKiwiHelper.key_DELTA_SCORE,
                LSKiwiHelper.key_CHIRP_SCORE,
                LSKiwiHelper.key_PEAKS_SNR_SCORE,
                LSKiwiHelper.key_BANDWIDTH_SCORE,
                AnalysisKeys.EventScore,
                AnalysisKeys.EventNormscore,
                header_predictedSex,
                header_Harmonics,
                header_trueSex,
                header_Quality,
                "TP",                            "FP","FN",
            };

            //string[] ROC_HEADERS = { "startSec",   "min",         "secOffset",  "intensity",     "gridScore",    "deltaScore",  "chirpScore",      "PeaksSnrScore"  "bwScore",      "comboScore",   "normScore",     "preSex",     "Harmonics",   "truSex",      "Quality",    "TP",       "FP",        "FN"};
            Type[] ROC_COL_TYPES = { typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(double), typeof(string), typeof(int), typeof(string), typeof(int), typeof(int), typeof(int), typeof(int) };

            //ANDREW'S HEADERS:          Selection,        View,     Channel,     Begin Time (s),  End Time (s),  Low Freq (Hz),  High Freq (Hz),   Begin File,    Species,          Sex,         Harmonics,    Quality
            Type[] ANDREWS_TYPES = { typeof(string), typeof(string), typeof(int), typeof(double), typeof(double), typeof(double), typeof(double), typeof(string), typeof(string), typeof(string), typeof(int), typeof(int) };

            bool isFirstRowHeader = true;
            var  dtGroundTruth    = CsvTools.ReadCSVToTable(fiGroundTruth.FullName, isFirstRowHeader, ANDREWS_TYPES);
            var  dtPredictions    = CsvTools.ReadCSVToTable(fiPredictions.FullName, isFirstRowHeader);

            dtPredictions = LSKiwiHelper.MergeAdjacentPredictions(dtPredictions);

            //var weights = LSKiwiHelper.GetFeatureWeights(); //to try different weightings.

            //string colName  = "Species";
            //string value    = "LSK";
            //DataTableTools.DeleteRows(dtADResults, colName, value); //delete rows where Species name is not "LSK"
            var dtOutput = DataTableTools.CreateTable(ROC_HEADERS, ROC_COL_TYPES);
            int TP       = 0;
            int FP       = 0;
            int FN       = 0;

            foreach (DataRow myRow in dtPredictions.Rows)
            {
                double myStartSecAbs  = (double)myRow[AnalysisKeys.EventStartAbs];
                double startMin       = (double)myRow[AnalysisKeys.EventStartMin];
                double startSecOffset = (double)myRow[AnalysisKeys.EventStartSec];
                double intensityScore = (double)myRow[AnalysisKeys.EventIntensity];
                string name           = (string)myRow[AnalysisKeys.EventName];

                //double snrScore = (double)myRow[LSKiwiHelper.key_PEAKS_SNR_SCORE];
                //double sdPeakScore = (double)myRow[LSKiwiHelper.key_PEAKS_STD_SCORE]; //standard deviation of peak snr's
                //double periodicityScore = (double)myRow[LSKiwiHelper.key_DELTA_SCORE];
                double gridScore      = (double)myRow[LSKiwiHelper.key_GRID_SCORE];
                double deltScore      = (double)myRow[LSKiwiHelper.key_DELTA_SCORE];
                double chrpScore      = (double)myRow[LSKiwiHelper.key_CHIRP_SCORE];
                double peakSnrScore   = (double)myRow[LSKiwiHelper.key_PEAKS_SNR_SCORE]; //average peak
                double bandWidthScore = (double)myRow[LSKiwiHelper.key_BANDWIDTH_SCORE];

                //double comboScore   = (double)myRow[LSKiwiHelper.key_COMBO_SCORE];
                double eventScore = (double)myRow[AnalysisKeys.EventScore];
                double normScore  = (double)myRow[AnalysisKeys.EventNormscore];

                string predictedSex;
                if (name.EndsWith("(m)"))
                {
                    predictedSex = "M";
                }
                else if (name.EndsWith("(f)"))
                {
                    predictedSex = "F";
                }
                else
                {
                    predictedSex = "???";
                }

                //List<string[]> excludeRules = LSKiwiHelper.GetExcludeRules();
                //if (FilterEvent(myRow, excludeRules) == null) continue;

                DataRow opRow = dtOutput.NewRow();
                opRow[AnalysisKeys.EventStartAbs]       = myStartSecAbs;
                opRow[AnalysisKeys.EventStartMin]       = startMin;
                opRow[AnalysisKeys.EventStartSec]       = startSecOffset;
                opRow[AnalysisKeys.EventIntensity]      = intensityScore;
                opRow[LSKiwiHelper.key_GRID_SCORE]      = gridScore;
                opRow[LSKiwiHelper.key_DELTA_SCORE]     = deltScore;
                opRow[LSKiwiHelper.key_CHIRP_SCORE]     = chrpScore;
                opRow[LSKiwiHelper.key_PEAKS_SNR_SCORE] = peakSnrScore;
                opRow[LSKiwiHelper.key_BANDWIDTH_SCORE] = bandWidthScore;

                //opRow[LSKiwiHelper.key_COMBO_SCORE]     = comboScore;
                opRow[AnalysisKeys.EventScore]     = eventScore;
                opRow[AnalysisKeys.EventNormscore] = normScore;
                opRow[header_Quality]      = 0; //fill in with blanks
                opRow[header_predictedSex] = predictedSex;
                opRow[header_trueSex]      = "???";
                opRow["TP"] = 0;
                opRow["FP"] = 0;
                opRow["FN"] = 0;

                bool isTP = false;
                foreach (DataRow trueEvent in dtGroundTruth.Rows)
                {
                    double trueStart = (double)trueEvent["Begin Time (s)"];
                    string trueSex   = (string)trueEvent["Sex"];
                    if (trueStart >= myStartSecAbs - 10 && trueStart <= myStartSecAbs + 20 && predictedSex == trueSex) //myStart is close to trueStart AND same sex THERFORE TRUE POSTIIVE
                    {
                        isTP = true;
                        trueEvent["Begin Time (s)"] = double.NaN; //mark so that will not use again
                        opRow[header_Quality]       = trueEvent[header_Quality];
                        opRow[header_trueSex]       = trueEvent["Sex"];
                        opRow[header_Harmonics]     = trueEvent[header_Harmonics];
                        break;
                    }
                } //foreach - AD loop

                if (isTP)
                {
                    opRow["TP"] = 1;
                    TP++;
                }
                else //FALSE POSITIVE
                {
                    opRow["FP"] = 1;
                    FP++;
                }

                dtOutput.Rows.Add(opRow);
            } //foreach - MY loop

            //now add in the false negatives
            foreach (DataRow trueEvent in dtGroundTruth.Rows)
            {
                double trueStart = (double)trueEvent["Begin Time (s)"];
                if (!double.IsNaN(trueStart))
                {
                    DataRow row = dtOutput.NewRow();
                    row[AnalysisKeys.EventStartAbs] = trueStart;
                    row[AnalysisKeys.EventStartMin] = (int)(trueStart / 60);
                    row[AnalysisKeys.EventStartSec] = trueStart % 60;

                    //row[Keys.EVENT_INTENSITY] = 0.0;
                    //row[LSKiwiHelper.key_PEAKS_SNR_SCORE] = 0.0;
                    //row[LSKiwiHelper.key_PEAKS_STD_SCORE] = 0.0;
                    //row[LSKiwiHelper.key_DELTA_SCORE]     = 0.0;
                    //row[LSKiwiHelper.key_BANDWIDTH_SCORE] = 0.0;
                    //row[Keys.EVENT_NORMSCORE]             = 0.0;
                    //row[LSKiwiHelper.key_NEW_COMBO_SCORE] = 0.0;
                    row[header_predictedSex] = "???";
                    row["Harmonics"]         = trueEvent["Harmonics"];
                    row["Quality"]           = trueEvent["Quality"];
                    row[header_trueSex]      = trueEvent["Sex"];
                    row["TP"] = 0;
                    row["FP"] = 0;
                    row["FN"] = 1;
                    dtOutput.Rows.Add(row);
                    FN++;
                }
            }

            double recall      = TP / (double)(TP + FN);
            double specificity = TP / (double)(TP + FP);

            LoggedConsole.WriteLine("TP={0},  FP={1},  FN={2}", TP, FP, FN);
            LoggedConsole.WriteLine("RECALL={0:f3},  SPECIFICITY={1:f3}", recall, specificity);

            //use normalised score as the threshold to determine area under ROC curve
            int    totalPositiveCount = dtGroundTruth.Rows.Count;
            int    totalNegativeCount = FP;
            string sortString         = AnalysisKeys.EventNormscore + " desc";

            ROCCurve(dtOutput, totalPositiveCount, totalNegativeCount, sortString); //write ROC area above curve

            return(dtOutput);
        } //CalculateRecallPrecision()