| 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 |