Structure to wave note?
tkessler
I'm new to using structures in Igor, and would like to have a function that will write a given structure's fields to the note of a wave as a means to carry the data between various routines.
For example, if I have two structures defined in Igor as "A_struct" and "B_struct," then I'd like to have a function such as the following:
Function struct2note(wavenm, s, s_def)
WAVE wavenm
string s_def
STRUCT s_def &s
...remaining code
end
WAVE wavenm
string s_def
STRUCT s_def &s
...remaining code
end
...where the "s_def" string can be used to pass the string "A_struct" or "B_struct" to the function so the function can parse it out and append info from it to a wave as a note.
Is this possible to do in Igor?
You can pass a structure from one routine to a subroutine without any tricks. If you want to store the structure's information for later user, after the original functions have all returned, then you need tricks.
You can convert the structure to text and then convert back. Here is an example. Note the caveats in the comments:
Variable num
String str
Wave w
EndStructure
Function/S TestStructToText(s)
STRUCT TestStruct& s
String temp
String text = ""
sprintf temp, "num=%.15g;", s.num
text += temp
/// s.str must not contain = or ; characters
sprintf temp, "str=%s;", s.str
text += temp
// Path to wave must not contain = character
String fullPath = ""
if (WaveExists(s.w))
fullPath = GetWavesDataFolder(s.w, 2)
endif
sprintf temp, "wave=%s;", fullPath
text += temp
return text
End
Function SetStructFromText(text, s)
String text // keyword-value pair created by TestStructToText
STRUCT TestStruct& s
String temp
temp = StringByKey("num", text, "=")
s.num = str2num(temp)
temp = StringByKey("str", text, "=")
s.str = temp
temp = StringByKey("wave", text, "=")
Wave/Z s.w = $temp // NULL if wave does not exist
End
Function Demo()
Make /O root:DemoWave
STRUCT TestStruct s
s.num = pi
s.str = "pie"
Wave s.w = root:DemoWave
// Print s
String text = TestStructToText(s)
Print text
STRUCT TestStruct s2
SetStructFromText(text, s2)
Print s2
End
January 10, 2013 at 08:47 pm - Permalink
What I am looking to do instead is write a function that can take any structure definition as an input and then be able to parse it. So if I have one structure definition "A" that has two strings, and structure "B" that has three strings, I'd like to be able to pass the function either of these structures ("A" or "B") and have it identify their properties and convert them to a single text string.
In essence, I'm curious to know if there is a way to use a dynamic structure name in a "STRUCT" declaration in a function. I'd like to be able to use a string variable as the structure name, so the single STRUCT declared in a function can be any structure I've defined. With this approach, I'd like to write a function similar to the one in my first posting, where the string "s_def" can be the name of structure "A" or "B" depending on what I define "s_def" as.
January 10, 2013 at 10:35 pm - Permalink
No, this is not possible.
You can freeze-dry any structure using PutStruct and reconstitute it to a structure using GetStruct but you need to know the specific type of the structure to use it once reconstituted.
In its freeze-dried state it is binary and not human-readable. Also you can not freeze-dry any structure containing a String, Wave or DFREF field because the objects they refer to may not exist when they are reconstituted.
To store free-form data you can use a keyword-value string (see StringByKey).
I'm not sure why you want to freeze-dry any structure. You might be approaching the problem in the wrong way for an Igor program
No, this is not possible.
January 10, 2013 at 10:51 pm - Permalink
As for why this would be useful, one example would be to call multiple "wavestats" routines and store the resulting variables in several different local structures, such as the following:
Structure w_stat
variable npnts
variable numNANs
variable numINFs
variable avg
variable sum
variable sdev
variable sem
variable rms
variable adev
variable skew
variable kurt
variable minloc
variable maxloc
variable min
variable max
variable minRowLoc
variable maxRowLoc
variable startRow
variable endRow
endstructure
//w_stats structure creation handler
Function wstats(wavenm,w,[M,R])
WAVE wavenm
STRUCT w_stats &w
variable M //mode option
string R //range option
if(M)
else
M=2
endif
if(!stringmatch(R,""))
variable points=0
if(stringmatch(R[0],"["))
points=1
elseif(stringmatch(R[0],"("))
points=0
else
Abort "Improper range specification. Use \"[start, stop]\" for points or \"(start, stop)\" for x values"
endif
R=R[1,strlen(R)-2]
variable start=str2num(stringfromlist(0,R,","))
variable fin=str2num(stringfromlist(1,R,","))
if(points)
Wavestats/Q/M=(M)/R=[start,fin] wavenm
else
Wavestats/Q/M=(M)/R=(start,fin) wavenm
endif
else
Wavestats/Q/M=(M) wavenm
endif
w.npnts=V_npnts
w.numNANs=V_numNANs
w.numINFs=V_numINFs
w.avg=V_avg
w.sum=V_sum
w.sdev=V_sdev
w.sem=V_sem
w.rms=V_rms
w.adev=V_adev
w.skew=V_skew
w.kurt=V_kurt
w.minloc=V_minloc
w.maxloc=V_maxloc
w.min=V_min
w.max=V_max
w.minRowLoc=V_minRowLoc
w.maxRowLoc=V_maxRowLoc
w.startRow=V_startRow
w.endRow=V_endRow
end
//All put to use...
Function Demo()
make test
make test2
test=x
test=x^2
STRUCT w_stat tt1
wstats(test, tt1, R="[0-100]") //runs wavestats between pt 0-100 on wave "test" and stores results in struct tt1
STRUCT w_stat tt2
wstats(test2, tt2, R="[0-100]") //runs wavestats between pt 0-100 on wave "test2" and stores results in struct tt2
//calculations can now be done with the structures
Print tt1.avg-tt2.avg
end
In this case we have two structures of definition "w_stat" that exist locally in the "Demo" function, and arguably provide a more concise approach to pulling the wavestats variables than having to call "wavestats" each time and ensure the output variables (V_avg, V_sum, V_npnts, etc.) are properly stored each time.
The true benefit of this approach would be to pass the data in the structures globally as objects for other uses; however, the structs are restricted to this function and those that are called by it, which is a big limitation for this approach.
One workaround to this limitation is as you mentioned to write a function specific to one structure that will save its contents (with some exceptions) as a global string variable, though this requires a special handling routine for each structure definition when it would be nice to have a single function that can handle "any" structure in this manner, regardless of its definition.
The second option would be to append such a string to a current object that can handle it, and the wave "notes" feature seems like a good option to exploit for this purpose. With a basic pair of "SetNote" and "GetNote" routines that could save variables to the notes by key, one could quickly put and pull the structure information on a wave in whatever function the wave is currently being handled, and without the function needing to be called from a prior one, which is a requirement for direct uses of structures.
I guess one approach for this would be to forego using structs altogether and instead use a routine that will develop and manage the structured information in the notes of a wave directly (setting and getting the data from the wave notes instead of managing a separate struct and then writing it to the wave notes or a string); however, this might slow things down as the data would have to be converted to and from strings all the time instead of being pulled from the notes into a struct at the beginning of the function, and managed as structure data in the function, and then converted back to a note for storage at the end of the function's routines.
January 11, 2013 at 11:08 am - Permalink
For example, don't store the min and max of a wave unless you can save a significant amount of time by storing it. (If you do store it you will need to store the modification date of the wave to determine if the stored min and max may be stale.)
If you want to store data describing a wave then you should use the wave note to store keyword-value pairs. You compute and store only the data you need. For example, if you need the min and max, compute only what you need to compute (either using WaveMin and WaveMax or WaveStats/M=1) and store the minimum in the wave note.
Where structures work is for passing a collection of data from one function to another to another when the information needed consists of too many items to use regular parameters. You would collect all of the information in the structure in the top-level function and pass it to lower-level functions. You might use the keyword-value pair of the wave note as the source for some of the information in the structure.
The HDF5 Browser.ipf file uses structures this way. The structure HDF5BrowserData stores all of the data needed for an HDF5 browser window. A single function, SetHDF5BrowserData, is called by the high-level routines. It collects all of the information into the structure:
SetHDF5BrowserData(browserName, bd)
The structure is then passed from one routine to another.
January 11, 2013 at 12:32 pm - Permalink
I must be completely missing your point because you could use:
WAVE wavenm
String name
variable M //mode option
string R //range option
if(M)
else
M=2
endif
if(!stringmatch(R,""))
variable points=0
if(stringmatch(R[0],"["))
points=1
elseif(stringmatch(R[0],"("))
points=0
else
Abort "Improper range specification. Use \"[start, stop]\" for points or \"(start, stop)\" for x values"
endif
R=R[1,strlen(R)-2]
variable start=str2num(stringfromlist(0,R,","))
variable fin=str2num(stringfromlist(1,R,","))
if(points)
Wavestats/Q/M=(M)/R=[start,fin]/W wavenm
else
Wavestats/Q/M=(M)/R=(start,fin)/W wavenm
endif
else
Wavestats/Q/M=(M)/W wavenm
endif
Wave M_WaveStats
Duplicate /O M_WaveStats,$name
end
... and the various waves saved have nice dimension labels for the various variables which you can access with the [%...] method.
A.G.
WaveMetrics, Inc.
January 11, 2013 at 03:29 pm - Permalink
I see the mention of the M_WaveStats wave in the help documentation for wavestats, but when I try it out as you mentioned here it gives me an error that it expected a wave name, and upon debugging the "Wave M_WaveStats" declaration results in a pointer to a null wave. Is this perhaps a bug?
While this approach may work for WaveStats, my original goal was to have a paradigm that would globally save the contents of any user-defined structure and then be able to call it back as is needed. I did use a wavestats example here, but that is just one example and there could be other uses for structures to store data.
However, as hrodstein mentioned in his last post the issue here may be the storage of data and that a better approach may be to limit it as much as possible. While I agree reducing this is best, I can still see situations where storing structured data globally could be very useful. I think I will try to rework my approach here to use a keyword-value pairing of data to save directly in the notes of a wave, and create a set of handling functions for this purpose. That's ultimately what I wanted to do from the beginning with a function that could dynamically access a structure by its definition name, but I could not figure out how to do that (and it doesn't seem to be possible).
January 11, 2013 at 05:31 pm - Permalink
I see from the help that M_WaveStats is created only if you use /W or /C=<non-zero value>. So if you want to produce M_WaveStats use the /W flag. Here is an example:
Wave w
WaveStats /W /M=1 /Q w
Wave M_WaveStats
Print M_WaveStats
End
It is important that a wave declaration such as "Wave M_WaveStats" be placed after the operation that creates the wave. For an explanation of this, execute:
January 11, 2013 at 11:09 pm - Permalink
Ah missed that flag when checking out the code above. Its working now. I'll see what I can do with this as one option at least for handling wavestats.
January 11, 2013 at 11:59 pm - Permalink
I am trying to follow all the instructions given here to solve for my problems. Let give you an overview of what I want to achieve. I have a wave acquired over a period of 12hours. I change the concentration every 10 mins (it can vary). I have defined another wave (wave2) which have definite values anytime i change the concentration. Example, when wave1 concentration is changed, wave2 has a value of 2 (manually defined) or else a value of 0. I want to used wave2[i]=2 to average the range using wavestats and copy V_avg, V_npts, V_startRow and V_endRow to columns in a new table. Please help. I am new to Igor.
February 20, 2013 at 01:08 pm - Permalink
How about something like
WaveStats extractedwave
In this case, the start and end rows don't have much meaning.
John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
February 20, 2013 at 03:08 pm - Permalink