我正在寻找一种方法从R中将公式/函数插入到Excel中。我浏览了所有软件包,我能做的最好的是:
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")
这给了我一个文件,公式写在单元格SUM(A1:A2)中,好像它是Excel中的一个字符串。如果单击单元格并按Enter键,则公式将计算结果。我想一种做法的方法是使用可刷新的VBA代码,但通常的选择,refreshall等都不起作用。
答案 0 :(得分:4)
我能够使用@ n8的建议找到合适的解决方案。我们的想法是使用VBA代码,该代码将在所有工作簿和所有列上执行文本到列。然后使用VBscript从R调用此VBA代码。
这是R代码:
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")
文件template.xlsm是一个空的.xlsm工作簿,有一张表CAN.FI,这本书有一个嵌入式宏/模块叫做“text_to_column()”宏如下:
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
你会注意到R代码的末尾
path_to_vbs_file = "./text_to_column.vbs"
这指向vbscript,它只是一个.vbs文件。可以从文本文件创建它并更改扩展名。我跟着线程:How To create a vbscript Stackoverflow。我的.vbs文件名为:text_to_column.vbs,如下所示:
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脚本将打开文件test.xlsm运行VBA宏“text_to_column”,然后将文件保存为xlsx格式,名称为“test filled.xlsx”
vbscript从R的最后一行开始。
长话短说,代码R将打开模板,用字符串格式填充公式,调用将运行宏转换公式的vbscript。然后将保存具有适当格式的公式的最终文件。
另一方面,如果你想写公式,你可以考虑使用ADDRESS和INDIRECT,它允许你使用行号和列号,这比编写A1 B2等更容易。一个例子可能是:
SUM(INDIRECT(ADDRESS(1,1)&":"&ADDRESS(5,1)))
这将是A1:A5的总和。
再次感谢您的帮助,@ n8。 希望能帮助别人。
罗曼。
答案 1 :(得分:2)
XLConnect和openxlsx包似乎更适合处理公式。虽然不确定VBA脚本。
引用openxlsx文档中的示例。
> 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)
同样适用于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)"
或者,可以使用所需的公式和VB脚本创建Excel。并且,有选择地修改excel中的数据。由于未修改其余公式/ scrips,Excel将根据新输入进行更新。
例如,假设Excel&#34; Sheet1&#34;包含3列中的数据,其中C列中的公式为=SUM(A2:B2)
。因此,我们只能修改A列和B列中的数据,并且由于现有公式,Excel将针对C列进行更新。
> 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)
我希望这有帮助!
答案 2 :(得分:1)
我发现使用“文本到列”功能可以比“格式化”更彻底地应用格式更改&#34;实用工具。例如,如果将列从常规更改为文本,则没有任何单元格从&#34; Number&#34; to&#34; Number保存为文本&#34; (由左上角的绿色三角形表示),除非您单击进入单元格并按Enter键。而如果您使用&#34; text-to-columns&#34;实用程序,整列获得小绿色三角形。您也可以使用&#34; text-to-columns&#34;实用程序,将列指定为&#34; general&#34;。但是,存在一个限制,即您一次只能执行一列。
另一种解决方案可能是让Excel跟踪对工作簿的更改,并将单元格的值重新应用于单元格公式,例如:
Private Sub Worksheet_Change(ByVal Target As Range)
Target.FormulaR1C1 = Target.Value
End Sub
我怀疑,将它放在工作表代码中会导致在更改单元格时重新计算单元格。但我还没有测试过它。