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

Average rating
(2 votes)

#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

Back to top