<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def _caltrack_predict_design_matrix( model_type, model_params, data, disaggregated=False, input_averages=False, output_averages=False, ):
 """ An internal CalTRACK predict method for use with a design matrix of the form used in model fitting. Given a set model type, parameters, and daily temperatures, return model predictions. Parameters model_type : :any:`str` Model type (e.g., ``'cdd_hdd'``). model_params : :any:`dict` Parameters as stored in :any:`eemeter.CalTRACKUsagePerDayCandidateModel.model_params`. data : :any:`pandas.DataFrame` Data over which to predict. Assumed to be like the format of the data used for fitting, although it need only have the columns. If not giving data with a `pandas.DatetimeIndex` it must have the column `n_days`, representing the number of days per prediction period (otherwise inferred from DatetimeIndex). disaggregated : :any:`bool`, optional If True, return results as a :any:`pandas.DataFrame` with columns ``'base_load'``, ``'heating_load'``, and ``'cooling_load'`` input_averages : :any:`bool`, optional If HDD and CDD columns expressed as period totals, select False. If HDD and CDD columns expressed as period averages, select True. If prediction period is daily, results should be the same either way. Matters for billing. output_averages : :any:`bool`, optional If True, prediction returned as averages (not totals). If False, returned as totals. Returns ------- prediction : :any:`pandas.Series` or :any:`pandas.DataFrame` Returns results as series unless ``disaggregated=True``. """


    zeros = pd.Series(0, index=data.index)
    ones = zeros + 1

    if isinstance(data.index, pd.DatetimeIndex):
        days_per_period = day_counts(data.index)
    else:
        try:
            days_per_period = data["n_days"]
        except KeyError:
            raise ValueError("Data needs DatetimeIndex or an n_days column.")

    # TODO(philngo): handle different degree day methods and hourly temperatures
    if model_type in ["intercept_only", "hdd_only", "cdd_only", "cdd_hdd"]:
        intercept = _get_parameter_or_raise(model_type, model_params, "intercept")
        if output_averages == False:
            base_load = intercept * days_per_period
        else:
            base_load = intercept * ones
    elif model_type is None:
        raise ValueError("Model not valid for prediction: model_type=None")
    else:
        raise UnrecognizedModelTypeError(
            "invalid caltrack model type: {}".format(model_type)
        )

    if model_type in ["hdd_only", "cdd_hdd"]:
        beta_hdd = _get_parameter_or_raise(model_type, model_params, "beta_hdd")
        heating_balance_point = _get_parameter_or_raise(
            model_type, model_params, "heating_balance_point"
        )
        hdd_column_name = "hdd_%s" % heating_balance_point
        hdd = data[hdd_column_name]
        if input_averages == True and output_averages == False:
            heating_load = hdd * beta_hdd * days_per_period
        elif input_averages == True and output_averages == True:
            heating_load = hdd * beta_hdd
        elif input_averages == False and output_averages == False:
            heating_load = hdd * beta_hdd
        else:
            heating_load = hdd * beta_hdd / days_per_period
    else:
        heating_load = zeros

    if model_type in ["cdd_only", "cdd_hdd"]:
        beta_cdd = _get_parameter_or_raise(model_type, model_params, "beta_cdd")
        cooling_balance_point = _get_parameter_or_raise(
            model_type, model_params, "cooling_balance_point"
        )
        cdd_column_name = "cdd_%s" % cooling_balance_point
        cdd = data[cdd_column_name]
        if input_averages == True and output_averages == False:
            cooling_load = cdd * beta_cdd * days_per_period
        elif input_averages == True and output_averages == True:
            cooling_load = cdd * beta_cdd
        elif input_averages == False and output_averages == False:
            cooling_load = cdd * beta_cdd
        else:
            cooling_load = cdd * beta_cdd / days_per_period
    else:
        cooling_load = zeros

    # If any of the rows of input data contained NaNs, restore the NaNs
    # Note: If data contains ANY NaNs at all, this declares the entire row a NaN.
    # TODO(philngo): Consider making this more nuanced.
    def _restore_nans(load):
        load = load[data.sum(axis=1, skipna=False).notnull()].reindex(data.index)
        return load

    base_load = _restore_nans(base_load)
    heating_load = _restore_nans(heating_load)
    cooling_load = _restore_nans(cooling_load)

    if disaggregated:
        return pd.DataFrame(
            {
                "base_load": base_load,
                "heating_load": heating_load,
                "cooling_load": cooling_load,
            }
        )
    else:
        return base_load + heating_load + cooling_load