def fsolve(func, x0, args=(), fprime=None, full_output=0, col_deriv=0, xtol=1.49012e-08, maxfev=0, band=None, epsfcn=0.0, factor=100, diag=None): ''' Wrapped scipy.optimize.fsolve to handle units Presumably func and x0 have units associated with them. We simply wrap the function, ''' def wrappedfunc(x, *args): x = Unit(x, x0.exponents, x0.label) return float(func(x)) if full_output: x, infodict, ier, mesg = _fsolve(wrappedfunc, float(x0), args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag) x = Unit(x, x0.exponents, x0.label) return x, infodict, ier, mesg else: x, = _fsolve(wrappedfunc, float(x0), args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag) x = Unit(x, x0.exponents, x0.label) return x
def fsolve(func, t0, args=(), fprime=None, full_output=0, col_deriv=0, xtol=1.49012e-08, maxfev=0, band=None, epsfcn=0.0, factor=100, diag=None): """wrapped fsolve command to work with units. We get the units on the function argument, then wrap the function so we can add units to the argument and return floats. Finally we call the original fsolve from scipy. """ try: # units on initial guess, normalized tU = [t / float(t) for t in t0] except TypeError: tU = t0 / float(t0) def wrapped_func(t, *args): 't will be unitless, so we add unit to it. t * tU has units.' try: T = [x1 * x2 for x1, x2 in zip(t, tU)] except TypeError: T = t * tU try: return [float(x) for x in func(T, *args)] except TypeError: return float(func(T)) sol = _fsolve(wrapped_func, t0, args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag) if full_output: x, infodict, ier, mesg = sol try: x = [x1 * x2 for x1, x2 in zip(x, tU)] except TypeError: x = x * tU return x, infodict, ier, mesg else: try: x = [x1 * x2 for x1, x2 in zip(sol, tU)] except TypeError: x = sol * tU return x
def wave_number(f, h, rho=1025, g=9.80665): """ Calculates wave number To compute wave number from angular frequency (w), convert w to f before using this function (f = w/2*pi) Parameters ----------- f: numpy array Frequency [Hz] h: float Water depth [m] rho: float (optional) Water density [kg/m^3] g: float (optional) Gravitational acceleration [m/s^2] Returns ------- k: pandas DataFrame Wave number [1/m] indexed by frequency [Hz] """ try: f = np.array(f) except: pass assert isinstance(f, np.ndarray), 'f must be of type np.ndarray' assert isinstance(h, (int, float)), 'h must be of type int or float' assert isinstance(rho, (int, float)), 'rho must be of type int or float' assert isinstance(g, (int, float)), 'g must be of type int or float' w = 2 * np.pi * f # angular frequency xi = w / np.sqrt(g / h) # note: =h*wa/sqrt(h*g/h) yi = xi * xi / np.power(1.0 - np.exp(-np.power(xi, 2.4908)), 0.4015) k0 = yi / h # Initial guess without current-wave interaction # Eq 11 in IEC 62600-101 using initial guess from Guo (2002) def func(kk): val = np.power(w, 2) - g * kk * np.tanh(kk * h) return val mask = np.abs(func(k0)) > 1e-9 if mask.sum() > 0: k0_mask = k0[mask] w = w[mask] k, info, ier, mesg = _fsolve(func, k0_mask, full_output=True) assert ier == 1, 'Wave number not found. ' + mesg k0[mask] = k k = pd.DataFrame(k0, index=f, columns=['k']) return k
def nsolve(objective, x0, *args, **kwargs): """A Wrapped version of scipy.optimize.fsolve. objective: a callable function f(x) = 0 x0: the initial guess for the solution. This version warns you if the call did not finish cleanly and prints the message. Returns: If there is only one result it returns a float, otherwise it returns an array. """ if 'full_output' not in kwargs: kwargs['full_output'] = 1 ans, info, flag, msg = _fsolve(objective, x0, *args, **kwargs) if flag != 1: raise Exception('nsolve did not finish cleanly: {}'.format(msg)) if len(ans) == 1: return float(ans) else: return ans
def kappa_from_fofrh_and_sizedist(f_of_RH, dist, wavelength, RH, verbose = False, f_of_RH_collumn = None): """ Calculates kappa from f of RH and a size distribution. Parameters ---------- f_of_RH: TimeSeries dist: SizeDist wavelength: float RH: float Relative humidity at which the f_of_RH is taken. column: string when f_of_RH has more than one collumn name the one to be used verbose: bool Returns ------- TimeSeries """ def minimize_this(gf, sr, f_rh_soll, ext, wavelength, verbose = False): gf = float(gf) sr_g = sr.apply_growth(gf, how='shift_bins') sr_g_opt = sr_g.calculate_optical_properties(wavelength) ext_g = sr_g_opt.extinction_coeff_sum_along_d.data.values[0][0] f_RH = ext_g / ext out = f_RH - f_rh_soll if verbose: print('test growhtfactor: %s'%gf) print('f of RH soll/is: %s/%s'%(f_rh_soll,f_RH)) print('diviation from soll: %s'%out) print('---------') return out # make sure f_of_RH has only one collumn if f_of_RH.data.shape[1] > 1: if not f_of_RH_collumn: txt = 'f_of_RH has multiple collumns (%s). Please name the one you want to use by setting the f_of_RH_collumn argument.'%(f_of_RH.data.columns) raise ValueError(txt) else: f_of_RH = f_of_RH._del_all_columns_but(f_of_RH_collumn) n_values = dist.data.shape[0] gf_calc = _np.zeros(n_values) kappa_calc = _np.zeros(n_values) f_of_RH_aligned = f_of_RH.align_to(dist) for e in range(n_values): frhsoll = f_of_RH_aligned.data.values[e][0] if _np.isnan(frhsoll): kappa_calc[e] = _np.nan gf_calc[e] = _np.nan continue if type(dist.index_of_refraction).__name__ == 'float': ior = dist.index_of_refraction else: ior = dist.index_of_refraction.iloc[e][0] if _np.isnan(ior): kappa_calc[e] = _np.nan gf_calc[e] = _np.nan continue sr = dist.copy() sr.data = sr.data.iloc[[e],:] sr.index_of_refraction = ior sr_opt = sr.calculate_optical_properties(wavelength) ext = sr_opt.extinction_coeff_sum_along_d.data.values[0][0] if ext == 0: kappa_calc[e] = _np.nan gf_calc[e] = _np.nan continue if verbose: print('goal for f_rh: %s'%frhsoll) print('=======') gf_out = _fsolve(minimize_this, 1, args = (sr, frhsoll, ext, wavelength, verbose), factor=0.5, xtol = 0.005) gf_calc[e] = gf_out if verbose: print('resulting gf: %s'%gf_out) print('=======\n') kappa_calc[e] = kappa_simple(gf_out, RH, inverse=True) ts_kappa = _timeseries.TimeSeries(_pd.DataFrame(kappa_calc, index = f_of_RH_aligned.data.index, columns= ['kappa'])) ts_kappa._data_period = f_of_RH_aligned._data_period ts_kappa._y_label = '$\kappa$' ts_gf = _timeseries.TimeSeries(_pd.DataFrame(gf_calc, index = f_of_RH_aligned.data.index, columns= ['growth factor'])) ts_kappa._data_period = f_of_RH_aligned._data_period ts_gf._y_label = 'growth factor$' return ts_kappa, ts_gf
def kappa_from_fofrh_and_sizedist(f_of_RH, dist, wavelength, RH, verbose=False): """ Calculates kappa from f of RH and a size distribution. Parameters ---------- f_of_RH: TimeSeries dist: SizeDist wavelength: float RH: float Relative humidity at which the f_of_RH is taken. verbose: bool Returns ------- TimeSeries """ def minimize_this(gf, sr, f_rh_soll, ext, wavelength, verbose=False): gf = float(gf) sr_g = sr.apply_growth(gf, how='shift_bins') sr_g_opt = sr_g.calculate_optical_properties(wavelength) ext_g = sr_g_opt.extinction_coeff_sum_along_d.data.values[0][0] f_RH = ext_g / ext out = f_RH - f_rh_soll if verbose: print('test growhtfactor: %s' % gf) print('f of RH soll/is: %s/%s' % (f_rh_soll, f_RH)) print('diviation from soll: %s' % out) print('---------') return out n_values = dist.data.shape[0] gf_calc = _np.zeros(n_values) kappa_calc = _np.zeros(n_values) f_of_RH_aligned = f_of_RH.align_to(dist) for e in range(n_values): frhsoll = f_of_RH_aligned.data.values[e][0] if _np.isnan(frhsoll): kappa_calc[e] = _np.nan gf_calc[e] = _np.nan continue ior = dist.index_of_refraction.iloc[e][0] if _np.isnan(ior): kappa_calc[e] = _np.nan gf_calc[e] = _np.nan continue sr = dist.copy() sr.data = sr.data.iloc[[e], :] sr.index_of_refraction = ior sr_opt = sr.calculate_optical_properties(wavelength) ext = sr_opt.extinction_coeff_sum_along_d.data.values[0][0] if ext == 0: kappa_calc[e] = _np.nan gf_calc[e] = _np.nan continue if verbose: print('goal for f_rh: %s' % frhsoll) print('=======') gf_out = _fsolve(minimize_this, 1, args=(sr, frhsoll, ext, wavelength, verbose), factor=0.5, xtol=0.005) gf_calc[e] = gf_out if verbose: print('resulting gf: %s' % gf_out) print('=======\n') kappa_calc[e] = kappa_simple(gf_out, RH, inverse=True) ts_kappa = _timeseries.TimeSeries( _pd.DataFrame(kappa_calc, index=f_of_RH_aligned.data.index, columns=['kappa'])) ts_kappa._data_period = f_of_RH_aligned._data_period ts_kappa._y_label = '$\kappa$' ts_gf = _timeseries.TimeSeries( _pd.DataFrame(gf_calc, index=f_of_RH_aligned.data.index, columns=['growth factor'])) ts_kappa._data_period = f_of_RH_aligned._data_period ts_gf._y_label = 'growth factor$' return ts_kappa, ts_gf
def partition_with_curve(x, numpart, curve, method='brentq', return_exp=False, excluded=[]): """ Partition `x` in `numparts` parts following bpf x : float --> the value to partition numpart : int --> the number of partitions curve : bpf --> the curve to follow. It is not important over which interval x it is defined. The y coord defines the width of the partition (see example) return_exp : bool --> | False -> the return value is the list of the partitions | True -> the return value is a tuple containing the list of the partitions and the exponent of the weighting function Returns: the list of the partitions Example ======= # Partition the value 45 into 7 partitions following the given curve >>> import bpf4 as bpf >>> curve = bpf.halfcos2(0, 11, 1, 0.5, exp=0.5) >>> distr = partition_with_curve(45, 7, curve) >>> distr array([ 11. , 10.98316635, 10.4796218 , 7.89530421, 3.37336152, 0.76854613, 0.5 ]) >>> abs(sum(distr) - 45) < 0.001 True """ x0, x1 = curve.bounds() n = x def func(r): return sum((bpf.expon(x0, x0, x1, x1, exp=r)|curve).map(numpart)) - n try: if method == 'brentq': r = _brentq(func, x0, x1) curve = bpf.expon(x0, x0, x1, x1, exp=r)|curve parts = curve.map(numpart) elif method == 'fsolve': xs = np.linspace(x0, x1, 100) rs = [round(float(_fsolve(func, x)), 10) for x in xs] rs = set(r for r in rs if x0 <= r <= x1 and r not in excluded) parts = [] for r in rs: curve = bpf.expon(x0, x0, x1, x1, exp=r)|curve parts0 = curve.map(numpart) parts.extend(parts0) except ValueError: minvalue = curve(bpf.minimum(curve)) maxvalue = curve(bpf.maximum(curve)) if n/numpart < minvalue: s = """ no solution can be found for the given parameters. x is too small for the possible values given in the bpf, for this amount of partitions try either giving a bigger x, lowering the number of partitions or allowing smaller possible values in the bpf """ elif n/numpart > maxvalue: s = """ no solution can be found for the given parameters. x is too big for the possible values given in the bpf. try either giving a smaller x, increasing the number of partitions or allowing bigger possible values in the bpf """ else: s = """???""" ERROR['partition_with_curve.func'] = func raise ValueError(s) if abs(sum(parts) - n)/n > 0.001: print("Error exceeds threshold: ", parts, sum(parts)) if return_exp: return parts, r return parts
def run(self, field = 5.0, # V/cm temperature = 295.15, # K runtime = None, # seconds exposure = 0.5, # [0-1] plot = True, res = 200, # px/cm cursor_ovr = {'hover': False}, back_col = 0.3, band_col = 1, well_col = 0.05, noise = 0.015, detectlim = 0.04, interpol = 'linear', # 'cubic','nearest' dset_name = 'vertical', # 'horizontal' replNANs = True): # replace NANs by 'nearest' interpolation max_mob = 0 for i,lane in enumerate(self.samples): for j,frag in enumerate(lane): mob = size_to_mobility(len(frag), field, self.gel_concentration) # cm/s frag.mobility = mob self.samples[i][j] = frag max_mob = max((max_mob, mob)) # vWBR eq. parameters muL, muS, gamma mu = _np.zeros(100) for i, Li in enumerate(_np.linspace(100, 50000, 100)): mu[i] = size_to_mobility(Li, field, self.gel_concentration) muS0 = 3.5E-4 # cm^2/(V.sec) ############################################ muL0 = 1.0E-4 # cm^2/(V.sec) ############################################ gamma0 = 8000 # bp ############################################ vWBR = lambda L, muS, muL, gamma: (1/muS+(1/muL-1/muS)*(1-_np.exp(-L/gamma)))**-1 def residuals(pars, L, mu): return mu - vWBR(L, *pars) pars, cov, infodict, mesg, ier = _leastsq(residuals, [muS0, muL0, gamma0], args=(_np.linspace(100, 50000, 100), mu), full_output=True) muS, muL, gamma = pars time = runtime or (0.9 * self.gel_length)/max_mob # sec # Free solution mobility estimate space = _np.logspace(_np.log10(100), _np.log10(3000), 10) DNAspace_for_mu0 = _np.array([round(val, 0) for val in space]) Tvals = _np.unique(dset['T']) # (g/(100 mL))*100 # Mobility dependence on size (mu(L)) for each agarose percentage (Ti) ln_mu_LxT = [] for Lj in DNAspace_for_mu0: ln_mu_T = [] for Ti in Tvals: mu = size_to_mobility(Lj, field, Ti) ln_mu_T.append(_np.log(mu)) ln_mu_LxT.append(ln_mu_T) ln_mu_LxT = _np.array(ln_mu_LxT) # Linear regression for each DNA size lregr_stats = [] exclude = [] for l in range(len(DNAspace_for_mu0)): not_nan = _np.logical_not(_np.isnan(ln_mu_LxT[l])) if sum(not_nan) > 1: # (enough points for linear regression) gradient, intercept, r_value, p_value, std_err =\ _stats.linregress(Tvals[not_nan], ln_mu_LxT[l][not_nan]) lregr_stats.append((gradient, intercept, r_value, p_value, std_err)) exclude.append(False) else: exclude.append(True) exclude = _np.array(exclude) ln_mu_LxT = ln_mu_LxT[~exclude] if len(lregr_stats) > 0: # Free solution mobility determination ln_mu0 = _np.mean([row[1] for row in lregr_stats]) # mean of intercepts mu0 = _np.exp(ln_mu0) # cm^2/(V.seg) else: mu0 = None self.freesol_mob = mu0 eta = 2.414E-5*10**(247.8*(temperature-140)) # (Pa.s)=(kg/(m.s)) accurate to within 2.5% from 0 °C to 370 °C pore_size = lambda gamma, muL, mu0, lp, b: (gamma*muL*lp*b/mu0)**(1/2) pore_size_fit = lambda C: 143*C**(-0.59) a = pore_size(gamma, muL, mu0, lp, b) a_fit = pore_size_fit(self.gel_concentration) # ################################## a_fit = a_fit.to('m') # ################################## self.poresize = a self.poresize_fit = a_fit reduced_field = lambda eta, a, mu0, E, kB, T: eta*a**2*mu0*E/(kB*T) epsilon = reduced_field(eta, a, mu0, field*100, kB, temperature) # Diffusion coefficient of a blob Dblob = lambda kB, T, eta, a: kB*T/(eta*a) Db = Dblob(kB, temperature, eta, a) # Diffusion regime frontiers (in number of occupied pores) def diff_Zimm_Rouse(Nbp, args): kB, T, qeff, eta, mu0, a, b, l, lp = args Nbp = Nbp[0] L = Nbp * b Rg = radius_gyration(L, lp) D0 = free_solution(kB, T, eta, Rg) DRouse = Ogston_Rouse(Nbp, kB, T, a, eta, b, l) g = Zimm_g(Nbp, DRouse, qeff, mu0, kB, T) g = g.to_base_units() DZimm = Ogston_Zimm(D0, g) return DZimm - DRouse Zimm_Rouse = lambda x0, args: ( Nbp_to_N(_fsolve(diff_Zimm_Rouse, x0, args)[0],args[5], args[6], args[7])) equil_accel = lambda epsilon: epsilon**(-2/3) accel_plateau = lambda epsilon: epsilon**(-1) N_lim1 = accel_plateau(epsilon) # ################################# N_lim2 = equil_accel(epsilon) # # *** Major problem *** ## N_lim3 = Zimm_Rouse(2000, # ################################# [kB, temperature, qeff, eta, mu0, a, b, l, lp]) N_to_Nbp = lambda N, a, b, l: N*(l/b)*(a/l)**2 # number of base pairs (bp) self.accel_to_plateau = N_to_Nbp(N_lim1, a, b, l) self.equil_to_accel = N_to_Nbp(N_lim2, a, b, l) self.Zimm_to_Rouse = N_to_Nbp(N_lim3, a, b, l) for i,lane in enumerate(self.samples): for j,frag in enumerate(lane): Nbp = len(frag) N = Nbp_to_N(Nbp, a, b, l) if N < N_lim3: # Ogston-Zimm L = Nbp * b # (m) Rg = radius_gyration(L, lp) # (m) D0 = free_solution(kB, temperature, eta, Rg) # (m^2/s) DRouse = Ogston_Rouse(Nbp, kB, temperature, a, eta, b, l) # (m^2/s) g = Zimm_g(Nbp, DRouse, qeff, mu0, kB, temperature) # base D = Ogston_Zimm(D0, g) # unit elif N < N_lim2: # Rouse/Reptation-equilibrium D = Db/N**2 elif N > N_lim1: # Reptation-plateau (reptation with orientation) D = Db*epsilon**(3/2) else: # Accelerated-reptation D = Db*epsilon*N**(-1/2) frag.band_width = _np.sqrt(2*D*time) + self.welly self.samples[i][j]=frag # Total bandwidths time0 = self.welly/(mu0*field) bandwidths0 = [mobs*time0[l]*field for l, mobs in enumerate(mobilities)] # Max intensities raw_Is = [] maxI = Q_(-_np.inf, 'ng/cm') minI = Q_(_np.inf, 'ng/cm') for i,lane in enumerate(self.samples): lane_I = [] for j,frag in enumerate(lane): frag_Qty = quantities[i][j] frag_Wth = bandwidths[i][j] # w=FWHM or w=FWTM ??? if False: FWHM = Gauss_FWHM(frag_Wth) else: FWHM = frag_Wth std_dev = Gauss_dev(FWHM) auc = frag_Qty # area under curve proportional to DNA quantity Gauss_hgt = lambda auc, dev: auc/(dev*_np.sqrt(2*_np.pi)) frag_I = Gauss_hgt(auc, std_dev) # peak height if frag_I > maxI: maxI = frag_I if frag_I < minI: minI = frag_I lane_I.append(frag_I) raw_Is.append(lane_I) # max intensity normalization satI = maxI+exposure*(minI-maxI) intensities = [[(1-back_col)/satI*fragI for fragI in lane] for lane in raw_Is] self.intensities = intensities # Plot gel if plot: # Title mins, secs = divmod(time.to('s').magnitude, 60) # time is in secs hours, mins = divmod(mins, 60) hours = Q_(hours, 'hr') mins = Q_(mins, 'min') secs = Q_(secs, 's') title = ('E = %.2f V/cm\n' 'C = %.1f %%\n' 'T = %.2f K\n' 't = %d h %02d m\n' 'expo = %.1f' % (field, self.gel_concentration, temperature, hours, mins, exposure)) gel_width = len(samples) * (self.wellx + self.wellsep) + self.wellsep # cm pxl_x = int( gel_width * res) pxl_y = int( gel_len * res) lane_centers = [(l+1)*self.wellsep + sum(wellx[:l]) + 0.5*wellx[l] for l in range(nlanes)] rgb_arr = _np.zeros(shape=(pxl_y, pxl_x, 3), dtype=_np.float32) bandlengths = wellx bands_pxlXYmid = [] # Paint the bands for i in range(nlanes): distXmid = lane_centers[i] pxlXmid = int(round((distXmid * res).magnitude)) bandlength = bandlengths[i] from_x = int(round(((distXmid - bandlength/2.0) * res).magnitude)) to_x = int(round(((distXmid + bandlength/2.0) * res).magnitude)) bands_pxlXYmid.append([]) for j in range(len(lanes[i])): distYmid = distances[i][j] pxlYmid = int(round((distYmid * res).magnitude)) bands_pxlXYmid[i].append((pxlXmid, pxlYmid)) bandwidth = bandwidths[i][j] # w=FWHM or w=FWTM ??? if False: FWHM = Gauss_FWHM(bandwidth) else: FWHM = bandwidth std_dev = Gauss_dev(FWHM) maxI = intensities[i][j] midI = Gaussian(distYmid, maxI, distYmid, std_dev) if pxlYmid < len(rgb_arr): # band within gel frontiers rgb_arr[pxlYmid, from_x:to_x] += midI bckwdYstop = False if pxlYmid > 0 else True forwdYstop = False if pxlYmid < len(rgb_arr)-1 else True pxlYbck = pxlYmid-1 pxlYfor = pxlYmid+1 while not bckwdYstop or not forwdYstop: if not bckwdYstop: distYbck = Q_(pxlYbck, 'px')/res bckYI = Gaussian(distYbck, maxI, distYmid, std_dev) if pxlYbck < len(rgb_arr): rgb_arr[pxlYbck, from_x:to_x] += bckYI pxlYbck -= 1 if bckYI <= 1E-5 or pxlYbck == -1: bckwdYstop = True if not forwdYstop: distYfor = Q_(pxlYfor, 'px')/res forYI = Gaussian(distYfor, maxI, distYmid, std_dev) rgb_arr[pxlYfor, from_x:to_x] += forYI pxlYfor += 1 if forYI <= 1E-5 or pxlYfor == pxl_y: forwdYstop = True # Background color if noise is None or noise <= 0: rgb_arr += back_col else: bckg = _np.random.normal(back_col, noise, (len(rgb_arr), len(rgb_arr[0]))) rgb_arr += bckg[:, :, _np.newaxis] # Saturation rgb_arr[rgb_arr > 1] = 1 rgb_arr[rgb_arr < 0] = 0 # bands_arr = _np.ma.masked_where(rgb_arr == back_col, rgb_arr) ########### bands_arr = rgb_arr # Plot fig = _plt.figure() ax1 = fig.add_subplot(111, facecolor=str(back_col)) ax1.xaxis.tick_top() ax1.yaxis.set_ticks_position('left') ax1.spines['left'].set_position(('outward', 8)) ax1.spines['left'].set_bounds(0, gel_len) ax1.spines['right'].set_visible(False) ax1.spines['bottom'].set_visible(False) ax1.spines['top'].set_visible(False) ax1.spines['right'].set_color(str(back_col)) ax1.spines['bottom'].set_color(str(back_col)) ax1.xaxis.set_label_position('top') #_plt.xticks(lane_centers, names) majorLocator = _FixedLocator(list(range(int(gel_len+1)))) minorLocator = _FixedLocator([j/10.0 for k in range(0, int(gel_len+1)*10, 10) for j in range(1+k, 10+k, 1)]) ax1.yaxis.set_major_locator(majorLocator) ax1.yaxis.set_minor_locator(minorLocator) ax1.tick_params(axis='x', which='both', top='off') # Gel image bands_plt = ax1.imshow(bands_arr, extent=[0, gel_width, gel_len, 0], interpolation='none') # Draw wells for i in range(nlanes): ctr = lane_centers[i] wx = wellx[i] wy = welly[i] ax1.fill_between(x=[ctr-wx/2, ctr+wx/2], y1=[0, 0], y2=[-wy, -wy], color=str(well_col)) # Invisible rectangles overlapping the bands for datacursor to detect bands = [] for i in range(nlanes): bandlength = bandlengths[i] center = lane_centers[i] x = center - bandlength/2.0 for j in range(len(lanes[i])): dna_frag = lanes[i][j] bandwidth = bandwidths[i][j] dist = distances[i][j].magnitude y = dist - bandwidth/2.0 pxlX, pxlY = bands_pxlXYmid[i][j] band_midI = bands_arr[pxlY, pxlX][0] alpha = 0 if abs(band_midI - back_col) >= detectlim else 0.4 band = _plt.Rectangle((x, y), bandlength, bandwidth, fc='none', ec='w', ls=':', alpha=alpha, label='{} bp'.format(len(dna_frag))) _plt.gca().add_patch(band) bands.append(band) _plt.ylim(gel_len, -max(welly)) xlim = sum(wellx) + (nlanes+1)*self.wellsep _plt.xlim(0, xlim) _plt.ylabel('Distance (cm)') _plt.xlabel('Lanes') bbox_args = dict(boxstyle='round,pad=0.6', fc='none') an1 = _plt.annotate(title, xy=(0, 0), xytext=(xlim+0.4, (gel_len+max(welly))/2.0), va="center", bbox=bbox_args) an1.draggable() _plt.gca().set_aspect('equal', adjustable='box') cursor_args = dict(display='multiple', draggable=True, hover=False, bbox=dict(fc='white'), arrowprops=dict(arrowstyle='simple', fc='white', alpha=0.5), xytext=(15, -15), formatter='{label}'.format) if cursor_ovr: for key in cursor_ovr: cursor_args[key] = cursor_ovr[key] if cursor_args['hover'] == True: cursor_args['display'] = 'single' #_datacursor(bands, **cursor_args) return _plt return None