Search code examples
rexcelrexcelvba

Does RInterface.GetArrayToVBA() always return an array?


Referring to this question from Wilmott Forums, I've just written the following function:

Public Function KmeansPrice(ByVal priceArray As Range, _
                            ByVal clustersNumber As Integer) As Double

    ' Following rows are reproducible only if RExcel has been installed
    ' on your Excel!

    Dim y() As Double

    RInterface.StartRServer
    RInterface.PutArrayFromVBA "x", priceArray
    RInterface.PutArrayFromVBA "n", clustersNumber
    RInterface.RRun "x = as.numeric(x)"
    RInterface.RRun "cluster = kmeans(x, n)$cluster"
    RInterface.RRun "bestBid = rep(NA, n)"
    RInterface.RRun "for(i in 1:n)" & _
                    "{" & _
                    "  assign(paste('group.', i, sep = ''), " & _
                    "         x[cluster == i]);" & _
                    "  bestBid[i] = max(get(paste('group.', i, sep = '')))" & _
                    "}"
    RInterface.RRun "y = min(bestBid) + 0.01"
    y = RInterface.GetArrayToVBA("y")
    KmeansPrice = y(0, 0)

End Function

Of course I've prototyped it in R before and it worked properly, then I guess that the cause of this error:

Error -2147220501
in Module RExcel.RServer

Error in variable assignment

is related to the wrong usage of RInterface.GetArrayToVBA() for what concerns dimensions and indexing of arrays from R to VBA.

Is anyone able to make the code above work? A working example with an array of just five or ten elements as priceArray and clustersNumber equal to 2 or 3 would be sufficient.


Solution

  • I'm not familiar with the clustering function, but this returns a result without breaking.

    I prefer to make my functions in an R editor and then source the code, so I did this in R, then sourced my R function.

    kmeansPrice <- function(priceArray,clustersNumber)
    {
      `[` <- function(...) base::`[`(...,drop=FALSE) #in case we have a 1 dimensional table
      x<-priceArray
      n<- clustersNumber
      x<-matrix(as.numeric(x),nrow=dim(x)[1],ncol=dim(x)[2])
      cluster = kmeans(x, n)$cluster
      bestBid = rep(NA, n)
      for(i in 1:n)
      {
        assign(paste('group.', i, sep = ''),
        x[cluster == i])
        bestBid[i] = max(get(paste('group.', i, sep = '')))
      }
      return(min(bestBid) + 0.01)
    }
    

    Then you can just

    Public Function KmeansPrice(ByVal priceArray As Range, _
                                ByVal clustersNumber As Integer) As Double
    
    rinterface.PutArrayFromVBA "priceArray", priceArray.Value 'I think this ".Value" was your problem'
    rinterface.PutArrayFromVBA "clustersNumber", clustersNumber
    rinterface.RRun "theResult <- kmeansPrice(priceArray,clustersNumber)"
    y = rinterface.GetRExpressionValueToVBA("theResult") 'preferred to GetArrayToVBA for single-value results'
    KmeansPrice = y
    End Function
    

    and run it with example data: a 2x4 table that evaluates to

         [,1] [,2]
    [1,]    5    9
    [2,]    6   10
    [3,]    7   11
    [4,]    8   12
    

    with 3 "clusters"

    Sub runkmeans()
    theResult = KmeansPrice(Range("BH2:BI5"), 3)
    MsgBox (theResult)
    End Sub
    

    which yields 6.01