def plot_diffs(dataset,repeatlist,labels=None,outfolder=None,nsigma_crange=1,fignum=None,figsize=(9.6,6.7)): """Generate comparison and difference plots for a list of datasets (data,x,y) and a list of indices `repeatlist` as (index1,index2,label). An optional list of `labels` matching data in repeatlist can be provided, and then are used for titles of plots, otherwise numeric indices are simply used.""" stats=[] result=[] #plot surface map, who knows why on figure 5 fignumber(fignum,figsize=figsize) for i1,i2,t in repeatlist: d1,d2=dataset[i1],dataset[i2] diff=d1[0]-d2[0] result.append((diff,d1[1],d1[2])) #mng = plt.get_current_fig_manager() #mng.window.showMaximized() plt.clf() ax1,ax2,ax3=diff_images(d1[0],d2[0],d2[1],d2[2],fignum=0) #x and y are taken from second data if labels is not None: ax1.set_title(labels[i1]) ax2.set_title(labels[i2]) stats.append([labels[i1],labels[i2],np.nanstd(diff)]) else: stats.append([i1,i2,np.nanstd(diff)]) ax3.set_title('Diff, rms= %5.3f nm'%(np.nanstd(diff)*1000) ) plt.sca(ax3) plt.clim(*filtered_span(diff,nsigma=nsigma_crange,itmax=1,span=True)) ax1.set_aspect('equal') ax2.set_aspect('equal') ax3.set_aspect('equal') plt.suptitle(t) plt.tight_layout() if outfolder: #pdb.set_trace() plt.savefig(os.path.join(outfolder,fn_add_subfix("diff","_%i_%i"%(i1,i2),'.jpg'))) save_data(os.path.join(outfolder,fn_add_subfix("diff","_%i_%i"%(i1,i2),'.dat')), diff,d1[1],d1[2]) if outfolder: np.savetxt(os.path.join(outfolder,"diff_report.txt"),result,fmt="%s") return result
def getdataset(datalist,outfolder=None,fignum=None,figsize=(9.6,6.7),levelingfunc=None, caldata=None,psdrange=[1e-15,1e-6]): """for each file in a list read data and make a preview plot using `plot_surface_analysis`, return a list of (data,x,y). caldata are optional calibration data of same shape as data.""" """moved here from newview_plotter. This is a similar function to calibrate samples, """ if levelingfunc==None: levelingfunc= lambda x: x result=[] if outfolder is not None: os.makedirs(outfolder,exist_ok=True) fig=fignumber(fignum) for ff in datalist: plt.clf() wdata,x,y=matrixZygo_reader(ff,scale=(1000.,1000,1.),center=(0,0)) if caldata is not None: wdata=wdata-caldata wdata,x,y=levelingfunc(wdata),x,y #simple way to remove plane plot_surface_analysis(wdata,x,y,label=os.path.basename(ff),outfolder=outfolder,nsigma_crange=1, fignum=(fignum+1 if fignum else None),psdrange=psdrange,levelingfunc=levelingfunc,frange=[4,100]) #[1e-11,1e-6] maximize() result.append((wdata,x,y)) return result
def calculatePSD2(wdata,xg,yg,outname="",wfun=None,vrange=[None,None],rmsrange=None,prange=None,fignum=1,misal_deg=(1,1),leg_deg=(10,10)): """Updated version with subtracted 4 terms legendre.""" """given points w, calculate and plot surface maps with different leveling (piston, tilt, sag, 10 legendre) use psd2d to calculate and save x and y 2d PSDs, plots only y. fignum window where to plot, if fignnum is 0 current figure is cleared, if None new figure is created. Default to figure 1 .""" ##STILL AWFUL CODE # misal_deg is useless, since data are expected to be already leveled. # if not, still can be applied, but is quite useless. ###AWFUL CODE, partially fixed -> fix plotting window ## FOUR PANEL PLOT save as _lev.png, _piston.dat, _tilt.dat, _sag.dat ## on fignum fig=fignumber(fignum) plt.clf() figManager = plt.get_current_fig_manager() figManager.window.showMaximized() # wdata=-fit.legendre2d(wdata,1,1)[0] lwdata=wdata-fit.legendre2d(wdata,*misal_deg)[0] lwdata2=wdata-fit.legendre2d(wdata,*leg_deg)[0] # #make plots ax=compare_images([wdata,lwdata,lwdata2],xg,yg,titles=['original','cone corrected','10 legendre 2D corrected'],fignum=0,commonscale=True,vmin=vrange[0],vmax=vrange[1]) #generator of axis, not sure how it can work below, missing something? for im in ax: plt.xlabel('X (mm)') plt.ylabel('Y (mm)') # ##return wdata,lwdata,lwdata2 wdata,lwdata,lwdata2=leveldata(wdata,xg,yg) #calculate and make a plot with 3 panels if outname: np.savetxt(fn_add_subfix(outname,'_piston','.dat'),wdata) #doesn't write x and y np.savetxt(fn_add_subfix(outname,'_%2i_%2i_misal'%misal_deg,'.dat'),lwdata) np.savetxt(fn_add_subfix(outname,'_%2i_%2i_leg'%leg_deg,'.dat'),lwdata2) leg10=wdata-fit.legendre2d(wdata,*leg_deg)[0] #forth panel contains legendre plt.subplot(224) plt.imshow(leg10,extent=(xg[0],xg[-1],yg[0],yg[-1]), interpolation='None',aspect='auto') plt.title('(%i,%i) 2D legendre removed'%leg_deg) plt.colorbar() #display(plt.gcf()) if outname: plt.savefig(fn_add_subfix(outname,'_lev','.png')) ## DATA OUTPUT save as _psd.dat, psd2d creates #create packed data for txt output #containing x and y psds, with 3 different levelings #resulting in 6 couples freq/psd. # matches the order specified in header #header for final matrix output, define labels of psd sequence head='yfreq ypsd xfreq xpsd ylevfreq ylevpsd xlevfreq xlevpsd ysagfreq ysagpsd xsagfreq xsagpsd' #this assumes x and y are same xg and yg on all datalist #change this part, we are no more interested in doing psd on all possible leveling, only 4 terms removed by line # along the direction of interest, however to keep constant number of columns, replace piston, tilt, sag # with tilt, sag, 4 legendre datalist=[wdata,lwdata,lwdata2] labels=['_tilt','_mis','_leg'] #postfix for output file flist=[] psdlist=[] for d,l in zip(datalist,labels): plt.figure(3) #x and y psds fx,px=psd2d(d.T,yg,xg,wfun=wfun) #psds along x, no plot fy,py=plot_psd2d(d,xg,yg,outname=fn_add_subfix(outname,l),wfun=wfun, rmsrange=rmsrange,prange=prange,vrange=vrange) #psds along y, plot #display(plt.gcf()) #note y goes first flist.extend([fy,fx]) psdlist.extend([py,px]) avgpsdlist=[avgpsd2d(p) for p in psdlist] #generate output array and save it with header outarr=np.empty((np.max([len(f) for f in flist]),len(psdlist)*2))*np.nan for i,(f,p) in enumerate(zip(flist,avgpsdlist)): outarr[:len(f),i*2]=f outarr[:len(p),i*2+1]=p if outname: np.savetxt(fn_add_subfix(outname,'_psd','.dat'),outarr,header=head,fmt='%f') ## PLOT AVG PSDs save as _psds.png fw1,fw1m,fw2,fw2m,fw3,fw3m=flist #used only in plot pw1avg,pw1mavg,pw2avg,pw2mavg,pw3avg,pw3mavg=avgpsdlist #average psds plt.figure(2) plt.clf() plt.plot(fw1,pw1avg,label='Y') plt.plot(fw2,pw2avg,label='Y lev.') plt.plot(fw3,pw3avg,label='Y sag.') plt.plot(fw1m,pw1mavg,'--',label='X') plt.plot(fw2m,pw2mavg,'--',label='X lev.') plt.plot(fw3m,pw3mavg,'--',label='X sag.') plt.loglog() plt.grid() plt.legend(loc=0) if outname: plt.savefig(fn_add_subfix(outname,'_psds','.png')) return outarr
def psd2d_analysis(wdata,x,y,title=None,wfun=None,vrange=None, rmsrange=None,prange=None,fignum=5,rmsthr=None, aspect='auto', ax2f=None, units=None,outname=None,norm=1,rmsnorm=True): """ Calculates 2D PSD as image obtained combining all profile PSDS calculated along vertical slices of data. Resulting image has size If title is provided rms slice power is also calculated and plotted on three panels with figure and PSD. Return PSD as PSD2D object. uses plot_rms_power(f,p,rmsrange=None) to calculate rms power. fignum window where to plot, if fignnum is 0 current figure is cleared, if None new figure is created. Default to figure 5. rmsrange is one or a list of frequency ranges for plotting integrated rms. Can contain None to use max or min. If axf2 is set to boolean or array of boolean, plot slice rms for the frequencies associated to rmsrange on second axis. rmsrange must be set accordingly (same number of elements). rmsthr sets a threshold for data inclusion. If rms is above the value, the line is considered to contain invalid data and is removed from output PSD. This makes it easy to average the returned PSDs. Corresponding data are still visualized in central panel, but are marked with a red cross at top of y axes. If multiple rms range intervals are provided, line is removed if any of them is over the threshold, but this might change in future, e.g. TODO: make it possible to add vector threshold with as many elements as rms range intervals. Set outname to empty string to plot without generating output. typical values: rmsrange=[[None,0.1],[0.1,0.2],[0.2,1],[1,None]] #list of frequency intervals for rms ax2f=[0,1,1,0,0] # axis where to plot rms corresponding to intervals in rmsrange: 1 right, 0 left wfun = np.hanning # type of windows for fourier transform units = ("mm","mm","$\mu$m") # units of surface data from which PSD is calculated vrange_surf=([-0.5,0.5]) #color scale of surface map vrange_leg=([-0.05,0.05]) #color scale of legendre removed map prange=np.array((1e-8,1.e-1))#np.array((5e-8,1.e-5)) #color scale of 2d psd plot fs,ps=psd2d_analysis(levellegendre(y,wdata,2),x,y,outname="",wfun=np.hanning, rmsrange=rmsrange,prange=prange,ax2f=ax2f,units=units) """ #2018/04/05 # -critical renaming of plot_psd2d(wdata,x,y..) -> psd2d_analysis # and _plot_psd2d(f,p,x) -> plot_psd2d consistently with other routines. # -added title to determine if generating plot, with the intent of replacing outname. plt.pause(1) #print('cane',fignum) if outname is not None: #handle backcompatibility print ('psd2d_analysis WARNING: `title` replaced `outname` and output figure will be no more generated.'+ 'OUTNAME will be removed in next version, use title and save the plot after returning from routine.') if title is not None: print('outname should be not used together with title, it will be ignored.') else: title=outname if vrange is not None: if prange is None: print("""WARNING: older versions of psd2d use vrange as range of psdvalue. In updated version prange is range of psd and vrange is range for surface plots. Update your code.""") #check compatibility with old interface if len(wdata.shape)==1: if len(y.shape)==2: print("WARNING: your code calling psd2d uses old call") print("psd2d(x,y,data), automaticly adjusted, but") print("update IMMEDIATELY your code to call") print("psd2d(data,x,y)") x,y,wdata=wdata,x,y f, p = psd2d(wdata, x, y, wfun=wfun, norm=norm, rmsnorm=rmsnorm) # calculate PSD 2D # GENERATE OUTPUT if title is not None: if np.size(units)==1: units=np.repeat(units,3) if prange is None: if f[0] == 0: prange=span(p[1:,:]) else: prange=span(p) if prange[0]<1e-20: #arbitrary small value print("WARNING: low limit detected in prange, can cause problems with log color scale.") if vrange is None: vrange=span(wdata) #plot surface map, who knows why on figure 5 fig=fignumber(fignum) #pdb.set_trace() # plt.clf() maximize() plt.draw() plt.suptitle(title) ################ #plot Surface ax1=plt.subplot(311) plot_data(wdata,x,y,aspect=aspect,vmin=vrange[0],vmax=vrange[1],units=units,title='Surface',stats=True) plt.xlabel(None) plt.grid(1) ################ #plot 2D PSD ax2=plt.subplot(312,sharex=ax1) plot_psd2d(f,p,x,prange=prange,units=units) if rmsrange is not None: #plot horizontal lines to mark frequency ranges for rms extraction, #rms is tranformed to 2D array if len(np.array(rmsrange).shape)==1: #if only one interval, make it 2D anyway rmsrange=[rmsrange] rmsrange=np.array(rmsrange) for fr in np.array(rmsrange): #I want fr to be array type, even if I don't care about rmsrange this does the job ax2.hlines(fr[fr != None],*((span(x)-x.mean())*1.1+x.mean())) ax2.set_xlabel("") plt.xlabel(None) #plt.subplots_adjust(top=0.85) ################ #plot rms slice ax3=plt.subplot(313,sharex=ax1) if f[0]==0: #ignore component of 0 frequency in rms calculation if present ff,pp=f[1:],p[1:] else: ff,pp=f,p rms=plot_rms_power(ff,pp,x,rmsrange=rmsrange,ax2f=ax2f,units=units) #pdb.set_trace() mask=np.isfinite(rms) #True if good. it is an array if rmsthr is not None: mask = mask & (rms < rmsthr) ax3.hlines(rmsthr,*ax2.get_xlim()) #plot markers on rms chart ax2.plot(x[~mask],np.repeat(ax2.get_ylim()[1],len(x[~mask])),'rx') #plot markers on psd2d chart #pdb.set_trace() #questa era qui, ma dava errore perche' mask e' lineare #mask = np.all(mask,axis =0) # if any of mask is False -> False p[:,~mask]=np.nan ax3.grid(1) #plt.tight_layout(rect=(0, 0.03, 1, 0.95) if title else (0, 0, 1, 1)) # replaced with more evolved interactive resize: # plt.colorbar().remove() #dirty trick to adjust size to other panels def resize(event): box1 = ax1.get_position() box2 = ax2.get_position() box3 = ax3.get_position() ax2.set_position([box1.x0, box2.y0, box1.width , box2.height]) ax2.set_adjustable("box",share=True) ax3.set_position([box1.x0, box3.y0, box1.width , box3.height]) ax3.set_adjustable("box",share=True) cid = fig.canvas.mpl_connect('draw_event', resize) cid2 = fig.canvas.mpl_connect('resize_event', resize) resize(None) #plt.tight_layout() if outname: #kept for compatibility, will be removed in next version plt.savefig(fn_add_subfix(outname,'_2dpsd','.png')) return f,p
def xdiff_images(data1, data2, x=None, y=None, fignum=None, titles=None, vmin=None, vmax=None, commonscale=False, direction=0, *args, **kwargs): """plots two data sets with common axis and their difference. Return the three axis. Colorbars are formatted to be same height as plot. """ fig = fignumber(fignum) plt.clf() ax = None aspect = kwargs.pop('aspect', 'auto') #this is to set scale if not fixed d1std = [np.nanstd(data) for data in (data1, data2)] std = min(d1std) if x is None: x = np.arange(data1.shape[1]) if y is None: y = np.arange(data1.shape[0]) ax1 = plt.subplot(131, sharex=ax, sharey=ax) s = (std if commonscale else d1std[0]) d1mean = np.nanmean(data1) axim = plt.imshow(data1, extent=np.hstack([span(x), span(y)]), interpolation='None', aspect=aspect, vmin=kwargs.get('vmin', d1mean - s), vmax=kwargs.get('vmax', d1mean + s), *args, **kwargs) plt.colorbar(fraction=0.046, pad=0.04) ax2 = plt.subplot(132, sharex=ax, sharey=ax) s = (std if commonscale else d1std[1]) d2mean = np.nanmean(data2) axim = plt.imshow(data2, extent=np.hstack([span(x), span(y)]), interpolation='None', aspect=aspect, vmin=kwargs.get('vmin', d2mean - s), vmax=kwargs.get('vmax', d2mean + s), *args, **kwargs) plt.colorbar(fraction=0.046, pad=0.04) ax3 = plt.subplot(133, sharex=ax, sharey=ax) plt.title('Difference (2-1)') diff = data2 - data1 axim = plt.imshow(diff, extent=np.hstack([span(x), span(y)]), interpolation='None', aspect=aspect, *args, **kwargs) plt.colorbar(fraction=0.046, pad=0.04) axes = [ax1, ax2, ax3] if titles is not None: for ax, tit in zip(axes, titles): if tit is not None: ax.set_title(tit) return axes
def diff_images(data1, data2, x=None, y=None, fignum=None, titles=None, vmin=None, vmax=None, commonscale=False, direction=0, *args, **kwargs): """plots two data sets with common axis and their difference. Return the three axis. Colorbars are formatted to be same height as plot. 2018/06/19 use data2D routines, allowing to add parameters (e.g. stats legend). """ fig = fignumber(fignum) plt.clf() aspect = kwargs.pop('aspect', 'auto') #this is to set scale if not fixed d1std = [np.nanstd(data) for data in (data1, data2)] std = min(d1std) if x is None: x = np.arange(data1.shape[1]) if y is None: y = np.arange(data1.shape[0]) ax1 = plt.subplot(131) s = (std if commonscale else d1std[0]) d1mean = np.nanmean(data1) plot_data(data1, x, y, aspect=aspect, vmin=kwargs.get('vmin', d1mean - s), vmax=kwargs.get('vmax', d1mean + s), *args, **kwargs) ax2 = plt.subplot(132, sharex=ax1, sharey=ax1) s = (std if commonscale else d1std[1]) d2mean = np.nanmean(data2) plot_data(data2, x, y, aspect=aspect, vmin=kwargs.get('vmin', d2mean - s), vmax=kwargs.get('vmax', d2mean + s), *args, **kwargs) ax3 = plt.subplot(133, sharex=ax1, sharey=ax1) plt.title('Difference (2-1)') diff = data2 - data1 plot_data(diff, x, y, aspect=aspect, *args, **kwargs) axes = [ax1, ax2, ax3] if titles is not None: for ax, tit in zip(axes, titles): if tit is not None: ax.set_title(tit) return axes
def compare_images(datalist, x=None, y=None, fignum=None, titles=None, vmin=None, vmax=None, commonscale=False, axis=0, axmax=0, *args, **kwargs): """return a generator that plots n images in a list in subplots with shared zoom and axes. datalist is a list of data in format (data, x, y). x and y provide plot range (temporarily, ideally want to be able to plot same scale). fignum window where to plot, if fignnum is 0 current figure is cleared, if None new figure is created. axis defines the axis with larger number of plots (default ncols >= nrows) axmax maximum nr of plots along shorter dim. """ #modified again 2018/06/05 to accept list of data,x,y triplets. x and y are # accepted as range. # old docstring was: # return a generator that plots n images in a list in subplots with shared # zoom and axes. datalist is a list of 2d data on same x and y. # fignum window where to plot, if fignnum is 0 current figure is cleared, # if None new figure is created. # this was changed a couple of times in interface, for example in passing # ((data,x,y),...) rather than (data1,data2),x,y # it can be tricky in a general way if data don't have all same size, # at the moment it is assumed they have. # in any case x and y are used only to generate the extent, # that is then adapted to the size of data. gridsize = find_grid_size(len(datalist), axmax) if axis == 1: gridsize = gridsize[::-1] fig = fignumber(fignum) plt.clf() ax = None # this is to set scale if not fixed d1std = [np.nanstd(data[0]) for data in datalist] std = min(d1std) for i, d in enumerate(datalist): """adjust to possible input formats""" data, x, y = d if x is None: x = np.arange(data.shape[1]) if y is None: y = np.arange(data.shape[0]) ax = plt.subplot(gridsize[0], gridsize[1], i + 1, sharex=ax, sharey=ax) if titles is not None: print("titles is not none, it is ", titles) plt.title(titles[i]) # plt.xlabel('X (mm)') # plt.ylabel('Y (mm)') s = (std if commonscale else d1std[i]) d1mean = np.nanmean(data) aspect = kwargs.pop('aspect', None) axim = plt.imshow(data, extent=np.hstack([span(x), span(y)]), interpolation='none', aspect=aspect, vmin=kwargs.get('vmin', d1mean - s), vmax=kwargs.get('vmax', d1mean + s), *args, **kwargs) plt.colorbar() yield ax
def psd_variability(psdlist,psdrange=None,rmsrange=None,fignum=None,units=None, outname=None,figsize=(15,5)): # to be removed """given a list of 2d psd files plot spans of all of them on same plot. TODO: replace input using data rather than files. Move to 2DPSD""" from plotting.fignumber import fignumber from pySurf.data2D import projection from pySurf.psd2d import rms_power from scipy.stats import ttest_ind units=['mm','mm','$\mu$m'] labels = [p.name for p in psdlist] #gestione rudimentale dell'output troppo stanco per pensare allo standard. if outname is not None: label=os.path.basename(outname) outfolder=os.path.dirname(outname) os.makedirs(outfolder,exist_ok=True) rms_v=[] fig=fignumber(fignum,figsize=figsize) plt.subplot(121) for (p,x,f),fn in zip(psdlist,labels): ps=projection(p,axis=1,span=True) rms_v.append(rms_power(f,p,rmsrange)) c=plt.plot(f,ps[0],label=os.path.basename(fn)+' AVG')[0].get_color() plt.plot(f,ps[1],'--',label='point-wise min',c=c) plt.plot(f,ps[2],'--',label='point-wise max',c=c) #pdb.set_trace() plt.ylabel('axial PSD ('+units[2]+'$^2$ '+units[1]+')') plt.xlabel('Freq. ('+units[1]+'$^{-1}$)') plt.vlines(rmsrange,*plt.ylim(),label='rms range',linestyle=':') plt.ylim(psdrange) plt.loglog() plt.grid(1) plt.legend(loc=0) print("Statistics for %s, rms range [%6.3g,%6.3g]:"%(label,*rmsrange)) for rms,fn in zip(rms_v,labels): print(os.path.basename(fn)+": rms=%6.3g +/- %6.3g (evaluated on %i profiles)"% (np.nanmean(rms),np.nanstd(rms),len(rms))) print ("--------------") print ("Distribution of rms: %6.3g +/- %6.3g"%(np.nanmean(rms_v,axis=1).mean(),np.nanmean(rms_v,axis=1).std())) print ("AGGREGATED: rms=%6.3g +/- %6.3g (evaluated on %i profiles)"% (np.nanmean(rms_v),np.nanstd(rms_v),np.size(rms_v))) if len(rms_v)==2: print ("Student T test t:%6.3g p:%6.3g"%ttest_ind(rms_v[0],rms_v[1])) l=[os.path.splitext(os.path.basename(f))[0]+" rms=%3.2g +/- %3.2g (N=%i)"% (np.nanmean(r),np.nanstd(r),len(r)) for f,r in zip(labels,rms_v)] plt.subplot(122) plt.hist([rr[np.isfinite(rr)] for rr in rms_v],density=True,bins=100,label=l) plt.legend(loc=0) plt.title(label+" distrib. of avg: %3.2g +/- %3.2g"% (np.nanmean(rms_v,axis=1).mean(),np.nanmean(rms_v,axis=1).std())) #handles = [Rectangle((0,0),1,1,color=c,ec="k") for c in [low,medium, high]] #labels= ["low","medium", "high"] #plt.legend(handles, labels) plt.tight_layout() if outname is not None: plt.savefig(fn_add_subfix(outname,'_stats','.jpg')) return rms_v