Package: pysnirf2
pysnirf2pip install snirfDynamically generated Python library for reading, writing, and validatingShared Near Infrared Spectroscopy Format (SNIRF) files.Developed and maintained by theBoston University Neurophotonics Center.DocumentationDocumentationis generated from source usinglazydocsInstallationpip install snirfpysnirf2 requires Python > 3.6.FeaturesThe library generated via metaprogramming, but the resulting classes explicitly implement each and every specified SNIRF field so as to provide an extensible object-oriented foundation for SNIRF applications.Open a SNIRF fileSnirf(<path>, <mode>)opens a SNIRF file at<path>or creates a new one if it doesn't exist.Use mode 'w' to create a new file, 'r' to read a file, and 'r+' to edit an existing file.fromsnirfimportSnirfsnirf=Snirf(r'some\path\subj1_run01.snirf','r+')Create a SNIRF file objectSnirf()with no arguments creates a temporary file which can be written later usingsave().snirf=Snirf()Closing a SNIRF fileASnirfinstance wraps a file on disk. It should be closed when you're done reading from it or saving.snirf.close()Use awithstatement to ensure that the file is closed when you're done with it:withSnirf(r'some\path\subj1_run01.snirf','r+')assnirf:# Read/writesnirf.save()Copy a SNIRF file objectAnySnirfobject can be copied to a new instance in memory, after which the original can be closed.snirf2=snirf.copy()snirf.close()# snirf2 is free for manipulationView or retrieve a file's contents>>>snirfSnirfat/filename:C:\Users\you\some\path\subj1_run01.snirfformatVersion:v1.0nirs:<iterableof2<class'pysnirf2.NirsElement'>>>>>snirf.nirs[0].probeProbeat/nirs1/probecorrelationTimeDelayWidths:[0.]correlationTimeDelays:[0.]detectorLabels:['D1''D2']detectorPos2D:[[30.0.][0.30.]]detectorPos3D:[[30.0.0.][0.30.0.]]filename:C:\Users\you\some\path\subj1_run01.snirffrequencies:[1.]landmarkLabels:NonelandmarkPos2D:NonelandmarkPos3D:Nonelocation:/nirs/probemomentOrders:NonesourceLabels:['S1']sourcePos2D:[[0.0.]]sourcePos3D:[[0.][0.][0.]]timeDelayWidths:[0.]timeDelays:[0.]useLocalIndex:Nonewavelengths:[690.830.]wavelengthsEmission:NoneEdit a SNIRF fileAssign a new value to a field>>>snirf.nirs[0].metaDataTags.SubjectID='subj1'>>>snirf.nirs[0].metaDataTags.SubjectID'subj1'>>>snirf.nirs[0].probe.detectorPos3D[0,:]=[90,90,90]>>>snirf.nirs[0].probe.detectorPos3Darray([[90.,90.,90.],[0.,30.,0.]])Note: assignment via slicing is not possible indynamic_loadingmode.Indexed groupsIndexed groups are defined by the SNIRF file format as groups of the same type which are indexed via their name + a 1-based index, i.e.data1,data2, ... orstim1,stim2,stim3, ...pysnirf2 provides an iterable interface for these groups using Pythonic 0-based indexing, i.e.data[0],data[1], ... orstim[0],stim[1]],stim[2], ...>>>snirf.nirs[0].stim<iterableof0<class'pysnirf2.StimElement'>>>>>len(nirs[0].stim)0To add an indexed group, use theappendGroup()method of anyIndexedGroupclass. Indexed groups are created automatically.nirsis an indexed group.>>>snirf.nirs[0].stim.appendGroup()>>>len(nirs[0].stim)1>>>snirf.nirs[0].stim[0]StimElementat/nirs/stim2data:NonedataLabels:Nonefilename:C:\Users\you\some\path\subj1_run01.snirfname:NoneTo remove an indexed groupdelsnirf.nirs[0].stim[0]Save a SNIRF fileOverwrite the open filesnirf.save()Save As in a new locationsnirf.save(r'some\new\path\subj1_run01_edited.snirf')Thesave()function can be called for any group or indexed group:snirf.nirs[0].metaDataTags.save('subj1_run01_edited_metadata_only.snirf')Dynamic loading modeFor larger files, it may be useful to load data dynamically: data will only be loaded on access, and only changed datasets will be written onsave(). When creating a newSnirfinstance, setdynamic_loadingtoTrue(DefaultFalse).snirf=Snirf(r'some\path\subj1_run01.snirf','r+',dynamic_loading=True)Note: in dynamic loading mode, array data cannot be modified with indices like in the example above:>>>snirf=Snirf(TESTPATH,'r+',dynamic_loading=True)>>>snirf.nirs[0].probe.detectorPos3Darray([[30.,0.,0.],[0.,30.,0.]])>>>snirf.nirs[0].probe.detectorPos3D[0,:]=[90,90,90]>>>snirf.nirs[0].probe.detectorPos3Darray([[30.,0.,0.],[0.,30.,0.]])To modify an array indynamic_loadingmode, assign it, modify it, and assign it back to the Snirf object.>>>detectorPos3D=snirf.nirs[0].probe.detectorPos3D>>>detectorPos3D[0,:]=[90,90,90]>>>snirf.nirs[0].probe.detectorPos3D=detectorPos3Darray([[90.,90.,90.],[0.,30.,0.]])Validating a SNIRF filepysnirf2 features functions for validating SNIRF files against the specification and generating detailed error reports.Validate a Snirf object you have createdresult=snirf.validate()Validate a SNIRF file on diskTo validate a SNIRF file on diskfromsnirfimportvalidateSnirfresult=validateSnirf(r'some\path\subj1_run01.snirf')assertresult,'Invalid SNIRF file!\n'+result.display()# Crash and display issues if the file is invalid.Validation resultsThe validation functions return aValidationResultinstance which contains details about the SNIRF file.
To view the validation result:>>>result.display(severity=3)# Display all fatal errors<pysnirf2.pysnirf2.ValidationResultobjectat0x000001C0CCF05A00>/nirs1/data1/measurementList103/dataTypeFATALREQUIRED_DATASET_MISSING/nirs1/data1/measurementList103/dataTypeIndexFATALREQUIRED_DATASET_MISSING/nirs1/data1FATALINVALID_MEASUREMENTLISTFound668OK(hidden)Found635INFO(hidden)Found204WARNING(hidden)Found3FATALFileisINVALIDTo look at a particular result:>>>result.errors[2]<pysnirf2.pysnirf2.ValidationIssueobjectat0x000001C0CB502F70>location:/nirs1/data1severity:3FATALname:8INVALID_MEASUREMENTLISTmessage:ThenumberofmeasurementListelementsdoesnotmatchtheseconddimensionofdataTimeSeriesThe full list of validation resultsresult.issuescan be explored programatically.Code generationThe interface and validator are generated via metacode that downloads and parsesthe latest SNIRF specification.See\genfor details.