R/import_csv.R

Defines functions import_csv

Documented in import_csv

# WARNING - Generated by {fusen} from dev/flat_import_export.Rmd: do not edit by hand # nolint: line_length_linter.

#' Flexible CSV/TXT File Import via data.table
#'
#' @description
#' Reads one or more CSV/TXT files using \code{\link[data.table]{fread}} as the backend.
#' Supports flexible combination strategies and source-file tracking. All return values
#' are \code{data.table} objects.
#'
#' @param file A non-empty \code{character} vector of file paths to CSV/TXT files.
#'   All paths must point to existing, accessible files.
#' @param combine A \code{logical} scalar controlling the combination strategy:
#'   \itemize{
#'     \item \code{TRUE} (default): Combine all files into a single \code{data.table}.
#'     \item \code{FALSE}: Return a named \code{list} of individual \code{data.table} objects.
#'   }
#' @param file_col A \code{character} scalar or \code{NULL} specifying the source-tracking
#'   column name (default: \code{"_file"}). Set to \code{NULL} to suppress the source column.
#'   Only applies when \code{combine = TRUE}.
#' @param full_path A \code{logical} scalar controlling path representation in labels:
#'   \itemize{
#'     \item \code{FALSE} (default): Use only the filename (via \code{basename()}).
#'     \item \code{TRUE}: Use the full file path.
#'   }
#' @param keep_ext A \code{logical} scalar controlling whether the file extension is
#'   retained in labels:
#'   \itemize{
#'     \item \code{FALSE} (default): Strip the file extension (e.g., \code{"data"}).
#'     \item \code{TRUE}: Retain the file extension (e.g., \code{"data.csv"}).
#'   }
#' @param ... Additional arguments passed directly to \code{\link[data.table]{fread}}
#'   (e.g., \code{select}, \code{drop}, \code{na.strings}, \code{skip}, \code{nThread}).
#'
#' @details
#' Label generation is controlled by the combination of \code{full_path} and \code{keep_ext}:
#' \tabular{ll}{
#'   \code{full_path = FALSE, keep_ext = FALSE} \tab Filename without extension: \code{"data"} \cr
#'   \code{full_path = FALSE, keep_ext = TRUE}  \tab Filename with extension: \code{"data.csv"} \cr
#'   \code{full_path = TRUE,  keep_ext = FALSE} \tab Full path without extension: \code{"/path/to/data"} \cr
#'   \code{full_path = TRUE,  keep_ext = TRUE}  \tab Full path with extension: \code{"/path/to/data.csv"} \cr
#' }
#'
#' When \code{combine = TRUE} and \code{file_col} is not \code{NULL},
#' \code{\link[data.table]{rbindlist}} is called with \code{idcol = file_col},
#' which generates the source column directly during the merge step without any
#' intermediate copies.
#'
#' @return
#' \itemize{
#'   \item \code{combine = TRUE}: A single \code{data.table} containing all imported rows.
#'     If \code{file_col} is not \code{NULL}, the first column contains the source
#'     file label for each row.
#'   \item \code{combine = FALSE}: A named \code{list} of \code{data.table} objects.
#'     List names are derived from file paths according to \code{full_path} and
#'     \code{keep_ext} settings.
#' }
#'
#' @note
#' \itemize{
#'   \item All specified files must exist and be readable at call time.
#'   \item \code{combine = TRUE} assumes compatible column structures across files;
#'     mismatched columns are automatically aligned via \code{fill = TRUE}.
#'   \item Logical parameters (\code{combine}, \code{full_path}, \code{keep_ext}) reject
#'     \code{NA} values explicitly.
#'   \item Only the extension of the file itself is stripped; dots in directory
#'     names (e.g. \code{"v1.2/file"}) are left intact.
#'   \item Duplicated labels (same file name in different folders) trigger a
#'     warning and are made unique with \code{make.unique()}.
#' }
#'
#' @seealso
#' \code{\link[data.table]{fread}}, \code{\link[data.table]{rbindlist}}
#'
#' @import data.table
#' @export
#' @examples
#' # Example: CSV file import demonstrations
#'
#' # Setup test files
#' csv_files <- mintyr_example(
#'   mintyr_examples("csv_test")     # Get example CSV files
#' )
#'
#' # Example 1: Import and combine CSV files using data.table
#' import_csv(
#'   csv_files,                      # Input CSV file paths
#'   combine = TRUE,                 # Combine all files into one data.table
#'   file_col = "_file",             # Column name for file source
#'   keep_ext = TRUE,                # Include .csv extension in _file column
#'   full_path = TRUE                # Show complete file paths in _file column
#' )
import_csv <- function(file,
                       combine   = TRUE,
                       file_col  = "_file",
                       full_path = FALSE,
                       keep_ext  = FALSE,
                       ...) {

  # -- 1. Input validation ----------------------------------------------------
  if (!is.character(file) || length(file) == 0L || anyNA(file))
    stop("`file` must be a non-empty character vector without NA.", call. = FALSE)
  missing_files <- file[!file.exists(file)]
  if (length(missing_files) > 0L)
    stop("The following files do not exist or cannot be accessed:\n",
         paste0("  - ", missing_files, collapse = "\n"), call. = FALSE)
  .check_flag(combine, "combine")
  .check_flag(full_path, "full_path")
  .check_flag(keep_ext, "keep_ext")
  if (!is.null(file_col) &&
      (!is.character(file_col) || length(file_col) != 1L ||
       is.na(file_col) || !nzchar(file_col)))
    stop("`file_col` must be a single non-empty character string, or NULL.",
         call. = FALSE)

  # -- 2. Labels --------------------------------------------------------------
  # .strip_ext() only touches the last path component, so dots in directory
  # names (e.g. "v1.2/file") are never mistaken for an extension.
  labels <- if (full_path) file else basename(file)
  if (!keep_ext) labels <- .strip_ext(labels)

  if (anyDuplicated(labels)) {
    dup <- unique(labels[duplicated(labels)])
    warning("Duplicated source label(s): ", paste(dup, collapse = ", "),
            ". Files with the same name in different folders cannot be told apart; ",
            "labels were made unique with make.unique(). ",
            "Consider `full_path = TRUE`.", call. = FALSE)
    labels <- make.unique(labels, sep = "_")
  }

  # -- 3. Read ----------------------------------------------------------------
  file_list        <- lapply(file, data.table::fread, ...)
  names(file_list) <- labels

  if (!combine) return(file_list)

  data.table::rbindlist(file_list, use.names = TRUE, fill = TRUE,
                        idcol = file_col)
}

Try the mintyr package in your browser

Any scripts or data that you put into this service are public.

mintyr documentation built on Oct. 5, 2026, 5:08 p.m.