text: import click
  import numpy as np
  from tqdm import tqdm
  
  from ..io import (
      append_column_to_hdf5,
      append_predictions_cta,
      append_predictions_cta,
      drop_prediction_column,
      drop_prediction_groups,
      load_model,
      read_telescope_data_chunked,
  )
  from ..apply import predict_dxdy
  from ..configuration import AICTConfig
  from ..logging import setup_logging
  from ..preprocessing import calc_true_disp, camera_to_horizontal
  
  
  @click.command()
  @click.argument("configuration_path", type=click.Path(exists=True, dir_okay=False))
  @click.argument("data_path", type=click.Path(exists=True, dir_okay=False))
  @click.argument("dxdy_model_path", type=click.Path(exists=False, dir_okay=False))
  @click.option("-n", "--n-jobs", type=int, help="Number of cores to use")
  @click.option("-y", "--yes", help="Do not prompt for overwrites", is_flag=True)
  @click.option("-v", "--verbose", help="Verbose log output", is_flag=True)
  @click.option(
      "-N",
      "--chunksize",
      type=int,
      help="If given, only process the given number of events at once",
  )
  def main(
      configuration_path, data_path, dxdy_model_path, chunksize, n_jobs, yes, verbose
  ):
      """
      Apply given model to data. Three columns are added to the file, x_prediction, y_prediction
      and disp_prediction
  
      CONFIGURATION_PATH: Path to the config yaml file
      DATA_PATH: path to the FACT data in a h5py hdf5 file, e.g. erna_gather_fits output
      DXDY_MODEL_PATH: Path to the pickled dxdy model.
      """
      log = setup_logging(verbose=verbose)
  
      config = AICTConfig.from_yaml(configuration_path)
      model_config = config.dxdy
  
      columns_to_delete = [
          "x_prediction",
          "y_prediction",
          "theta",
          "theta_deg",
          "theta_rec_pos",
          "disp_prediction",
      ]
      for i in range(1, 6):
          columns_to_delete.extend(
              [
                  "theta_off_" + str(i),
                  "theta_deg_off_" + str(i),
                  "theta_off_rec_pos_" + str(i),
              ]
          )
  
      n_del_cols = 0
      for column in columns_to_delete:
          n_del = 0
          if config.data_format == "CTA":
              n_del = drop_prediction_groups(data_path, group_name=column, yes=yes)
          elif config.data_format == "simple":
              n_del = drop_prediction_column(
                  data_path, group_name=config.events_key, column_name=column, yes=yes
              )
          n_del_cols += n_del
  
      if n_del_cols > 0:
          log.warn(
              "Source dependent features need to be calculated from the predicted source possition. "
              + "Use e.g. `fact_calculate_theta` from https://github.com/fact-project/pyfact."
          )
  
      log.info("Loading model")
      dxdy_model = load_model(dxdy_model_path)
      log.info("Done")
  
      if n_jobs:
          dxdy_model.n_jobs = n_jobs
  
      df_generator = read_telescope_data_chunked(
          data_path,
          config,
          chunksize,
          model_config.columns_to_read_apply,
          feature_generation_config=model_config.feature_generation,
      )
  
      log.info("Predicting on data...")
      for df_data, start, stop in tqdm(df_generator):
  
          dxdy = predict_dxdy(
              df_data[model_config.features],
              dxdy_model,
          )
  
          x = df_data[config.cog_x_column] + dxdy[:, 0]
          y = df_data[config.cog_y_column] + dxdy[:, 1]
  
          if config.data_format == "CTA":
              alt, az = camera_to_horizontal(df_data, config, x, y)
              df_data.reset_index(inplace=True)
              for tel_id, group in df_data.groupby("tel_id"):
                  d = group[["obs_id", "event_id"]].copy()
                  d["y_prediction"] = y[group.index]
                  d["x_prediction"] = x[group.index]
                  d["dx_prediction"] = dxdy[:, 0][group.index]
                  d["dy_prediction"] = dxdy[:, 1][group.index]
                  d["alt_prediction"] = alt[group.index]
                  d["az_prediction"] = az[group.index]
                  append_predictions_cta(
                      data_path,
                      d,
                      f"/dl2/event/telescope/{model_config.output_name}/tel_{tel_id:03d}",
                  )
          elif config.data_format == "simple":
              key = config.events_key
              append_column_to_hdf5(data_path, x, key, "x_prediction")
              append_column_to_hdf5(data_path, y, key, "y_prediction")
              append_column_to_hdf5(data_path, dxdy[:, 0], key, "dx_prediction")
              append_column_to_hdf5(data_path, dxdy[:, 1], key, "dy_prediction")
              if config.coordinate_transformation == "CTA":
                  alt, az = camera_to_horizontal(df_data, config, x, y)
                  append_column_to_hdf5(data_path, alt, key, "alt_prediction")
              elif config.coordinate_transformation == "FACT":
                  source_zd, az = camera_to_horizontal(df_data, config, x, y)
                  append_column_to_hdf5(data_path, source_zd, key, "source_zd_prediction")
              append_column_to_hdf5(data_path, az, key, "az_prediction")
  
  
  if __name__ == "__main__":
      # pylint: disable=no-value-for-parameter
      main()
  