docs: def build_csv_transforming_training_input_fn(schema,
                                               features,
                                               stats,
                                               analysis_output_dir,
                                               raw_data_file_pattern,
                                               training_batch_size,
                                               num_epochs=None,
                                               randomize_input=False,
                                               min_after_dequeue=1,
                                               reader_num_threads=1,
                                               allow_smaller_final_batch=True):
    """Creates training input_fn that reads raw csv data and applies transforms.
  
    Args:
      schema: schema list
      features: features dict
      stats: stats dict
      analysis_output_dir: output folder from analysis
      raw_data_file_pattern: file path, or list of files
      training_batch_size: An int specifying the batch size to use.
      num_epochs: numer of epochs to read from the files. Use None to read forever.
      randomize_input: If true, the input rows are read out of order. This
          randomness is limited by the min_after_dequeue value.
      min_after_dequeue: Minimum number elements in the reading queue after a
          dequeue, used to ensure a level of mixing of elements. Only used if
          randomize_input is True.
      reader_num_threads: The number of threads enqueuing data.
      allow_smaller_final_batch: If false, fractional batches at the end of
          training or evaluation are not used.
  
    Returns:
      An input_fn suitable for training that reads raw csv training data and
      applies transforms.
  
    """
  
    def raw_training_input_fn():
      """Training input function that reads raw data and applies transforms."""
  
      if isinstance(raw_data_file_pattern, six.string_types):
        filepath_list = [raw_data_file_pattern]
      else:
        filepath_list = raw_data_file_pattern
  
      files = []
      for path in filepath_list:
        files.extend(file_io.get_matching_files(path))
  
      filename_queue = tf.train.string_input_producer(
          files, num_epochs=num_epochs, shuffle=randomize_input)
  
      csv_id, csv_lines = tf.TextLineReader().read_up_to(filename_queue, training_batch_size)
  
      queue_capacity = (reader_num_threads + 3) * training_batch_size + min_after_dequeue
      if randomize_input:
        _, batch_csv_lines = tf.train.shuffle_batch(
            tensors=[csv_id, csv_lines],
            batch_size=training_batch_size,
            capacity=queue_capacity,
            min_after_dequeue=min_after_dequeue,
            enqueue_many=True,
            num_threads=reader_num_threads,
            allow_smaller_final_batch=allow_smaller_final_batch)
  
      else:
        _, batch_csv_lines = tf.train.batch(
            tensors=[csv_id, csv_lines],
            batch_size=training_batch_size,
            capacity=queue_capacity,
            enqueue_many=True,
            num_threads=reader_num_threads,
            allow_smaller_final_batch=allow_smaller_final_batch)
  
      csv_header, record_defaults = csv_header_and_defaults(features, schema, stats, keep_target=True)
      parsed_tensors = tf.decode_csv(batch_csv_lines, record_defaults, name='csv_to_tensors')
      raw_features = dict(zip(csv_header, parsed_tensors))
  
      transform_fn = make_preprocessing_fn(analysis_output_dir, features, keep_target=True)
      transformed_tensors = transform_fn(raw_features)
  
      # Expand the dims of non-sparse tensors. This is needed by tf.learn.
      transformed_features = {}
      for k, v in six.iteritems(transformed_tensors):
        if isinstance(v, tf.Tensor) and v.get_shape().ndims == 1:
          transformed_features[k] = tf.expand_dims(v, -1)
        else:
          transformed_features[k] = v
  
      # image_feature_engineering does not need to be called as images are not
      # supported in raw csv for training.
  
      # Remove the target tensor, and return it directly
      target_name = get_target_name(features)
      if not target_name or target_name not in transformed_features:
        raise ValueError('Cannot find target transform in features')
  
      transformed_target = transformed_features.pop(target_name)
  
      return transformed_features, transformed_target
  
    return raw_training_input_fn