Loop matrix

Hi everyone,

I have written a procedure where the waves are sorted by time of day and then the statistics is calculated for each hour. There are several waves and I would like to do a loop in the Function CalcConcStats_BC_Test(sortedTimeOfDay, sortedBC1, sortedBC2, averagingIntervalInMinutes) to avoid writing code for each wave (I mean: BC1Stats, BC2Stats,...).
I have tried to do a wavelist, but it does not work.


Thanks in advance, I would really appreciate any help you could provide!

Please find attached a .txt file with some data (Prueba_Stats_BC.txt).

Here is the procedure:



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


//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------  SELECT BC waves to be sorted  -----------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Function WavesSorted_BC_Test()
	String date_utc_time		//Name of the Date&Time wave that we want to analyze
	string BC1
	string BC2
	prompt date_utc_time, "Date&Time wave to be sorted", popup WaveList("*", ";", "")
	prompt BC1, "BC1 wave to be sorted", popup WaveList("*", ";", "")
	prompt BC2, "BC2 wave to be sorted", popup WaveList("*", ";", "")
	DoPrompt "Waves sorted", date_utc_time, BC1, BC2
	SortedByTimeOfDay_BC_Test($date_utc_time, $BC1, $BC2)
End


//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------  Function to sort input Date&Time wave by the time of day. NaNs (blanks) go to the end  --------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Function/S SortedByTimeOfDay_BC_Test(date_utc_time, BC1, BC2)
	wave date_utc_time							//Date&Time wave to be sorted
	wave BC1, BC2		//BC waves to be sorted
	variable i
	
	duplicate/O date_utc_time, TimeOfDay_Sorted
	duplicate/O BC1,  BC1_Sorted
	duplicate/O BC2,  BC2_Sorted
	
	TimeOfDay_Sorted = mod(date_utc_time, 24*60*60)		// TimeOfDay now contains just time of day (remove date)
	Sort {TimeOfDay_Sorted,date_utc_time}, TimeOfDay_Sorted, BC1_Sorted, BC2_Sorted		// Sort by TimeOfDay, then by date


	CalcConcStats_BC_Test(TimeOfDay_Sorted, BC1_Sorted, BC2_Sorted, 60)
	
End


//----------------------------------------------------------------------------------------------------------------
//--------------------  Function to estimate the statistics of the waves  --------------------
//----------------------------------------------------------------------------------------------------------------

Function CalcConcStats_BC_Test(sortedTimeOfDay, sortedBC1, sortedBC2, averagingIntervalInMinutes)
	wave sortedTimeOfDay		// Time-of-day wave
	wave sortedBC1, sortedBC2		// Concentrations wave
	variable averagingIntervalInMinutes		// Interval over which to average in minutes; e.g., 60 for 1 hour
	
	variable numBins = (24 * 60) / averagingIntervalInMinutes
	
	variable averagingIntervalInSeconds = averagingIntervalInMinutes * 60
	
	//Number of columns of Statistical tables
	variable numStatsColumns = 12
	
	// Variables to find the first and last point of each bin
	variable numInputPoints = numpnts(sortedTimeOfDay)
	variable i, bin, timeOfDay
	
	Variable BC1, BC2
	
	// To find the median and standard deviation for each bin
	variable first
	variable last
	variable count
	variable median

	//-------------------------------------------------------------------------------------------//
	//--------------------  Make output waves of BC waves  --------------------//
	//-------------------------------------------------------------------------------------------//
	
	//--------------------------------------------------
	//--------------------  BC1  --------------------
	//--------------------------------------------------
	
	Make /O /D /N=(numBins,numStatsColumns) BC1Stats = 0		// 2D wave
	SetDimLabel 1, 0, Count, BC1Stats			// Column 0 is count of concentrations in bin
	SetDimLabel 1, 1, First, BC1Stats			// Column 1 is first input point in bin
	SetDimLabel 1, 2, Last, BC1Stats			// Column 2 is last input point in bin
	SetDimLabel 1, 3, Min, BC1Stats			// Column 3 is median concentration for bin
	SetDimLabel 1, 4, Max, BC1Stats			// Column 4 is median concentration for bin
	SetDimLabel 1, 5, Mean, BC1Stats			// Column 5 is mean concentration for bin
	SetDimLabel 1, 6, StdDev, BC1Stats		// Column 6 is standard deviation for bin
	SetDimLabel 1, 7, Median, BC1Stats		// Column 7 is median concentration for bin
	SetDimLabel 1, 8, Q25, BC1Stats		// Column 8 is Quartil25 for bin
	SetDimLabel 1, 9, Q75, BC1Stats		// Column 9 is Quartil75 for bin
	SetDimLabel 1, 10, upperInnerFence1, BC1Stats		// Column 10 is  for bin
	SetDimLabel 1, 11, lowerInnerFence1, BC1Stats		// Column 11 is  for bin
	
	
	// Set X scaling such that the statistics for each bin fall in the middle of bin when graphed
	SetScale/P x, averagingIntervalInSeconds-3600, averagingIntervalInSeconds, "dat", BC1Stats
	
	// Preset columns of output
	BC1Stats[][%first] = -1
	BC1Stats[][%last] = -1

	// Find the first and last point of each bin, and count
	for (i=0; i<numInputPoints; i+=1)			// Bin and sum up the concentrations
		timeOfDay = sortedTimeOfDay[i]
		bin = trunc(timeOfDay / averagingIntervalInSeconds)
		if (BC1Stats[bin][%first] == -1)
			BC1Stats[bin][%first] = i
		endif
		BC1Stats[bin][%last] = i
		if (numtype(BC1) == 0)				// Exclude NaNs and INFs
			BC1Stats[bin][%count] += 1			// Increment bin count
		endif
		count = BC1Stats[bin][%count]
	endfor	
	
	// Find the minimum for each bin
	BC1Stats[][%min] = WaveMin(sortedBC1, BC1Stats[p][%first], BC1Stats[p][%last])

	// Find the maximum for each bin
	BC1Stats[][%max] = WaveMax(sortedBC1, BC1Stats[p][%first], BC1Stats[p][%last])

	// Find the mean, median and standard deviation for each bin
	for(bin=0; bin<numBins; bin+=1)
		first = BC1Stats[bin][%first]		// First input point for this bin
		last = BC1Stats[bin][%last]		// Last input point for this bin
		Duplicate /R=[first,last] /FREE sortedBC1, temp1		// Needed because StatsMedian works on the whole wave

		WaveTransform zapNaNs, temp1		// Needed because StatsMedian does not support NaNs
		
		median = StatsMedian(temp1)
		BC1Stats[bin][%median] = median

//		WaveStats /M=2 /Q temp1
		WaveStats/Q temp1
		BC1Stats[bin][%mean] = V_avg
		BC1Stats[bin][%stddev] = V_sdev

		StatsQuantiles/Q temp1
		BC1Stats[bin][%Q25] = V_Q25
		BC1Stats[bin][%Q75] = V_Q75
		
		Variable H1=V_Q75 - V_Q25
		Variable step1=1.5*H1
		Variable upperInnerFence1=V_Q75 + step1
		Variable upperOuterFence1=V_Q75 + 2*step1
		Variable lowerInnerFence1=V_Q25 - step1
		Variable lowerOuterFence1=V_Q25 - 2*step1
		
		BC1Stats[bin][%upperInnerFence1] = upperInnerFence1
		BC1Stats[bin][%lowerInnerFence1] = lowerInnerFence1

	endfor
	

	//--------------------------------------------------
	//--------------------  BC2  --------------------
	//--------------------------------------------------
	
	Make /O /D /N=(numBins,numStatsColumns) BC2Stats = 0		// 2D wave
	SetDimLabel 1, 0, Count, BC2Stats			// Column 0 is count of concentrations in bin
	SetDimLabel 1, 1, First, BC2Stats			// Column 1 is first input point in bin
	SetDimLabel 1, 2, Last, BC2Stats			// Column 2 is last input point in bin
	SetDimLabel 1, 3, Min, BC2Stats			// Column 3 is median concentration for bin
	SetDimLabel 1, 4, Max, BC2Stats			// Column 4 is median concentration for bin
	SetDimLabel 1, 5, Mean, BC2Stats			// Column 5 is mean concentration for bin
	SetDimLabel 1, 6, StdDev, BC2Stats		// Column 6 is standard deviation for bin
	SetDimLabel 1, 7, Median, BC2Stats		// Column 7 is median concentration for bin
	SetDimLabel 1, 8, Q25, BC2Stats		// Column 8 is Quartil25 for bin
	SetDimLabel 1, 9, Q75, BC2Stats		// Column 9 is Quartil75 for bin
	SetDimLabel 1, 10, upperInnerFence2, BC2Stats		// Column 10 is  for bin
	SetDimLabel 1, 11, lowerInnerFence2, BC2Stats		// Column 10 is  for bin
	
	// Set X scaling such that the statistics for each bin fall in the middle of bin when graphed
	SetScale/P x, averagingIntervalInSeconds-3600, averagingIntervalInSeconds, "dat", BC2Stats
	
	// Preset columns of output
	BC2Stats[][%first] = -1
	BC2Stats[][%last] = -1

	// Find the first and last point of each bin
	for (i=0; i<numInputPoints; i+=1)			// Bin and sum up the concentrations
		timeOfDay = sortedTimeOfDay[i]
		bin = trunc(timeOfDay / averagingIntervalInSeconds)
		if (BC2Stats[bin][%first] == -1)
			BC2Stats[bin][%first] = i
		endif
		BC2Stats[bin][%last] = i
		if (numtype(BC2) == 0)				// Exclude NaNs and INFs
			BC2Stats[bin][%count] += 1			// Increment bin count
		endif
		count = BC2Stats[bin][%count]
	endfor	
	
	// Find the minimum for each bin
	BC2Stats[][%min] = WaveMin(sortedBC2, BC2Stats[p][%first], BC2Stats[p][%last])

	// Find the maximum for each bin
	BC2Stats[][%max] = WaveMax(sortedBC2, BC2Stats[p][%first], BC2Stats[p][%last])

	// Find the median and standard deviation for each bin
	for(bin=0; bin<numBins; bin+=1)
		first = BC2Stats[bin][%first]		// First input point for this bin
		last = BC2Stats[bin][%last]		// Last input point for this bin
		Duplicate /R=[first,last] /FREE sortedBC2, temp2		// Needed because StatsMedian works on the whole wave

		WaveTransform zapNaNs, temp2		// Needed because StatsMedian does not support NaNs
		
		median = StatsMedian(temp2)
		BC2Stats[bin][%median] = median

//		WaveStats /M=2 /Q temp2
		WaveStats/Q temp2
		BC2Stats[bin][%mean] = V_avg
		BC2Stats[bin][%stddev] = V_sdev
		
		StatsQuantiles/Q temp1
		BC2Stats[bin][%Q25] = V_Q25
		BC2Stats[bin][%Q75] = V_Q75
		
		Variable H2=V_Q75 - V_Q25
		Variable step2=1.5*H2
		Variable upperInnerFence2=V_Q75 + step2
		Variable upperOuterFence2=V_Q75 + 2*step2
		Variable lowerInnerFence2=V_Q25 - step2
		Variable lowerOuterFence2=V_Q25 - 2*step2
		
		BC2Stats[bin][%upperInnerFence2] = upperInnerFence2
		BC2Stats[bin][%lowerInnerFence2] = lowerInnerFence2

	endfor	
	
End

Prueba_Stats_BC.txt (5.67 KB)
So you want to avoid writing the same code over and over again? Is there a specific meaning for CalcConcStats_BC_Test to take two waves BC1 and BC2 (I guess not from the code)? If not just rewrite the code to use dynamic wave names instead of hard-coded ones (i.e., BC1Stats etc. ... never a good idea but for to simplest cases), and then use a function to loop though a wave list repeatedly calling your stats function in the process. Here this should give you some idea (note the use of the '$', and you should probably read more about it by executing
DisplayHelpTopic "Converting a String into a Reference Using $"
):

Function DynamicExampe(inwave)
	wave inwave
	String statsname = nameofwave(inwave)+"_stats"
	Make/D/O/N=(round(DimSize(inwave,0)/2)) $statsname
	SetScale/P x,DimOffset(inwave,0),DimDelta(inwave,0)*2, $statsname
	Wave workwave = $statsname
	workwave = inwave[p*2] 
End


Btw. this function downsamples the incoming data to halve the points in the process.
Just to clear things up: Of course the function has no real world application besides demonstrating the use of '$'. I thought it might be nice to have at least some effect beyond creating an empty wave. Btw I use Interpolate2 for 'resampling', because I rather dial in the output points.
Thank you chozo and thomas_braun for your help.

The waves come from an Aethalometer instrument, so I have seven black carbon waves (BC1, BC2, ..., BC7). The function CalcConcStats_BC_Test() calculates the statistics of each wave.
I am going to try what you wrote me, I hope it works!

Thanks again!