class_docstr: Base class for cubic regression spline stateful transforms
  
  This class contains all the functionality for the following stateful
  transforms:
   - ``cr(x, df=None, knots=None, lower_bound=None, upper_bound=None, constraints=None)``
     for natural cubic regression spline
   - ``cc(x, df=None, knots=None, lower_bound=None, upper_bound=None, constraints=None)``
     for cyclic cubic regression spline
class_docstr_tok: ["Base","class","for","cubic","regression","spline","stateful","transforms","This","class","contains","all","the","functionality","for","the","following","stateful","transforms",":","-","``","cr","(","x",",","df=None",",","knots=None",",","lower_bound=None",",","upper_bound=None",",","constraints=None",")","``","for","natural","cubic","regression","spline","-","``","cc","(","x",",","df=None",",","knots=None",",","lower_bound=None",",","upper_bound=None",",","constraints=None",")","``","for","cyclic","cubic","regression","spline"]
code: def memorize_finish(self):
      args = self._tmp["args"]
      xs = self._tmp["xs"]
      # Guards against invalid subsequent memorize_chunk() calls.
      del self._tmp
  
      x = np.concatenate(xs)
      if args["df"] is None and args["knots"] is None:
          raise ValueError("Must specify either 'df' or 'knots'.")
  
      constraints = args["constraints"]
      n_constraints = 0
      if constraints is not None:
          if safe_string_eq(constraints, "center"):
              # Here we collect only number of constraints,
              # actual centering constraint will be computed after all_knots
              n_constraints = 1
          else:
              constraints = np.atleast_2d(constraints)
              if constraints.ndim != 2:
                  raise ValueError(
                      "Constraints must be 2-d array or " "1-d vector."
                  )
              n_constraints = constraints.shape[0]
  
      n_inner_knots = None
      if args["df"] is not None:
          min_df = 1
          if not self._cyclic and n_constraints == 0:
              min_df = 2
          if args["df"] < min_df:
              raise ValueError(
                  "'df'=%r must be greater than or equal to %r."
                  % (args["df"], min_df)
              )
          n_inner_knots = args["df"] - 2 + n_constraints
          if self._cyclic:
              n_inner_knots += 1
      self._all_knots = _get_all_sorted_knots(
          x,
          n_inner_knots=n_inner_knots,
          inner_knots=args["knots"],
          lower_bound=args["lower_bound"],
          upper_bound=args["upper_bound"],
      )
      if constraints is not None:
          if safe_string_eq(constraints, "center"):
              # Now we can compute centering constraints
              constraints = _get_centering_constraint_from_dmatrix(
                  _get_free_crs_dmatrix(
                      x, self._all_knots, cyclic=self._cyclic
                  )
              )
  
          df_before_constraints = self._all_knots.size
          if self._cyclic:
              df_before_constraints -= 1
          if constraints.shape[1] != df_before_constraints:
              raise ValueError(
                  "Constraints array should have %r columns but"
                  " %r found."
                  % (df_before_constraints, constraints.shape[1])
              )
          self._constraints = constraints
  
imports: import numpy
  from patsy.util import have_pandas, atleast_2d_column_default, no_pickling, assert_no_pickling, safe_string_eq
  from patsy.state import stateful_transform
label_desc: Use image node_id 3 for calling the CubicRegressionSpline obj's underlying member method code with example usage: obj.memorize_finish() without return types