/// <summary> /// Instantiates an initialized instance. /// Supports data resampling (including simple detection of signal begin/end) and amplitude unification. /// </summary> /// <param name="inputData">1D array containing the pattern input data</param> /// <param name="numOfVariables">Number of pattern variables</param> /// <param name="variablesSchema">Schema of variables organization in the 1D input pattern</param> /// <param name="detrend">Specifies whether to remove trend from the variables' data</param> /// <param name="unifyAmplitudes">Specifies whether to unify amplitude of variable's data over the time dimension</param> /// <param name="signalBeginThreshold">If specified (GT 0), signal begin will be decided at timepoint Tx where (abs(s(Tx) - s(T0)) / s(max) - s(min)) >= given threshold (x in order 0..last)</param> /// <param name="signalEndThreshold">If specified (GT 0), signal end will be decided at timepoint Tx where (abs(s(Tx) - s(T last)) / s(max) - s(min)) >= given threshold (x in order last..0)</param> /// <param name="uniformTimeScale">If false then each variable will have its own time dimension</param> /// <param name="targetTimePoints">If specified, resulting parttern variable's data will be upsampled and/or downsampled to have specified fixed length (time points)</param> public InputPattern(double[] inputData, int numOfVariables, VariablesSchema variablesSchema, bool detrend = false, bool unifyAmplitudes = false, double signalBeginThreshold = 0d, double signalEndThreshold = 0d, bool uniformTimeScale = true, int targetTimePoints = -1 ) : this(numOfVariables) { List <double[]> patternRawData = PatternDataFromArray(inputData, 0, inputData.Length, numOfVariables, variablesSchema); int rawTimePoints = patternRawData[0].Length; //Remove trend? if (detrend) { //Trend removal -> convert data to differences for (int varIdx = 0; varIdx < numOfVariables; varIdx++) { double[] detrended = new double[patternRawData[varIdx].Length]; detrended[0] = 0; for (int i = 1; i < patternRawData[varIdx].Length; i++) { detrended[i] = patternRawData[varIdx][i] - patternRawData[varIdx][i - 1]; } patternRawData[varIdx] = detrended; } } //Initially set begin and end signal indexes to full range int[] signalBeginIdxs = new int[numOfVariables]; signalBeginIdxs.Populate(0); int[] signalEndIdxs = new int[numOfVariables]; signalEndIdxs.Populate(rawTimePoints - 1); //Detection of signal begin? if (signalBeginThreshold > 0d) { int minSignalBeginIdx = -1; for (int varIdx = 0; varIdx < numOfVariables; varIdx++) { signalBeginIdxs[varIdx] = DetectSignalBegin(patternRawData[varIdx], signalBeginThreshold); if (minSignalBeginIdx == -1 || minSignalBeginIdx > signalBeginIdxs[varIdx]) { minSignalBeginIdx = signalBeginIdxs[varIdx]; } } if (uniformTimeScale) { signalBeginIdxs.Populate(minSignalBeginIdx); } } //Detection of signal end? if (signalEndThreshold > 0d) { int maxSignalEndIdx = -1; for (int varIdx = 0; varIdx < numOfVariables; varIdx++) { signalEndIdxs[varIdx] = DetectSignalEnd(patternRawData[varIdx], signalEndThreshold); if (maxSignalEndIdx == -1 || maxSignalEndIdx < signalEndIdxs[varIdx]) { maxSignalEndIdx = signalEndIdxs[varIdx]; } } if (uniformTimeScale) { signalEndIdxs.Populate(maxSignalEndIdx); } } //Correct begin/end indexes for (int varIdx = 0; varIdx < numOfVariables; varIdx++) { if (signalEndIdxs[varIdx] <= signalBeginIdxs[varIdx]) { signalBeginIdxs[varIdx] = 0; signalEndIdxs[varIdx] = rawTimePoints - 1; } } //Resampling targetTimePoints = Math.Max(2, targetTimePoints == -1 ? rawTimePoints : targetTimePoints); for (int varIdx = 0; varIdx < numOfVariables; varIdx++) { int signalLength = (signalEndIdxs[varIdx] - signalBeginIdxs[varIdx]) + 1; if (signalLength != rawTimePoints || targetTimePoints != rawTimePoints) { //Perform resampling int lcm = Discrete.LCM(signalLength, targetTimePoints, out _); //Upsample double[] upsampledData = Upsample(patternRawData[varIdx], signalBeginIdxs[varIdx], signalEndIdxs[varIdx], lcm); //Downsample double[] downsampledData = Downsample(upsampledData, targetTimePoints); VariablesDataCollection.Add(downsampledData); } else { //No resampling is necessary so simply use the unchanged raw data double[] signalData = new double[signalLength]; for (int i = 0; i < signalLength; i++) { signalData[i] = patternRawData[varIdx][signalBeginIdxs[varIdx] + i]; } VariablesDataCollection.Add(signalData); } } //Unify amplitudes if (unifyAmplitudes) { UnifyAmplitudes(); } return; }
/// <summary> /// Extracts variables' data from 1D array /// </summary> /// <param name="inputData">1D array containing pattern input data</param> /// <param name="dataStartIndex">Specifies the zero-based starting index of pattern input data in the given 1D input data array</param> /// <param name="dataLength">Specifies the length of pattern input data in the given 1D input data array</param> /// <param name="numOfVariables">Number of pattern variables</param> /// <param name="varDataOrganization">Variables' time-order data organization in the given 1D input data array</param> public List <double[]> PatternDataFromArray(double[] inputData, int dataStartIndex, int dataLength, int numOfVariables, VariablesSchema varDataOrganization) { //Check data length if (dataLength < numOfVariables || (dataLength % numOfVariables) != 0) { throw new FormatException("Incorrect length of input data."); } //Pattern data int timePoints = dataLength / numOfVariables; List <double[]> patternData = new List <double[]>(numOfVariables); for (int i = 0; i < numOfVariables; i++) { patternData.Add(new double[timePoints]); } Parallel.For(0, timePoints, timeIdx => { for (int i = 0; i < numOfVariables; i++) { double varValue = varDataOrganization == VariablesSchema.Groupped ? inputData[dataStartIndex + timeIdx * numOfVariables + i] : inputData[dataStartIndex + i * timePoints + timeIdx]; patternData[i][timeIdx] = varValue; } });//timeIdx return(patternData); }