Package: danpy
Collection of useful python functions/features.Prepared by: Daniel A HagenInstallationpipinstalldanpyInstallation from GitHubgitclonehttps://github.com/danhagen/danpy.git&&cddanpy
pipinstall-rrequirements.txt
pipinstall.Statusbar for Pythonfor/whileloops withdanpy.sbThis helpful statusbar can be used withfor/whileloops to keep track of how much time has elapsed as well as how much time remains. Simply place inside the loop (after initializing the statusbar --dsb-- withfinalValue) andupdatewith the current timestep (i). Atitlecan be added to the statusbar to keep track of individual function/loops and it is recommended that any function that runs a loop usesarbitrary_function_name.__name__to automatically assign an appropriate title. TheinitialValuecan also be initialized or left at default (0).Initialize statusbar before running a for/while loop.fromdanpy.sbimport*fromtimeimportsleepinitialValue=0finalValue=10statusbar=dsb(initialValue,finalValue,title='a Loop')foriinrange(finalValue):sleep(0.5)statusbar.update(i)It is useful to eitherresetthe statusbar instance. However, loops run in succession will automatically reset if the loops are of the same size.fromdanpy.sbimport*fromtimeimportsleepinitialValue=0finalValue=10numberOfOutsideLoops=3statusbar=dsb(initialValue,finalValue,title="Loop-D-Loops")forjinrange(numberOfOutsideLoops):foriinrange(finalValue):sleep(0.5)statusbar.update(i)It is also possible to rename loops when they are run in succession by using the updatingtitle.fromdanpy.sbimport*fromtimeimportsleepinitialValue=0finalValue=10statusbar=dsb(initialValue,finalValue,title="a Loop")foriinrange(finalValue):sleep(0.5)statusbar.update(i)foriinrange(finalValue):sleep(0.5)statusbar.update(i,title="a Different Loop")However, these automatic reset features will only work if the length of each loop is the sameandthey have the same starting value. If you wish to run consecutive loops withdifferentstarting values or loop lengths, then you canresetthe statusbar. It should be noted that the automatic reset, although convenient, will initialize thestart_timeafter the first iteration of the loop. Therefore it is not the most accurate representation of runtime. We recommend a hard reset between trials or a redefinition of the statusbar before each loop.Resetting StatusbarA statusbar can be reset by calling the builtin functionresetone of two way;reset()will return a statusbar with the previously usedinitialValue,finalValue, andtitle, orreset(**kwargs)can manually reset any of those values (while the others are kept as previously define).fromdanpy.sbimport*fromtimeimportsleepinitialValue=0finalValue=10statusbar=dsb(initialValue,finalValue,title='One Loop')foriinrange(finalValue):sleep(0.5)statusbar.update(i)aDifferentFinalValue=1010aDifferentInitialValue=1000statusbar.reset(initialValue=aDifferentInitialValue,finalValue=aDifferentFinalValue,title="Another Loop")foriinrange(aDifferentInitialValue,aDifferentFinalValue):sleep(0.5)statusbar.update(i)UsingwhileLoopsIf using awhileloop, the statusbar will still update, but depending on the nature of the code in the loop, the extrapolation to determine time remaining may be off.fromdanpy.sbimport*fromtimeimportsleepcount=0finalCount=10statusbar=dsb(count,finalCount,title="a while Loop")whilecount<finalCount:sleep(0.5)statusbar.update(count)count+=1Only compatible with while loops that utilize acountmetric where the loop continues whilecount<finalValue. The "<" ensures that the statusbar terminates at 100%. If you use "<=" then the input to the statusbar will bestatusbar.update(i,finalValue+1,**kwargs).Useful Functions for Python withdanpy.useful_functionsIncluded in this package are the functionsis_numberandsave_figures.Simple function to save all current figures.The functionsave_figuresis designed to save all currently open figures (or a prescribed subset of them) to a convenient location for easy storing, viewing, and comparison.
I find that this works best if you create a "figures/" destination folder first so that your figures automatically get organized in a location other than location of your project's source code.In order to save your figures, you must specify thedestinationof the figures (str, in this case, "figures/"), thebaseFileName(str, which can be used to further describe the figures), and a dictionary of the parameters (params:dict) used to create these plots.
This last argument is a little different and can left blank (i.e.,{}) if desired, but I find this to be incredibly useful when you need to compare parameters across trials.
This will also be used to create anotes.txt(orREADME.md; see below) file that allows for quick reference of the used parameters. As an example, try:fromdanpy.useful_functionsimportsave_figuresimportmatplotlib.pyplotaspltimportnumpyasnpdt=0.01signalAmplitude=1signalFrequency=0.5# HzdurationOfSignal=10# secondsparams={"Time Step":dt,"Signal Amplitude":signalAmplitude,"Signal Frequency":signalFrequency,"Duration Of Signal":durationOfSignal}time=np.arange(0,durationOfSignal+dt,dt)signal1=signalAmplitude*np.sin(2*np.pi*signalFrequency*time)signal2=signalAmplitude*np.cos(2*np.pi*signalFrequency*time)fig1=plt.figure()ax1=plt.gca()ax1.plot(time,signal1)ax1.set_xlabel("Time (s)")ax1.set_ylabel("Signal 1")fig2=plt.figure()ax2=plt.gca()ax2.plot(time,signal2)ax2.set_xlabel("Time (s)")ax2.set_ylabel("Signal 2")save_figures("figures/","Signal_figures",params)plt.show()This code will create a subfolder in "figures/" that is time stamped that contains three files (Signal_figures_01-01.png,Signal_figures_01-02.png, andnotes.txt). The first two files have the base file nameSignal_figuresfollowed by the number 01 (which corresponds to the trial number) and then the figure number. This can be useful when you wish to add figures to thesamesubfolder, when the parameters don't change (but more on that later).The notes will appear as such,[Created 2020/01/03 at 09:28.42]

##############################
########### Notes ############
##############################

		NONE

##############################
######### Parameters #########
##############################

		Time Step: 0.01
		Signal Amplitude: 1
		Signal Frequency: 0.5
		Duration Of Signal: 10

##############################Note that all of the parameters have been listed conveniently as well as the date and time that the file was generated. You can also add your own notes to further categorize your results with theaddNoteskwarg.Additionally, you can set the kwargsaveAsMDasTrue(default isFalse) to create a Github-flavored markdown version of thisnotes.txtfiles, which will be saved as aREADME.mdfile to allow for seamless viewing.
If these figures are to be pushed to your project repository, simply navigate to thefigures/folder and the corresponding subfolder and your results will be automatically presented on the Github subfolder page thanks to the magic of Github-flavored markdown.
I prefer this option as it will also automatically add HTML figure blocks with all of the figures in the corresponding subfolder. As an example, see the screen grab of theREADME.mdfile created for the above code whensaveAsMD=True.These README files can be edited directly as well to include things like figure captions.For either option, if you wish to run an additional trial and have those results saved in the same folder you can specify thesubFolderNameto the previously generated subfolder and (1) the figures will be saved there with the number identifier changed (generally, plus 1) to denote a new trial and (2) thenotes.txtorREADME.mdfile will beappendedto include the new information.To do this, you will also want to recover the path to the previous subfolder. To do this, use the kwargreturnPath=Truein the firstsave_figuresinstance which will return thePath(frompathlibpackage) of the folder, which we will designate asfigPath.
Then use the kwargsubFolderName=figPath.stem.fromdanpy.useful_functionsimportsave_figuresimportmatplotlib.pyplotaspltimportnumpyasnpdt=0.01signalAmplitude=1signalFrequency=0.5# HzdurationOfSignal=10# secondsparams={"Time Step":dt,"Signal Amplitude":signalAmplitude,"Signal Frequency":signalFrequency,"Duration Of Signal":durationOfSignal}time=np.arange(0,durationOfSignal+dt,dt)signal1=signalAmplitude*np.sin(2*np.pi*signalFrequency*time)signal2=signalAmplitude*np.cos(2*np.pi*signalFrequency*time)fig1=plt.figure()ax1=plt.gca()ax1.plot(time,signal1)ax1.set_xlabel("Time (s)")ax1.set_ylabel("Signal 1")fig2=plt.figure()ax2=plt.gca()ax2.plot(time,signal2)ax2.set_xlabel("Time (s)")ax2.set_ylabel("Signal 2")# save first set of figuresfigPath=save_figures("figures/","Signal_figures",params,returnPath=True)plt.close('all')fig3=plt.figure()ax1=plt.gca()ax1.plot(time,signal1+signal2)ax1.set_xlabel("Time (s)")ax1.set_ylabel("Signal 1 + Signal 2")fig4=plt.figure()ax2=plt.gca()ax2.plot(time,signal1-signal2)ax2.set_xlabel("Time (s)")ax2.set_ylabel("Signal 1 - Signal 2")# save next set of figuressave_figures("figures/","Signal_figures",params,subFolderName=figPath.stem)plt.show()kwargsforsave_figuresThere are multiple ways to adjust the behavior of this function withkwargs. Specifically,fileType(default is "png") - You can change the file extension to any of the following: "eps", "pdf", "pgf", "png", "ps", "raw", "rgba", "svg", "svgz". Note that saving them as "pdf" will save each figure individually, where thekwargssaveAsPDFwill combine them into a single file.subFolderName(default is the time stamped folder name) - Sometimes it is more convenient to name the subfolder yourselforto send files to a previously defined location. In this case, just specify thesubFolderNameand it will send the figures there. Note that this should only be done for trials where the parametersdo not changeas thenotes.txtfiles is only generated when the subfolder is created.addNotes(defaultNone) - This will allow for notes to be added to thenotes.txtorREADME.mdfiles directly from the function call. Must be astr.saveAsMD(defaultFalse) - If true, the notes will be saved in Github-flavored markdown which includes figure displays in addition to notes and parameters. This can be viewed directly from the figure folder once uploaded to your projects repository or locally with a markdown previewer (like Atom).saveAsPDF(defaultFalse) - If true, in addition to saving the figures as the specified filetype, a single PDF will be constructed to include all figures generated.returnPath(defaultFalse) - If you intend on saving additional figures to the folder location, this option allows you to return the path of the subfolder. Use this for the first trial and thesubFolderNameargument for any additional trials to send new figures to the same location. Note that this returns aPathitem from thepathlibpackage, which means that the subfolder name can be recovered by the command<figure subfolder path>.stemfigs(default will save every figure open) - You can alternatively specify the specific figures that you wish to save. This should be a list of figures.Simpletimerclass that allows you to keep track of function run/loop times.This class creates a timer that allows you to keep track of how long loops take to run. This can be particularly helpful if you are running many large loops with irregular run times and you want to keep track of how long your code has been running.To use this class, simply instantiate it withmyTimer = timer()and then print updates after each loop with a variety of commands depending on what you are doing.As an example consider the code snippet belowfromdanpy.useful_functionsimporttimerimporttimemyTimer=timer()myTimer.start()foriinrange(5):print("Trial "+str(i+1)+"\n")time.sleep(5)myTimer.loop()Which prints the following output.Trial 1Total Run Time: 00:00:05 (00:00:05)Trial 2Total Run Time: 00:00:10 (00:00:05)Trial 3Total Run Time: 00:00:15 (00:00:05)Trial 4Total Run Time: 00:00:20 (00:00:05)Trial 5Total Run Time: 00:00:25 (00:00:05)Alternatively, you can choose to only print the total run time of your code by starting the timer at the beginning and then ending it at the bottom of your code with the commandmyTimer.end(), which will print the total run time as well. If this is the case, you can select the arg--single_trialwhen the timer is initialized and the displays will adjust to only display the total run time (i.e.,myTimer = timer('--single_trial')). Note, however, that you cannot use theloopfunction in single trial mode.If instead you wish to display or save the run time, the output of these functions can be suppressed by the kwargverbose=0. The value of thetotalRunTimecan then be recovered as an attribute of the timer (i.e.,myTimer.totalRunTime). Similarly, the trial run time can always be recovered by the attributetrialRunTime. And lastly, the formatedstrversion of these values can be recovered by the attributestotalRunTimeStrandtrailRunTimeStr, respectively.You can always reset your timer with the functionresetas well, in case you ever needed to run back to back timers (but be sure to use--single_trialagain if you want to continue using that option).fromdanpy.useful_functionsimporttimerimporttimemyTimer=timer()print("First Timer\n")myTimer.start()foriinrange(5):print("Trial "+str(i+1)+"\n")time.sleep(5)myTimer.loop()print("Second Timer\n")myTimer.reset()foriinrange(5):print("Trial "+str(i+1)+"\n")time.sleep(5)myTimer.loop()Simple test function for asserting a variable is, in fact, a number.In order to quickly test if a variable is a number (and not astrorbool), simply typeis_number(variable,variableName)as such:fromdanpy.useful_functionimportis_numbermyVariable=3.1415926536is_number(myVariable,"myVariable")This will not raise an AssertionError asmyVariableis a number. To make the debugging easier to locate, the additional argumentsdefaultandnoteshave been added.fromdanpy.useful_functionsimportis_numbermyVariable="Not a number"defaultValue=3.1415926536notes="This is where I would put notes pertaining to the variable like relative magnitude, units, etc."is_number(myVariable,"myVariable",default=defaultValue,notes=notes)which will raise theAssertionError:AssertionError: myVariable must be an int, float, float32, float64, or numpy.float not <class 'str'>. Default is 3.1415926536. This is where I would put notes pertaining to the variable like relative magnitude, units, etc.