% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/freq.R
\name{freq}
\alias{freq}
\title{Frequency Table}
\usage{
freq(
  data,
  x = NULL,
  weights = NULL,
  digits = 1,
  valid = TRUE,
  cum = FALSE,
  sort = "",
  na_val = NULL,
  labelled_levels = c("prefixed", "labels", "values"),
  factor_levels = c("observed", "all"),
  rescale = TRUE,
  styled = TRUE,
  ...
)
}
\arguments{
\item{data}{A \code{data.frame}, vector, or factor. If a data frame is provided,
specify the target variable \code{x}. If both \code{data} and \code{x} are supplied as
vectors, \code{data} is ignored with a warning.}

\item{x}{A variable from \code{data} (unquoted).}

\item{weights}{Optional numeric vector of weights (same length as \code{x}).
The variable may be referenced as a bare name when it belongs to \code{data},
or as a qualified expression like \code{other$w} (evaluated in the calling
environment), which always takes precedence over \code{data} lookup. \code{NA}
weights are treated as zero with a warning; see \code{Details}.}

\item{digits}{Number of decimal digits to display for percentages (default: \code{1}).}

\item{valid}{Logical. If \code{TRUE} (default), display valid percentages
(excluding missing values).}

\item{cum}{Logical. If \code{FALSE} (the default), cumulative percentages are omitted.
If \code{TRUE}, adds cumulative percentages.}

\item{sort}{Sorting method for values:
\itemize{
\item \code{""} - no sorting (default)
\item \code{"+"} - increasing frequency
\item \code{"-"} - decreasing frequency
\item \code{"name+"} - alphabetical A-Z
\item \code{"name-"} - alphabetical Z-A
}}

\item{na_val}{Atomic vector of numeric or character values to be treated as missing (\code{NA}).

For \emph{labelled} variables (from \strong{haven} or \strong{labelled}), this argument
must refer to the underlying coded values, not the visible labels.

Example:

\if{html}{\out{<div class="sourceCode">}}\preformatted{x <- labelled(c(1, 2, 3, 1, 2, 3), c("Low" = 1, "Medium" = 2, "High" = 3))
freq(x, na_val = 1) # Treat all "Low" as missing
}\if{html}{\out{</div>}}}

\item{labelled_levels}{For \code{labelled} variables, defines how labels and
values are displayed:
\itemize{
\item \code{"prefixed"} or \code{"p"} - show labels as \verb{[value] label} (default)
\item \code{"labels"} or \code{"l"} - show only labels
\item \code{"values"} or \code{"v"} - show only numeric codes
}}

\item{factor_levels}{Character. Controls how factor and labelled values
are displayed in the frequency table. \code{"observed"} (the default;
matches Stata's \code{tab}) shows only levels present in the data.
\code{"all"} (matches SPSS \code{FREQUENCIES} and \code{\link[=code_book]{code_book()}}'s default)
keeps every declared level, including unused ones, which appear
with \code{n = 0}.}

\item{rescale}{Logical. If \code{TRUE} (default), rescale weights so that their
total equals the unweighted sample size (\code{length(weights)}). See
\code{Details} for the interaction with \code{NA} weights.}

\item{styled}{Logical. If \code{TRUE} (default), print the formatted spicy table.
If \code{FALSE}, return a plain \code{data.frame} with frequency values.}

\item{...}{Additional arguments passed to \code{\link[=print.spicy_freq_table]{print.spicy_freq_table()}}.}
}
\value{
With \code{styled = FALSE}, a plain \code{data.frame} with no extra attributes
and columns:
\itemize{
\item \code{value} - unique values or factor levels
\item \code{n} - frequency count (weighted if applicable)
\item \code{prop} - proportion of total
\item \code{valid_prop} - proportion of valid responses (if \code{valid = TRUE})
\item \code{cum_prop}, \code{cum_valid_prop} - cumulative percentages (if \code{cum = TRUE})
}

With \code{styled = TRUE} (default), prints the formatted table to the
console and invisibly returns a \code{spicy_freq_table} object: the same
\code{data.frame} carrying rendering metadata as attributes (\code{digits},
\code{data_name}, \code{var_name}, \code{var_label}, \code{class_name}, \code{n_total},
\code{n_valid}, \code{weighted}, \code{rescaled}, \code{weight_var}) used by
\code{\link[=print.spicy_freq_table]{print.spicy_freq_table()}}.
}
\description{
Creates a frequency table for a vector or variable from a data frame, with
options for weighting, sorting, handling \emph{labelled} data, defining custom
missing values, and displaying cumulative percentages.

When \code{styled = TRUE}, the function prints a spicy-formatted ASCII table
using \code{\link[=print.spicy_freq_table]{print.spicy_freq_table()}} and \code{\link[=spicy_print_table]{spicy_print_table()}}; otherwise, it
returns a \code{data.frame} containing frequencies and proportions.
}
\details{
This function is designed to mimic common frequency procedures from
statistical software such as SPSS or Stata, while integrating the
flexibility of R's data structures.

It automatically detects the type of input (\code{vector}, \code{factor}, or
\code{labelled}) and applies appropriate transformations, including:
\itemize{
\item Handling of labelled variables via \strong{labelled} or \strong{haven}
\item Optional recoding of specific values as missing (\code{na_val})
\item Optional weighting with a rescaling mechanism
\item Support for cumulative percentages (\code{cum = TRUE})
\item Multiple display modes for labels via \code{labelled_levels}
\item Schema-vs-observed level display via \code{factor_levels}
}

For factor and labelled inputs, the \code{factor_levels} argument
controls whether declared-but-unobserved levels appear in the
output. The default \code{"observed"} drops them (Stata \code{tab} behavior);
\code{"all"} keeps them with \code{n = 0}, matching SPSS \code{FREQUENCIES} and
\code{\link[=code_book]{code_book()}}'s default. For schema-level inspection without
computing frequencies, use \code{\link[=varlist]{varlist()}} or \code{\link[=code_book]{code_book()}} with
\code{factor_levels = "all"}.

When weighting is applied (\code{weights}), the frequencies and percentages are
computed proportionally to the weights. The argument \code{rescale = TRUE}
normalizes weights so their sum equals the unweighted sample size
(\code{length(weights)}).

Missing values in \code{weights} are treated as zero (with a warning), so
the corresponding rows contribute nothing to any cell. With
\code{rescale = TRUE}, the remaining weights are normalized so the total
weighted N still equals \code{length(weights)} — the implicit share of the
zeroed rows is redistributed over the others, mirroring Stata's
\code{pweight} semantics. With \code{rescale = FALSE}, the total weighted N is
the actual sum of non-\code{NA} weights.
}
\examples{
# Frequency table with labelled ordered factor
freq(sochealth, education)
freq(sochealth, self_rated_health, sort = "-")

library(labelled)

# Simple numeric vector
x <- c(1, 2, 2, 3, 3, 3, NA)
freq(x)

# Plain vector with a sentinel value recoded as missing
freq(c(1, 2, 3, 99, 99), na_val = 99)

# Labelled variable (haven-style)
x_lbl <- labelled(
  c(1, 2, 3, 1, 2, 3, 1, 2, NA),
  labels = c("Low" = 1, "Medium" = 2, "High" = 3)
)
var_label(x_lbl) <- "Satisfaction level"

# Treat value 1 ("Low") as missing
freq(x_lbl, na_val = 1)

# Display only labels, add cumulative \%
freq(x_lbl, labelled_levels = "labels", cum = TRUE)

# Display values only, sorted descending
freq(x_lbl, labelled_levels = "values", sort = "-")

# Show all declared factor levels, including unused ones (SPSS-style).
# The default "observed" mirrors Stata's `tab` and drops unused levels.
f <- factor(c("Yes", "No", "Yes"), levels = c("Yes", "No", "Maybe"))
freq(f, factor_levels = "all")

# With weighting
df <- data.frame(
  sex = factor(c("Male", "Female", "Female", "Male", NA, "Female")),
  weight = c(12, 8, 10, 15, 7, 9)
)

# Weighted frequencies (normalized)
freq(df, sex, weights = weight, rescale = TRUE)

# Weighted frequencies (without rescaling)
freq(df, sex, weights = weight, rescale = FALSE)

# Base R style, with weights and cumulative percentages
freq(df$sex, weights = df$weight, cum = TRUE)

# Piped version (tidy syntax) and sort alphabetically descending ("name-")
df |> freq(sex, sort = "name-")

# Non-styled return (for programmatic use)
f <- freq(df, sex, styled = FALSE)
head(f)

}
\seealso{
\code{\link[=print.spicy_freq_table]{print.spicy_freq_table()}} for formatted printing.
\code{\link[=spicy_print_table]{spicy_print_table()}} for the underlying ASCII rendering engine.
}
