Package: saffy
SaffySignal Analysis Framework For YouA simple signal analysis framework, which aims at clarity of code and reproducibility of solutions. The plugin architecture
aims to encourage building modular code among scientists and data analysts. It provides a basic structure for signal
storage and a pipeline for analysis.Installpip3 install saffyFrom SourceUsing a virtualenv is recommended!Download the package to your project directorygit clone https://github.com/PPierzc/saffy.gitInstall dependenciespip3 install -r ./saffy/requirements.txtUsageimportsaffysig=saffy.SignalManager(generator=signal_data)Basic SignalManager instance structurefielddescriptionfssampling frequencynum_channelsnumber of channelschannel_namesname for each channeldatathe signal in the structure of (epoch x channel x signal)ttime vectorepochsnumber of epochstagsposition of tags in signalspectrummatrix of spectrumspectrum_freqsvector of frequenciesphasematrix of phaseSignalManager init functionIt takes one labelled argument:generatororfilename.GeneratorA dictionary of the structuredata = {
      'fs': # float,
      'num_channels': # integer,
      'channel_names': # list of strings,
      'epochs': # integer,
      't': # time array,
      'tags': # list,
      'data': # Signal Matrix
  }saffy.SignalManager(generator=data)FilenameThe name of the file generated by Svarog. 3 files eg.data.raw,data.xml,data.tagsaffy.SignalManager(filename='data')PluginsPlugins are classes that inherit from the PluginManager. They extend the functionality of the basic Signal Manager.
Some plugins are provided out of the boxFiltersAdds basic filtersGraphicsAdds functions to display the signal dataWelchCalculating the Welch SpectrumHilbertCalculating the Hilbert TransformCreating Custom PluginsYou might want to add some custom features.The proposed convention for plugin development is the following.
All data that is to be stored extra, should be stored in the form of a dictionary assigned to a variable of the same name
as the plugin.Plugin functions should be preceded by the plugin name.importsaffyclassCustomPlugin(saffy.PluginManager):def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)self.custom={'param':'some value'}defcustom_function(self):# do somethingpasssaffy.SignalManager.register_plugin(CustomPlugin)sig=saffy.SignalManager(generator=signal_data)sig.custom_function()ExampleA short example of how to use saffy for EEG data analysis.defgenerate_signal():data={'fs':512,'num_channels':3,'channel_names':['C3','C4','d1'],'epochs':1}T=20t=np.arange(0,T,1/data['fs'])mu_freq=10beta_freq=23net_freq=50data['data']=np.zeros((data['epochs'],data['num_channels'],len(t)))forepochinrange(data['epochs']):data['data'][epoch][0]+=(0.1*t+0.1)*sin(t,mu_freq)data['data'][epoch][0]+=(0.1*t+0.1)*sin(t,beta_freq)data['data'][epoch][0]+=sin(t,net_freq)data['data'][epoch][0]+=0.3*noise(t)data['data'][epoch][1]+=(0.1*t+0.1)*sin(t,mu_freq)data['data'][epoch][1]+=(0.1*t+0.1)*sin(t,beta_freq)data['data'][epoch][1]+=sin(t,net_freq)data['data'][epoch][1]+=0.3*noise(t)data['data'][epoch][2][::5*data['fs']]=1data['data'][epoch][2][0]=0data['t']=tdata['tags']=[]returndataEEG=saffy.SignalManager(generator=generate_signal())EEG.set_tags_from_channel('d1')EEG.remove_channel('d1')PRE_EEG=EEG.copy('pre')PRE_EEG.set_epochs_from_tags(-4,-2)PRE_EEG.welch_spectrum()PRE_EEG.spectrum=np.mean(PRE_EEG.spectrum,axis=0)PRE_EEG.spectrum=np.reshape(PRE_EEG.spectrum,(1,*PRE_EEG.spectrum.shape))POST_EEG=EEG.copy('post')POST_EEG.set_epochs_from_tags(0.5,2.5)POST_EEG.welch_spectrum()POST_EEG.spectrum=np.mean(POST_EEG.spectrum,axis=0)POST_EEG.spectrum=np.reshape(POST_EEG.spectrum,(1,*POST_EEG.spectrum.shape))fig,ax=plt.subplots(nrows=max([PRE_EEG.num_channels,POST_EEG.num_channels]),ncols=1,sharex=True,sharey=True,figsize=(10,10))PRE_EEG.graphics_spectrum_plot(fig,ax,'Change',label='Pre')POST_EEG.graphics_spectrum_plot(fig,ax,color='#0000ff',label='Post')forainax:a.legend()plt.show()plt.close()ContributingIf you like the project and want to add something to it then please create a pull request.The title should shortly summarize the goal of your additionIn the description go in depth with the changes you have made and why.