I export tables with large values to .xlsx using XLConnect.
Is there a way for the results to be written into an excel-cell with activated thousand separators?
library(XLConnect)
#example for a large value
a <- 10000000000
wb <- loadWorkbook("sof_q.xlsx"), create = TRUE)
cs <- createCellStyle(wb)
setDataFormat(cs, format = "0.00")
createSheet(wb, name = "a")
writeWorksheet(wb,a,"a",startRow = 1, startCol = 1, header = TRUE)
rc = expand.grid(row = 1:2, col = 1:2)
setCellStyle(wb, sheet = "a", row = rc$row, col = rc$col, cellstyle = cs)
setColumnWidth(wb, sheet = "a", column = 1:5, width = -1)
saveWorkbook(wb)
In Excel, a should look like this
10.000.000.000
Using
setDataFormat(cs, format = "0,000,000.00")
might work but in the case of shorter values, I have values like
0,032,666.29
Use # for a digit placeholder:
setDataFormat(cs, format = "###,###.00")
or
setDataFormat(cs, format = "###,##0")
Related
I want to load a workbook/sheet from an excel file that has complex formatting that can't be applied using openxlsx (ie cells where some of the text is italic and some is not) and then append the formatted data from that worksheet at the bottom of an existing worksheet.
I'm aware that cloneWorksheet() will copy the ENTIRE sheet, but i specifically want to add data from one worksheet into another existing worksheet.
wb <- createWorkbook()
sheet <- addWorksheet(wb = wb, sheetName = "sheet")
writeData(wb, sheet, mydata) #assume mydata is a data frame
wb2 <- loadWorkbook("myfile.xlsx")
writeData(wb = wb, sheet = sheet, x = wb2, startRow = ncol(mydata) + 1) #I know this doesn't work
For that last line, I want to take the formatted data from the worksheet in wb2 and add it after the existing data in the sheet of wb.
Yes the issue with loadWorkbook() is that it doesn't allow you to "write" the data using writeDate function but retains the styles. And reverse, readWorkbook() permits you to write the data to any other sheet, but does not preserve the style. So you need to combine the styles (and applicable rows/cols) from a loadWorkbook with the writeable data from readWorkbook. Let's say I have the below wb with various styles:
library(openxlsx)
#Create original file with various formats
wb <- createWorkbook()
big_font <- createStyle(fontSize = 20)
red_font <- createStyle(fontColour = "red")
border_cell <- createStyle(borderStyle = "thick", borderColour ="purple", border = "TopBottomLeftRight")
bold_blue_big <- createStyle(fontColour = "blue", fontSize = 25, textDecoration = "bold")
addWorksheet(wb, sheetName = "format")
writeData(wb, sheet = "format", mtcars)
addStyle(wb, sheet = "format", rows = 1:5, cols = 1:3, style = big_font, gridExpand = TRUE)
addStyle(wb, sheet = "format", rows = 6:7, cols = 1:3, style = red_font, gridExpand = TRUE)
addStyle(wb, sheet = "format", rows = 8:15, cols = 1:3, style = border_cell, gridExpand = TRUE)
addStyle(wb, sheet = "format", rows = 14:20, cols = 1:3, style = bold_blue_big, gridExpand = TRUE, stack = TRUE)
openXL(wb)
saveWorkbook(wb, "path/format.xlsx", overwrite = TRUE)
getStyles(wb)[[2]]
> getStyles(wb)[[2]]
A custom cell style.
Cell formatting: GENERAL
Font colour: #FF0000
The wb has set styles and the second style is color #FF0000 or red
If loadWorkbook reads in the xlsx file of wb just written, it will retain the styles
#Preserves styles
format_wb <- loadWorkbook("path/format.xlsx")
getStyles(format_wb)[[2]] #returns styles
> getStyles(format_wb)[[2]]
A custom cell style.
Cell formatting: GENERAL
Font name: Calibri
Font size: 11
Font colour: #FF0000
But not when readWorkwook is used. We will still want this writeable data to add to the new, merged
#Loses styles but writable data
write_wb <- readWorkbook("path/format.xlsx")
getStyles(write_wb) #returns no styles
Basically needed a specialized function to combine information from both of these. It would need to be modified if only a select few styles wanted to be copied over or specific style, but otherwise - if full duplication of all formatting:
This function has several inputs but it basically has:
the formatted workbook where the styles are coming from,
the workbook that the styles are being applied to,
the sheetname of the workbook,
and 5. and whether rows/columns should be added. This is for when you want to preserve the exact formatting from wb1 for cells A1:J25 but you are for example, adding wb1 contents to wb2, and wanting to format wb1.data at A150:J175. These defaults are otherwise set to 0 if no rows/cols adjustments should be made
The format_wb$styleObjects prints the style and also the rows and columns where that style is found. From there, it is just extracting the distinct values for each row (these_rows) and col (these_cols) as you sequence along all the styles in the format_wb. Add the style by referencing it with getStyles and apply to these_rows and these_cols. If not starting from the first row and first column (i.e., rows and columns are added), then the arguments addrow and addcol will need to change from default 0
#Function ----
apply_old_format <-
function(format_wb, new_wb, sheetname, addrow = 0, addcol = 0){
for(i in seq_along(format_wb$styleObjects)){
these_rows <- unique(format_wb$styleObjects[[i]]$rows)
these_cols <- unique(format_wb$styleObjects[[i]]$cols)
addStyle(new_wb, sheet = sheetname,
style = getStyles(format_wb)[[i]],
rows = addrow + (these_rows),
cols = addcol + (these_cols),
gridExpand = TRUE, stack = TRUE)
}
}
Now we can create a new workbook that will combine an existing data set, in this case iris, and appending the mtcars data set that was formatted in the original wb and loaded in as format_wb for its styles, and read in to be writeable as write_wb.
Example has long data on first sheet and wide data on second sheet depending on where second data set is appended
#Create merged product
new_wb <- createWorkbook()
addWorksheet(new_wb, sheetName = "iris_cars_long")
addWorksheet(new_wb, sheetName = "iris_cars_wide")
writeData(new_wb, sheet = "iris_cars_long", iris, colNames = TRUE)
writeData(new_wb, sheet = "iris_cars_long", write_wb, startRow = 152, startCol = 1)
apply_old_format(format_wb = format_wb, new_wb = new_wb, sheetname = "iris_cars_long") #adds to top part of iris data
apply_old_format(format_wb = format_wb, new_wb = new_wb, sheetname = "iris_cars_long", addrow = dim(iris)[1] +1) #adds to cars using dimension of data if it is not known, just used as demo can otherwise enter number
writeData(new_wb, sheet = "iris_cars_wide", iris, colNames = TRUE)
writeData(new_wb, sheet = "iris_cars_wide", write_wb, startRow = 1, startCol = length(iris) + 2) #for if you don't know exact length just for demo
apply_old_format(format_wb = format_wb, new_wb = new_wb, sheetname = "iris_cars_wide", addcol = length(iris) +1)
openXL(new_wb)
If I run code from the openxlsx vignette, pasted below, and open the file, it displays correctly but Excel considers it a Custom format. Can anyone explain why and how to get Excel to recognize it as a Date datatype?
# data.frame of dates
dates <- data.frame(d1 = Sys.Date() - 0:4)
for (i in 1:3) dates <- cbind(dates, dates)
names(dates) <- paste0("d", 1:8)
## Date Formatting
wb <- createWorkbook()
addWorksheet(wb, "Date Formatting", gridLines = FALSE)
writeData(wb, 1, dates) ## write without styling
## numFmt == 'DATE' will use the date format specified by the above
addStyle(wb, 1, style = createStyle(numFmt = "DATE"), rows = 2:11, cols = 1, gridExpand = TRUE)
saveWorkbook(wb, "Date Formatting.xlsx", overwrite = TRUE)
I am attempting to create save multiple formatted Excel files, each of which are subsetted from a certain data frame by a factor.
This is an example of what I have tried so far
# Create data
df <- data.frame(category = rep(c("a","b","c","d"),times = 20),
values = rnorm(20,5,2))
# Create workbooks named after specific level of factor
l1 <- sapply(levels(df$category), assign, value = createWorkbook())
# Create styles
hs <- createStyle(fgFill = "#808080", border = "bottom", textDecoration = "bold")
lt8 <- createStyle(bgFill = "#ff0000")
gt30 <- createStyle(bgFill = "#00b0f0")
grn <- createStyle(bgFill = "#00b000")
# For loop
for (i in l1) {
addWorksheet(i, names(i))
writeData(i, names(i), df[df$category == names(i),], headerStyle = hs)
conditionalFormatting(i, names(i), cols = 1:2, rows = 2:nrow(df[df$category == names(i),]), rule = "$B2<2", type = "expression", style = lt8)
conditionalFormatting(i, names(i), cols = 1:2, rows = 2:nrow(df[df$category == names(i),]), rule = "$B2>=7", type = "expression", style = gt30)
conditionalFormatting(i, names(i), cols = 1:2, rows = 2:nrow(df[df$category == names(i),]), rule = "AND($B2>=4, $B2<5.5)", style = grn)
setColWidths(i, names(i), cols=1:2, widths = "auto")
saveWorkbook(paste(i, ".wb", sep = ""), file = paste(i, " Report ", ".xlsx", sep = ""))
}
Each time, I run into this error
Error in if (tolower(sheetName) %in% tolower(wb$sheet_names)) stop("A worksheet by that name already exists! Sheet names must be unique case-insensitive.")
This is the first time I've attempted to assign any sheets so I'm not exactly sure why I keep getting this error.
Ultimately, I would like to save the subsetted and formatted excel workbooks through a repetitive process because my real data would produce many more workbooks. The workbooks must be separate and placing these subsets in sheets won't work.
Any and all advice on how to achieve this would be greatly appreciated.
Your error is coming from this line:
addWorksheet(i, names(i))
because names(i) is empty:
> names(l1[['a']])
character(0)
You might be better off looping over the names of l1, so you have the categories you want, using that to pull the appropriate workbook from the list. Something like:
for (i in names(l1)) {
wb = l1[[i]]
addWorksheet(wb, i)
category_data <- df[df$category == i,]
writeData(wb, i, category_data, headerStyle = hs)
conditionalFormatting(wb, i, cols = 1:2, rows = 2:nrow(category_data), rule = "$B2<2", type = "expression", style = lt8)
conditionalFormatting(wb, i, cols = 1:2, rows = 2:nrow(category_data), rule = "$B2>=7", type = "expression", style = gt30)
conditionalFormatting(wb, i, cols = 1:2, rows = 2:nrow(category_data), rule = "AND($B2>=4, $B2<5.5)", style = grn)
setColWidths(wb, i, cols=1:2, widths = "auto")
saveWorkbook(wb, file = paste(i, " Report ", ".xlsx", sep = ""))
}
There's still one subtle error here:
l1 <- sapply(levels(df$category), assign, value = createWorkbook())
createWorkbook() is only being called once, so you have 4 copies of the same workbook. That means the final save will have all 4 tabs. Compare:
> identical(l1$a, l1$b)
[1] TRUE
with 2 separate calls to createWorkbook():
> identical(createWorkbook(), createWorkbook())
[1] FALSE
Might be worth just looping over the distinct categories, and creating the workbook inside the loop. That is:
library(openxlsx)
# Create data
df <- data.frame(category = rep(c("a","b","c","d"),times = 20),
values = rnorm(20,5,2))
# Create styles
hs <- createStyle(fgFill = "#808080", border = "bottom", textDecoration = "bold")
lt8 <- createStyle(bgFill = "#ff0000")
gt30 <- createStyle(bgFill = "#00b0f0")
grn <- createStyle(bgFill = "#00b000")
# For loop
for (i in levels(df$category)) {
wb <- createWorkbook()
addWorksheet(wb, i)
category_data <- df[df$category == i,]
writeData(wb, i, category_data, headerStyle = hs)
conditionalFormatting(wb, i, cols = 1:2, rows = 2:nrow(category_data), rule = "$B2<2", type = "expression", style = lt8)
conditionalFormatting(wb, i, cols = 1:2, rows = 2:nrow(category_data), rule = "$B2>=7", type = "expression", style = gt30)
conditionalFormatting(wb, i, cols = 1:2, rows = 2:nrow(category_data), rule = "AND($B2>=4, $B2<5.5)", style = grn)
setColWidths(wb, i, cols=1:2, widths = "auto")
saveWorkbook(wb, file = paste(i, " Report ", ".xlsx", sep = ""))
}
After running this code
library(XLConnect)
template <- loadWorkbook ( filename = "template.xlsx" , create = T )
createSheet ( template , c("sheet1","sheet2") )
# setStyleAction(template,XLC$"STYLE_ACTION.NONE")
Data <- data.frame(
a = 1:10,
b = 11:20
)
setDataFormatForType(template, type = XLC$DATA_TYPE.NUMERIC, format = "0.00" )
# list22$`Brand Equity` <- as.numeric(list22$`Brand Equity`)
# list22$`Purchase Intent` <- as.numeric(list22$`Purchase Intent`)
csHeader <- createCellStyle(template, name = "header10")
setFillPattern(csHeader, fill = XLC$BORDER.DOUBLE)
setFillForegroundColor(csHeader, color = XLC$COLOR.DARK_RED)
# setCellFormula(object = template, sheet = (paste0("sheet",i)), row = c(2:4),col = c(1:3), formula = )
setCellStyle(template, sheet = "sheet1", row = 1,
col = c(1:2), cellstyle = csHeader)
setCellStyle(template, sheet = "sheet2", row = 1,
col = c(1:2), cellstyle = csHeader)
for (i in 1:2)
{
setColumnWidth(template, sheet = (paste0("sheet",i)), column = c(1:3), width = 15800)
writeWorksheet ( template , data = Data, sheet = (paste0("sheet",i)), startRow = 1 , startCol = 1 ,
header = TRUE )
}
saveWorkbook ( template )
I obtain
and
It does not seem to pass my argument about the color of the cell. Any ideas ? Moreover is there a way to write transform the numbers in percentages ? So 1 for instance would be 100%, 2 would be 200% etc...
For converting the numbers into percentage, you can write a function similar to this one:
addformatperc<-function(num,roundlevel){
betternum<-paste(prettyNum(round(num*100,roundlevel),big.mark = ","),"%",sep="")
return(betternum)
}
#Output
addformatperc(1,0)
[1] "100%"
I tried XLconnect and Xlsx but in vain.
the code is as below.
require(XLConnect)
require(xlsx)
a<-read.xlsx("C:\\R\\CPC2.xlsx",1,as.data.frame=TRUE)
b<-read.xlsx("C:\\R\\CPCVoucher1.xlsx",1,as.data.frame=TRUE)
wk <- loadWorkbook("XLConnectExample1.xlsx", create = TRUE)
writeWorksheet(wk, a, sheet = "test", startRow = 1, startCol = 2)
writeWorksheet(wk,b, sheet = "test", startRow = 15, startCol = 2)
saveWorkbook(wk)