The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Getting started with writexl

writexl writes data frames to xlsx. It needs no Java and no Excel, and for the common case it needs nothing from you either.

One data frame

path <- write_xlsx(iris, tempfile(fileext = ".xlsx"))

write_xlsx() returns the path it wrote, so it composes with anything that takes a file name.

Several sheets

A named list becomes one sheet per element, and the names become the tabs:

path <- write_xlsx(list(Flowers = iris, Cars = mtcars[1:5, 1:4]),
                   tempfile(fileext = ".xlsx"))

Sheet names are repaired rather than rejected: Excel forbids [ ] : * ? / \, limits a name to 31 characters, and refuses duplicates. writexl fixes each of those and warns, naming the original and the replacement, because a sheet name is often taken from data and losing the whole export over one stray / would be unkind.

What is written

Column types map to Excel’s own:

R Excel
character text
numeric, integer number
logical TRUE/FALSE
Date, POSIXct a formatted date or datetime
factor its labels, as text
difftime a number of seconds

NA becomes an empty cell. A column of a type xlsx has no representation for — complex, raw, a plain list column — is an error rather than a silent approximation.

na writes something else in place of an empty cell, for NaN as well as NA. It keeps its own type, so a number stays a number:

tmp <- write_xlsx(data.frame(x = c(1.5, NA)), na = "not measured")

Set it for the whole workbook with xl_properties(na = ), for one column with xl_col_spec(na = ), or for one cell with xl_cell_general(na = ); the innermost one that is set wins. Substituting a string into a numeric column makes that column mixed, so reading it back gives character.

Excel has no concept of a time zone, so POSIXct needs a decision. writexl makes it once for the whole workbook: if every value shares one zone the local wall-clock reading is written, and if they differ everything is converted to UTC and you are warned. Either way nothing is mislabelled.

The header row is written by default and styled bold; col_names = FALSE and format_headers = FALSE turn each off.

Everything else

The rest of the package is optional. Nothing below is needed to write a workbook, and each has a vignette of its own:

Two ideas run through all of them and are worth knowing early.

A format is an object. xl_font(), xl_fill() and the rest each return a complete xl_format, they combine with +, and the same object styles a cell, a column, a chart series or a conditional rule.

money <- xl_num_format("$#,##0.00") + xl_font(bold = TRUE)

A range is written one of two ways. Either as A1 text, or against the data frame by column name — and the second is worth preferring, because it moves with the column and accounts for the header row on its own:

xl_sheet(iris, autofilter = "A1:E151")                   # A1 text
#> <xl_sheet: 150 rows x 5 cols>
xl_sheet(iris, filter = xl_filter("Species", "==", "setosa"))  # by column
#> <xl_sheet: 150 rows x 5 cols>

Reading it back

writexl only writes. To read, use readxl:

readxl::read_xlsx(write_xlsx(head(iris, 3), tempfile(fileext = ".xlsx")))
#> # A tibble: 3 × 5
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#>          <dbl>       <dbl>        <dbl>       <dbl> <chr>  
#> 1          5.1         3.5          1.4         0.2 setosa 
#> 2          4.9         3            1.4         0.2 setosa 
#> 3          4.7         3.2          1.3         0.2 setosa

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.