The fapply function applies formatting to a vector.

fapply(x, format = NULL, width = NULL, justify = NULL)

Arguments

x

A vector, factor, or list to apply the format to.

format

A format to be applied.

width

The desired character width of the formatted vector. Default value is NULL, meaning the vector will be variable width.

justify

Whether to justify the return vector. Valid values are 'left', 'right', 'center', 'centre', or 'none'.

Value

A vector of formatted values.

Details

The fapply function accepts several types of formats: formatting strings, named vectors, vectorized functions, or user-defined formats. It also accepts a formatting list, composed of any of the previous types. The function will first determine the type of format, and then apply the format in the appropriate way. Results are returned as a vector.

The function also has parameters for width and justification.

Parameters may also be passed as attributes on the vector. See the fattr function for additional information on setting formatting attributes.

Types of Formats

The fapply function will process any of the following types of formats:

  • Formatting string: A single string will be interpreted as a formatting string. Formatting strings include R-style formatting codes, and some SAS-style format names like "best" and "date". See the FormattingStrings documentation and the sections below for further details.

  • Named vector: A named vector can serve as a lookup list or decode for a vector. You can use a named vector to perform simple lookups on character vectors.

  • Format object: A format object may be created using the value function. The format object is included in the fmtr package, and is specially designed for data categorization.

  • Vectorized formatting function: A vectorized function provides the most flexibility and power over your formatting. You can use an existing formatting function from any package, or create your own vectorized formatting function using Vectorize.

fapply will also accept a formatting list, which can contain any number of formats from the above list. To create a formatting list, see the flist function.

"best" Format

The SAS "best" format is used to fit numeric values within a certain width. The word "best" is followed by the desired width, i.e. "best6" or "best12". The format will then use the most optimal display for the available width.

The format will use the entire value if the number of digits fits in the desired width. If not, the format may round the value. The format may also use scientific notation if the value is very large or very small. If the format cannot fit the value in the desired width at all, it will emit stars ("*") in the desired width.

For input values that are less than the desired width, the result will be left-padded with spaces. The output value will then always contain the exact number of characters requested.

Such a format has no direct equivalent in R, and is indeed difficult to replicate. For this reason, the fmtr package added this format option for those situations when you want to replicate SAS "best" formatting as closely as possible.

The "best" format accepts widths between 1 and 32. The default width is 12. The R "best" format syntax does not accept a number of decimals, as in "bestW.d".

Note that "best" widths between 8 and 16 will match SAS most reliably. Small widths have many special cases, and the logic is difficult to replicate. For large values, there are some differences between SAS and R in how they represent these numbers, and sometimes they will not match.

"date" Format

The "date" format is used to display date values in a readable character form, such as "01JAN70" or "01-JAN-1970", depending on the specified width. The word "date" is followed by the desired width, e.g. "date7" or "date9". This format replicates similar capabilities in SAS.

The format converts numeric or Date values into character strings using a pattern that depends on the width. Smaller widths display shorter forms, while larger widths display more detail. For example:

  • date5 – Displays as mmmyy (e.g., "JAN70")

  • date7 – Displays as ddmmmyy (e.g., "01JAN70")

  • date9 – Displays as ddmmmyyyy (e.g., "01JAN1970")

  • date11 – Displays as dd-mmm-yyyy (e.g., "01-JAN-1970")

The "date" format accepts widths between 5 and 11. Widths outside this range are not valid and will result in an error. The default width is 7. Both "dateW" and "dateW." are accepted, the trailing dot (".") is optional and does not affect behavior.

For input values that are numeric, the function will interpret them as the number of days since 1970-01-01, consistent with R's internal date representation. If the input is already an R Date or POSIXt object, it will be used directly. Missing values will be returned as missing.

The output value is left-padded with spaces if it is shorter than the requested width, ensuring the formatted result always occupies exactly the specified number of characters. For example, for the date 1970-01-01, the result of date7 is "01JAN70", while the result of date8 is " 01JAN70", with one additional leading space.

This format has no direct equivalent in base R. The fmtr package adds this capability for users who wish to replicate SAS-style "date" formatting behavior as closely as possible.

"time" Format

The "time" format is used to display time-of-day values in a readable character form, such as "9:00" or "23:59:59.995", depending on the specified width and number of decimal places. The word "time" is followed by the desired width w and optional decimal precision d, e.g. "time8", "time12.3", or "time20.9". This format closely replicates the behavior of SAS TIMEw.d.

The format converts numeric or time-like objects into character strings representing elapsed time in hours, minutes, and seconds. Hours are not limited to the 0–23 range and may exceed 24, allowing the format to represent durations such as "119:26:40" or "1388:53:20".

Width and precision

The total width w controls the minimum number of characters in the output, while the optional decimal precision d controls the number of digits displayed after the decimal point for seconds.

  • time5 – Displays hours and minutes or only hours (e.g. " 9:00", "24:00", " 120")

  • time7 – Displays h:mm:ss or hh:mm (e.g., "1:03:10", " 23:59")

  • time8 – Displays as hh:mm:ss (e.g. " 1:03:10", "23:59:59")

  • time9.1 – Displays seconds with one decimal place (e.g., "1:00:00.0")

  • time12.3 – Displays seconds with three decimal places (e.g., " 9:00:01.005")

  • time20.9 – Displays seconds with up to nine decimal places (e.g., " 9:00:00.987654321")

Valid widths range from 2 to 20. The decimal precision d, when specified, must be between 0 and w - 1. Widths or precisions outside these ranges are not valid and will result in an error.

If the width w is omitted (e.g., "time" or "time."), it defaults to 8. If the decimal precision d is omitted, it defaults to 0. Both "TIMEw" and "TIMEw." are accepted, and the trailing dot is optional.

Input handling

The TIMEw.d format accepts the following input types:

  • Numeric values, interpreted as the number of seconds since midnight

  • POSIXt objects, using the time-of-day component

  • hms objects from the hms package

For numeric and hms inputs, negative values and values larger than 24 hours are allowed and formatted accordingly, consistent with SAS behavior. For example, values such as -3600 or 430000 are valid. For POSIXt inputs, only the clock time is used. As a result, negative times and times exceeding 24 hours are not applicable to POSIXt objects.

The TIMEw.d format resolves known differences in fractional-second rounding between base R and SAS, it applies SAS-compatible rounding to ensure that formatted results match SAS output exactly, particularly near rounding boundaries.

In addition, whereas base R effectively limits fractional seconds to 6 digits, TIMEw.d supports up to 12 digits of decimal precision, padding with trailing zeros when necessary, consistent with SAS behavior.

See also

fcat to create a format catalog, value to define a format, fattr to easily set the formatting attributes of a vector, and flist to define a formatting list. Also see fdata to apply formats to an entire data frame, and FormattingStrings for how to define a formatting string.

Examples

## Example 1: Formatting string ##
v1 <- c(1.235, 8.363, 5.954, 2.465)

# Apply string format.
fapply(v1, "%.1f")
# [1] "1.2" "8.4" "6.0" "2.5"

# Apply width and two decimals
fapply(v1, "%5.2f")
# [1] " 1.24" " 8.36" " 5.95" " 2.46"

# Apply "best" format
fapply(v1, "best3")
# [1] "1.2" "8.4" "  6" "2.5"

## Example 2: Named vector ##
# Set up vector
v2 <- c("A", "B", "C", "B")

# Set up named vector for formatting
fmt2 <- c(A = "Label A", B = "Label B", C = "Label C")

# Apply format to vector
fapply(v2, fmt2)
# [1] "Label A" "Label B" "Label C" "Label B"

## Example 3: User-defined format ##
# Define format
fmt3 <- value(condition(x == "A", "Label A"),
              condition(x == "B", "Label B"), 
              condition(TRUE, "Other"))
              
# Apply format to vector
fapply(v2, fmt3)
# [1] "Label A" "Label B" "Other"   "Label B"

## Example 4: Formatting function ##
# Set up vectorized function
fmt4 <- Vectorize(function(x) {

  if (x %in% c("A", "B"))
    ret <- paste("Label", x)
  else
    ret <- "Other" 
    
  return(ret)
})

# Apply format to vector
fapply(v2, fmt4)
# [1] "Label A" "Label B" "Other"   "Label B"

## Example 5: Formatting List - Row Type ##
# Set up data
# Notice each row has a different data type
v3 <- list(2841.258, "H", Sys.Date(),
           "L", Sys.Date() + 60, 1382.8865)
v4 <- c("int", "char", "date", "char", "date", "int")

# Create formatting list
lst <- flist(type = "row", lookup = v4,
       int = function(x) format(x, digits = 2, nsmall = 1, 
                                  big.mark=","),
       char = value(condition(x == "H", "High"),
                     condition(x == "L", "Low"),
                     condition(TRUE, "NA")),
       date = "%d%b%Y")

# Apply formatting list to vector
fapply(v3, lst)
# [1] "2,841.3"   "High"      "06Jan2024" "Low"       "06Mar2024" "1,382.9"  

## Example 6: Formatting List - Column Type ##
# Set up data
v5 <- c(Sys.Date(), Sys.Date() + 30, Sys.Date() + 60)
v5
# [1] "2024-01-06" "2024-02-05" "2024-03-06"

# Create formatting list
lst <- flist("%B", "This month is: %s", type = "column")

# Apply formatting list to vector
fapply(v5, lst)
# [1] "This month is: January"  "This month is: February" "This month is: March" 

# Example 7: Conditional Formatting
# Data vector
v6 <- c(8.38371, 1.46938, 3.28783, NA, 0.98632)

# User-defined format
fmt5 <- value(condition(is.na(x), "Missing"),
              condition(x < 1, "Low"), 
              condition(x > 5, "High"),
              condition(TRUE, "%.2f"))

# Apply format to data vector
fapply(v6, fmt5)
# [1] "High"    "1.47"    "3.29"    "Missing" "Low" 

# Example 8: "best" Format
#' # Data vector
v7 <- c(12.3456, 1234567.89, NA, 0.123456, 0.000012345)

fapply(v7, "best6")
# [1] "12.346" "1.23E6" NA       "0.1235" "123E-7"

# Example 9: "date" Format
# Data Vector
v8 <- as.Date(c("1924-02-29",NA,"1980-12-31","2019-12-31","2020-02-29","2030-08-20"))

fapply(v8, "date7")
# [1] "29FEB24" NA   "31DEC80" "31DEC19" "29FEB20" "20AUG30"

fapply(v8, "date11")
# [1] "29-FEB-1924" NA   "31-DEC-1980" "31-DEC-2019" "29-FEB-2020" "20-AUG-2030"

# Example 10: "time" format
# Data vector
v9 <- c(-3600, NA, 0, 59.9, 3600.12345, 86399.995, 90000)
v10 <- strptime(c("01:00:00.123", "09:00:01.456", NA), format="%H:%M:%OS")
fapply(v9, "time8")
# [1]  "-1:00:00" NA         " 0:00:00" " 0:01:00" " 1:00:00" "24:00:00" "25:00:00"

fapply(v10, "time10.2")
# [1] "1:00:00.12" "9:00:01.46" NA