Conditions | 11 |
Total Lines | 127 |
Lines | 0 |
Ratio | 0 % |
Changes | 3 | ||
Bugs | 1 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like fit_pixel_fixed_scatter() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | #!/usr/bin/env python |
||
314 | def fit_pixel_fixed_scatter(flux, ivar, initial_thetas, design_matrix, |
||
315 | regularization, censoring_mask, **kwargs): |
||
316 | """ |
||
317 | Fit theta coefficients and noise residual for a single pixel, using |
||
318 | an initially fixed scatter value. |
||
319 | |||
320 | :param flux: |
||
321 | The normalized flux values. |
||
322 | |||
323 | :param ivar: |
||
324 | The inverse variance array for the normalized fluxes. |
||
325 | |||
326 | :param initial_thetas: |
||
327 | A list of initial theta values to start from, and their source. For |
||
328 | example: `[(theta_0, "guess"), (theta_1, "old_theta")] |
||
329 | |||
330 | :param design_matrix: |
||
331 | The model design matrix. |
||
332 | |||
333 | :param regularization: |
||
334 | The regularization strength to apply during optimization (Lambda). |
||
335 | |||
336 | :param censoring_mask: |
||
337 | A per-label censoring mask for each pixel. |
||
338 | |||
339 | :keyword op_method: |
||
340 | The optimization method to use. Valid options are: `l_bfgs_b`, `powell`. |
||
341 | |||
342 | :keyword op_kwds: |
||
343 | A dictionary of arguments that will be provided to the optimizer. |
||
344 | |||
345 | :returns: |
||
346 | The optimized theta coefficients, the noise residual `s2`, and |
||
347 | metadata related to the optimization process. |
||
348 | """ |
||
349 | |||
350 | if np.sum(ivar) < 1.0 * ivar.size: # MAGIC |
||
351 | metadata = dict(message="No pixel information.", op_time=0.0) |
||
352 | fiducial = np.hstack([1.0, np.zeros(design_matrix.shape[1] - 1)]) |
||
353 | return (fiducial, np.inf, metadata) # MAGIC |
||
354 | |||
355 | # Determine if any theta coefficients will be censored. |
||
356 | censored_theta = ~np.any(np.isfinite(design_matrix), axis=0) |
||
357 | # Make the design matrix safe to use. |
||
358 | design_matrix[:, censored_theta] = 0 |
||
359 | |||
360 | feval = [] |
||
361 | for initial_theta, initial_theta_source in initial_thetas: |
||
362 | feval.append(_pixel_objective_function_fixed_scatter( |
||
363 | initial_theta, design_matrix, flux, ivar, regularization, False)) |
||
364 | |||
365 | initial_theta, initial_theta_source = initial_thetas[np.nanargmin(feval)] |
||
366 | |||
367 | op_kwds = dict(x0=initial_theta, |
||
368 | args=(design_matrix, flux, ivar, regularization), |
||
369 | disp=False, maxfun=np.inf, maxiter=np.inf) |
||
370 | |||
371 | if any(censored_theta): |
||
372 | # If the initial_theta is the same size as the censored_mask, but different |
||
373 | # to the design_matrix, then we need to censor the initial theta so that we |
||
374 | # don't bother solving for those parameters. |
||
375 | op_kwds["x0"] = np.array(op_kwds["x0"])[~censored_theta] |
||
376 | op_kwds["args"] = (design_matrix[:, ~censored_theta], flux, ivar, |
||
377 | regularization) |
||
378 | |||
379 | # Allow either l_bfgs_b or powell |
||
380 | t_init = time() |
||
381 | op_method = kwargs.get("op_method", "l_bfgs_b").lower() |
||
382 | if op_method == "l_bfgs_b": |
||
383 | |||
384 | op_kwds.update(m=design_matrix.shape[1], factr=10.0, pgtol=1e-6) |
||
385 | op_kwds.update(kwargs.get("op_kwds", {})) |
||
386 | |||
387 | # If op_bounds are given and we are censoring some theta terms, then we |
||
388 | # will need to adjust which op_bounds we provide. |
||
389 | if "bounds" in op_kwds and any(censored_theta): |
||
390 | op_kwds["bounds"] = \ |
||
391 | [b for b, is_censored in zip(op_kwds["bounds"], censored_theta) \ |
||
392 | if not is_censored] |
||
393 | |||
394 | op_params, fopt, metadata = op.fmin_l_bfgs_b( |
||
395 | _pixel_objective_function_fixed_scatter, |
||
396 | fprime=None, approx_grad=None, **op_kwds) |
||
397 | |||
398 | metadata.update(dict(fopt=fopt)) |
||
399 | |||
400 | elif op_method == "powell": |
||
401 | |||
402 | op_kwds.update(xtol=1e-6, ftol=1e-6) |
||
403 | op_kwds.update(kwargs.get("op_kwds", {})) |
||
404 | |||
405 | # Set 'False' in args so that we don't return the gradient, because fmin |
||
406 | # doesn't want it. |
||
407 | args = list(op_kwds["args"]) |
||
408 | args.append(False) |
||
409 | op_kwds["args"] = tuple(args) |
||
410 | |||
411 | t_init = time() |
||
412 | |||
413 | op_params, fopt, direc, n_iter, n_funcs, warnflag = op.fmin_powell( |
||
414 | _pixel_objective_function_fixed_scatter,full_output=True, **op_kwds) |
||
415 | |||
416 | metadata = dict(fopt=fopt, direc=direc, n_iter=n_iter, n_funcs=n_funcs, |
||
417 | warnflag=warnflag) |
||
418 | |||
419 | else: |
||
420 | raise ValueError("unknown optimization method '{}' -- " |
||
421 | "powell or l_bfgs_b are available".format(op_method)) |
||
422 | |||
423 | # Additional metadata common to both optimizers. |
||
424 | metadata.update(dict(op_method=op_method, op_time=time() - t_init, |
||
425 | initial_theta=initial_theta, initial_theta_source=initial_theta_source)) |
||
426 | |||
427 | # De-censor the optimized parameters. |
||
428 | if any(censored_theta): |
||
429 | theta = np.zeros(censored_theta.size) |
||
430 | theta[~censored_theta] = op_params |
||
431 | |||
432 | else: |
||
433 | theta = op_params |
||
434 | |||
435 | # Fit the scatter. |
||
436 | residuals_squared = (flux - np.dot(theta, design_matrix.T))**2 |
||
437 | scatter = op.fmin(_scatter_objective_function, 0.0, |
||
438 | args=(residuals_squared, ivar), disp=False) |
||
439 | |||
440 | return (theta, scatter**2, metadata) |
||
441 |