Insert function / formula in Excel from R and VBA and apply VBA macro from R

I am looking for a way to insert a formula / function into Excel from R. I went through all the packages and the best I could do this:

library(xlsx)
wb = createWorkbook(type = "xlsx")
sh = createSheet(wb, "CANFI")
cell = CellBlock(sheet = sh, startRow = 2, startColumn = 2, noRows = 1, noColumns = 1)
CB.setMatrixData(cellBlock = cell, x = as.matrix('=SUM(A1:A2)'), startRow = 1, startColumn = 1)
saveWorkbook(wb, file =  "./test.xlsx")

      

This gave me a file with the formula written in cell SUM (A1: A2) as if it were a string in Excel. If you click on a cell and press enter, the formula calculates the result. I guess the way to do the trick would be using VBA code that will update, but normal select, refreshall, etc. Did not work.

+2


source to share


3 answers


I was able to find the right solution using suggestion from @ n8. The idea was to use VBA code that would render text in a column across the entire workbook and across all columns. Then call this VBA code from R using VBscript.

Here is the R code:

library(xlsx)
wb = loadWorkbook("./source/template.xlsm")
sh   = wb$getSheet("CAN.FI")
cell = CellBlock(sheet = sh, startRow = 2, startColumn = 2, noRows = 1, noColumns = 1)
CB.setMatrixData(cellBlock = cell, x = as.matrix('=SUM(A1:A2)'), startRow = 1, startColumn = 1)

cell = CellBlock(sheet = sh, startRow = 4, startColumn = 4, noRows = 1, noColumns = 1)
CB.setMatrixData(cellBlock = cell, x = as.matrix('=SUM(A1:A2)'), startRow = 1, startColumn = 1)
saveWorkbook(wb, file =  "./test.xlsm")

path_to_vbs_file = "./text_to_column.vbs"
shell(shQuote(normalizePath(path_to_vbs_file)), "cscript", flag = "//nologo")

      

The template.xlsm file is a blank .xlsm workbook with one CAN.FI sheet, the workbook has a built-in macro / module called text_to_column (). The macro runs like this:

Sub text_to_column()
Application.ScreenUpdating = False
On Error Resume Next
For Each wksht In ActiveWorkbook.Worksheets
 wksht.activate
     For Each col In wksht.Columns
         Columns(col.Column).TextToColumns _
         Destination:=Cells(1, col.Column), _
         DataType:=xlDelimited, _
         TextQualifier:=xlDoubleQuote, _
         ConsecutiveDelimiter:=False, _
         Tab:=True, _
         Semicolon:=False, _
         Comma:=False, _
         Space:=False, _
         Other:=False, _
         FieldInfo:=Array(1, 1), _
         TrailingMinusNumbers:=True
     Next col
Next wksht
End Sub

      

At the end of the R code, you will notice

path_to_vbs_file = "./text_to_column.vbs"

      

This points to vbscript, which is just a .vbs file. You can create it from a text file and change the extension. I followed the theme: fooobar.com/questions/111293 / ... . My .vbs file is called: text_to_column.vbs and looks like this:

Option Explicit

ExcelMacroExample

Sub ExcelMacroExample() 

  Dim xlApp 
  Dim xlBook 

  Set xlApp = CreateObject("Excel.Application") 
  Set xlBook = xlApp.Workbooks.Open(".\test.xlsm", 0, True) 
  xlApp.Application.Visible = False
  xlApp.DisplayAlerts = False
  xlApp.Run "text_to_column"
  xlApp.ActiveWorkbook.SaveAs ".\test filled.xlsx", 51

  xlApp.ActiveWorkbook.Close
  xlApp.Quit

  Set xlBook = Nothing 
  Set xlApp = Nothing 

End Sub 

      



vb script will open the file test.xlsm will run the VBA macro "text_to_column" and then save the file in xlsx format called "test filled.xlsx"

vbscript runs from the last line in R.

In short, the R code will open the template, fill in the formula in string format, call vbscript, which will run the macro that transforms the formula. Then the final formulas file will be saved in the correct format.

On the side of the note, if you want to write a formula, you might consider using ADDRESS and INDIRECT, which allows for row and column numbers, which is easier than writing A1 B2 etc., like one example:

SUM(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(5,1)))

      

What will the sum A1: A5 do.

Thanks again for the help @ n8. Hope this helps people.

Romny.

+3


source


I have found that using the Text To Columns feature applies the formatting of the changes more carefully than the Formatting utility. For example, if you change a column from general to text, none of the cells change from "Number" to "Number saved as text" (as indicated by the green triangle in the upper left corner) unless you click in the cell and hit enter. If you use the "text-to-columns" utility, the entire column gets small green triangles. It may also happen for you that you can use the "text-to-columns" utility by specifying your column as "general". However, there is a limitation that you can only do one column at a time.

Another solution could be to make changes to the Excel track in the workbook and reapply the cell value to the cell formula, for example:



Private Sub Worksheet_Change(ByVal Target As Range)

  Target.FormulaR1C1 = Target.Value

End Sub

      

Assuming that in the Worksheet code, I suspect the cell will be recalculated when it is changed. But I haven't tested it.

+1


source


The XLConnect and openxlsx packages seem to be better suited for working with formulas. Not sure about the VBA script however.

Quoting an example from the openxlsx doc.

> library(openxlsx)
> wb <- createWorkbook()
> addWorksheet(wb, "Sheet 1")
> writeData(wb, "Sheet 1", x = iris)
> v <- c("SUM(A2:A151)", "AVERAGE(B2:B151)") ## skip header row
> writeFormula(wb, sheet = 1, x = v, startCol = 10, startRow = 2)
> writeFormula(wb, 1, x = "A2 + B2", startCol = 10, startRow = 10)
> saveWorkbook(wb, "writeFormulaExample.xlsx", overwrite = TRUE)

      

Likewise for XLConnect,

> library(XLConnect)
> wb <- loadWorkbook("cellFormula.xlsx", create = TRUE)
> createSheet(wb, "Formula")
> setCellFormula(wb, "Formula", 1, 1, "SUM($B$1:$B$29)")
> getCellFormula(wb, "Formula", 1, 1)
          Formula 
"SUM($B$1:$B$29)" 
> getCellFormula(wb, 1, 1, 1)
[1] "SUM($B$1:$B$29)"

      

Alternatively, you can create Excel with the required VB formulas and scripts. And, selectively modify the data in excel. Since the rest of the formulas / scenarios have not changed, Excel will update with the new input.

For example, suppose Excel "Sheet1" contains data in 3 columns with the formula in column C as =SUM(A2:B2)

. This way we can just change the data in columns A and B and Excel will update for column C due to the existing formulas.

> library(openxlsx)
> wb <- loadWorkbook("formula-demo.xlsx")
> # update data in A column
> writeData(wb, "Sheet1", 21:40, startCol = 1, startRow = 2, colNames = FALSE) 
Warning message:
Overwriting existing cell data. 
> saveWorkbook(wb, "formula-demo.xlsx", overwrite = T)

      

Hope this helps!

+1


source







All Articles