code: def analyze(problem, X, Y, second_order=False, print_to_console=False,
              seed=None):
      
      if seed:
          np.random.seed(seed)
  
      problem = extend_bounds(problem)
      num_vars = problem['num_vars']
  
      X = generate_contrast(problem)
  
      main_effect = (1. / (2 * num_vars)) * np.dot(Y, X)
  
      Si = ResultDict((k, [None] * num_vars)
                      for k in ['names', 'ME'])
      Si['ME'] = main_effect
      Si['names'] = problem['names']
  
      if print_to_console:
          print("Parameter ME")
          for j in range(num_vars):
              print("%s %f" % (problem['names'][j], Si['ME'][j]))
  
      if second_order:
          interaction_names, interaction_effects = interactions(problem,
                                                                Y,
                                                                print_to_console)
          Si['interaction_names'] = interaction_names
          Si['IE'] = interaction_effects
  
      Si.to_df = MethodType(to_df, Si)
  
      return Si
docs: Perform a fractional factorial analysis
  
      Returns a dictionary with keys 'ME' (main effect) and 'IE' (interaction
      effect). The techniques bulks out the number of parameters with dummy
      parameters to the nearest 2**n.  Any results involving dummy parameters
      could indicate a problem with the model runs.
  
      Arguments
      ---------
      problem: dict
          The problem definition
      X: numpy.matrix
          The NumPy matrix containing the model inputs
      Y: numpy.array
          The NumPy array containing the model outputs
      second_order: bool, default=False
          Include interaction effects
      print_to_console: bool, default=False
          Print results directly to console
  
      Returns
      -------
      Si: dict
          A dictionary of sensitivity indices, including main effects ``ME``,
          and interaction effects ``IE`` (if ``second_order`` is True)
  
      Examples
      --------
      >>> X = sample(problem)
      >>> Y = X[:, 0] + (0.1 * X[:, 1]) + ((1.2 * X[:, 2]) * (0.2 + X[:, 0]))
      >>> analyze(problem, X, Y, second_order=True, print_to_console=True)