From a graph marquee, save a list of points, average, or NaN data

#pragma rtGlobals=3     // Use modern global access method and strict wave access.

// Does simple tasks on the data within a marquee -- either prints avg or sets nan
function doWithinMarquee([doWhat, quiet])
// Note: if no changes are made, nothing is printed.
// Joel Corbin, 2016-09-22

string doWhat   // == one of: "avg", "nan", "getPlist" (get str list of row indices for selection)
                // (see below for what these mean)
           
variable quiet  // if set, function doesn't print anything

if (ParamIsDefault(doWhat))
    doWhat="avg"
endif

variable printStuff=0
if (ParamIsDefault(quiet) || quiet)
    printStuff=1
endif

    String graphName= WinName(0,1) 
    String traceList=TraceNameList(graphName,";",1+4)   // 1+4 --> normal+visible traces
    variable i, n_traces= itemsInList(traceList)
    variable alertedAlready= 0
    variable n_traces_used= 0
   
    // search for & get traces for any subwindows. Expand graph names to match traces to allow this.
    string graphsList= strmultiply(graphName, n_traces) // empty if n=0
    string childWinList= ChildWindowList(graphName)
    variable j, n_childs= itemsInList(childWinList)
    for (j=0; j<n_childs; j+=1)
        // [this doesn't execute if n_childs==0]
        string nextSubGr= graphName + "#" + stringFromList(j, childWinList)     // ParentGraph#Subwindow format
        graphsList+= nextSubGr + ";"
        traceList+= TraceNameList(nextSubGr,";",1+4)                            // 1+4 --> normal+visible traces
    endfor
    n_traces= itemsInList(traceList)
   
    // for each trace...
    for (i=0; i<n_traces; i+=1)
        // prepare
        graphName= stringFromList(i, graphsList)
        string traceName= stringFromList(i, traceList)
        Wave/Z yw= TraceNameToWaveRef(graphName,traceName)
        Wave/Z xw= XWaveRefFromTrace(graphName,traceName)
        if (!WaveExists(yw))
            printf "\t//* Wave %s not found.\r", traceName
        endif
       
        // get axis and marquee properties
        String hAxis= StringByKey("XAXIS", TraceInfo(graphName, traceName, 0)) 
        String vAxis= StringByKey("YAXIS", TraceInfo(graphName, traceName, 0)) 
        GetMarquee /W=$graphName $hAxis, $vAxis // removed /K (kill marquee) for the sake of the points version
        Variable xMin= min(V_right, V_left)
        Variable xMax= max(V_right, V_left)
        Variable yMin= min(V_top, V_bottom)
        Variable yMax= max(V_top, V_bottom)
       
        // get mask of selected items [code here inspired by http://www.igorexchange.com/node/1140]
        make /free /n=(numpnts(yw)) mask
        if( WaveExists(xw)) // y vs x
            mask = (yw[p] > yMin) && (yw[p] < yMax) && (xw > xMin) && (xw < xMax) ? 1 : 0
        else                    // just a waveform, use X scaling
            mask = (yw[p] > yMin) && (yw[p] < yMax) && (pnt2x(yw,p) > xMin) && (pnt2x(yw,p) < xMax) ? 1 : 0
        endif
       
        // if trace is masked [some points not visible] the result is wrong.
        if (0)
            String maskName= StringByKey("mask(x)", TraceInfo(graphName, traceName, 0), "=", ";")
            // But to mask a trace, I do a hack using zmarkersize... so very hard to work with]
            print maskName // msizez ??
        endif
       
        strswitch (doWhat)
            case "avg":
                mask= yw*mask/mask
               
                // get stats
                WaveStats /Q mask
                if (V_npnts > 2)
                    StatsQuantiles /iNaN /Q mask
                else
                    V_Q25=nan; V_Median=nan; V_Q75=nan
                endif
               
                // print result
                if (xMin > date2secs(2000, 1, 1)) // assume time:
                    string S_range= secs2dt(xMin) + ", " + secs2dt(xMax)
                else
                    S_range= num2str(xMin) + ", " + num2str(xMax)
                endif
               
                if (V_npnts > 0)
                    if (printStuff)
                        printf "Wave %s(%s) {avg, SD, %%SD}\t= {%.4g, %.4g, %.1f %%};  {min, Q25, Q50, Q75, max} = {%.4g, %.4g, %.4g, %.4g, %.4g}\r", traceName, S_range, V_avg, V_sdev, V_sdev/V_avg*100, V_min, V_Q25, V_Median, V_Q75, V_Max
                    endif
                    n_traces_used+=1
                endif
                break
               
               
            case "nan":
                if (!alertedAlready)
                    alertedAlready=1
                    // warn
                    doAlert 1, "This will permanently remove data from the selected wave(s). Continue?"
                    if (V_flag==2)
                        abort "Abort -- no changes made. -- " + GetRTStackInfo(0)
                    endif
                endif
               
                WaveStats /Q yw
                variable V_nansBefore= V_numNaNs

                // do it
                mask= !mask / !mask     // maskOfKeepers[1,nan] <- maskOfMarqueeMembers[0,1]
                yw *= mask
               
                // print a report
                WaveStats /Q yw
                variable V_numDeleted= V_numNaNs - V_nansBefore
                if (V_numDeleted)
                    if (printStuff)
                        printf "\t//* %s deleted %g points from wave %s according to a marquee selection [%s].\r", GetRTStackInfo(1), V_numDeleted, getwavesdatafolder(yw,2), secs2dt(datetime)
                    endif
                    n_traces_used+=1
                endif
               
                GetMarquee /W=$graphName /K // kill marquee
                break
               
            case "getPlist":
                DFREF sdf= getDataFolderDFR()
                cd root:;   newdatafolder /o/s Packages; newdatafolder /o/s jTools
               
                variable V_noList= 0
                if (DimSize(xw, 1)>1 || DimSize(yw, 1)>1) // can't handle multidimensional inputs...
                    if (printStuff)
                        printf "\t//* doWithinMarquee() can't handle multidimensional inputs, returning nothing...\r"
                    endif
                    V_noList= 1
                    make /free /n=1 mask=nan
                endif
               
                // do it
                mask= mask/mask * p
                duplicate /free mask, mask_deletedNaNs
                deleteNaNs(mask_deletedNaNs)
                if (!numpnts(mask_deletedNaNs)) // no selection
                    cd sdf
                    continue
                endif
               
                // save result 1/2
                duplicate /o mask, root:Packages:jTools:MarqueeMask /WAVE=MarqueeMask
                n_traces_used+= 1
               
                if (n_traces_used > 1)
                    printf "// (Multiple traces used, not saving selected points in MarqueeP / MarqueeMask... (to fix, remove all other traces first)"
                    // there is no mechanism for choosing which trace to save yet              
                endif
               
                // save result 2/2
                duplicate /o mask_deletedNaNs, root:Packages:jTools:MarqueePs /WAVE=MarqueePs
                string wnote= "Saved from a marquee selection of wave %s \ron %s by doWithinMarquee(\"getPlist\")"
                if (V_noList) // ensure no misleading info saved
                    xMin=nan; xMax=nan; yMin=nan; yMax=nan;
                else
                    wnote+= "\rThe marquee was located at {left, right, top, bottom} = {%g, %g, %g, %g}\r on the axes %s\r and %s"
                    sprintf wnote, wnote, getwavesdatafolder(yw,2), secs2dt(datetime), xMin, xMax, yMin, yMax, getwavesdatafolder(yw,2), ifElseStr(WaveExists(xw), getwavesdatafolder(yw,2), "[none]")
                endif
                note /K MarqueePs, wnote
                note /K MarqueeMask, wnote
                PutScrapText getwavesdatafolder(MarqueePs,2)
               
                // wrap up
                if (numpnts(MarqueePs) < 100) // interact if just a few points
                    string output= wave2list(MarqueePs)
                    if (printStuff)
                        printf "The following indices of wave %s (trace %s) were selected:\r%s\rand are saved in %s and %s \r", getWaveFullPathStr(yw), traceName, output, getWaveFullPathStr(MarqueePs), getWaveFullPathStr(MarqueeMask)
                    endif
                    // doAlert 0, "A list of row indices has been copied to the clipboard and printed."
                    PutScrapText output
                else
                    if (printStuff)
                        printf "// %g indices of wave %s (trace %s) were selected\r // and saved in %s and %s [the former wave ref copied to clipboard]\r", numpnts(mask), getWaveFullPathStr(yw), traceName, getWaveFullPathStr(MarqueePs), getWaveFullPathStr(MarqueeMask)
                    endif
                endif
               
                cd sdf
                break
               
            default:
                cd sdf
                abort "Argument not recognized -- " + GetRTStackInfo(0)
        endswitch
    endfor
   
    if (n_traces_used==0)
        print "No points were selected."
    endif
end


// Given "item" and 3, returns "item;item;item"
function/s strmultiply(input, n)
string input
variable n
variable i=0
    string output= ""
    for (i=0; i<n; i+=1)
        output += input + ";"
        n -= 1
    endfor
    return output
end

function/s secs2dt(secs)
// Given a datetime in seconds, returns an ISO-8601--formatted datetime.
//      e.g. secs2dt(date2secs(2012,1,31)) returns "2012-01-31 00:00:00"
    variable secs
    if (secs)
        return secs2date(secs,-2) + " " + secs2time(secs,3)
    endif
    return ""
end


function deleteNaNs(w)
    // Deletes points in w containing NaNs.
    wave w
   
    variable i, j, np= numpnts(w)
    make /free /n=(np) onda
   
    for (i=0; i<np; i+=1)
        if (numtype(w[i])!=2)
            onda[j]= w[i]
            j+=1
        endif
    endfor
   
    DeletePoints j, i-j, onda
    duplicate /o onda, w
end

function/s ifelseStr(bool, iftrue, iffalse)
// if variable bool is true, return iftrue, else return iffalse.
// analogue to R's ifelse()
    variable bool
    string iftrue, iffalse
    if (bool)
        return iftrue
    endif
    return iffalse
end


// Given a wave reference, returns a string-list formatted version of the wave.
function/s wave2list(w[,noNaNs])
// printed using %g.
//      e.g. make w={1,2,3}; print wave2list(w) returns "1;2;3"
//          make w={1,nan,3}; print wave2list(w) returns "1;3"
    wave w
    variable noNaNs // optionally remove NaNs from the returned list
   
    if (!WaveExists(w) || numpnts(w)==0)
        return ""
    endif
   
    string str
    wfprintf str, "%g;", w
   
    if (noNaNs)
        do
            str = removeFromList("NaN", str)
        while (findInList("NaN", str)>=0)
    endif
    return str
end

// returns "root:exampleDF:exampleWaveName"
function/s getWaveFullPathStr(w)
    wave w
    if (WaveExists(w))
        return getwavesdatafolder(w,2)
    else
        return ""
    endif
end


function findInList(findstr, liststr[, sep])
    // Returns the list index in which findstr is found in ;-list liststr,
    // else returns -1
    string findstr, liststr, sep
    if (ParamIsDefault(sep))
        sep = ";"
    endif
    return WhichListItem(findstr, liststr, sep)
end

Forum

Support

Gallery

Igor Pro 8

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More