Error with variant variable in regression output - excel

I have a macro which is written to perform an OLS regression on data that is selected by the user. This is part of a larger add in that I am writing but I am stuck on what I think must be somewhat of a simple issue. I keep getting a subscript out of range error and I think its because I am getting a different sized matrix to what I am expecting.
The sub takes two variables as its arguments and calculated the OLS estimator given the specification. The y variable is always a n x 1 range (one column and multiple row) and the X variable is a n x m range (can be multiple columns and rows). When this function is used when X is a single column range, the For... Next block works for the following code:
For bcnt = 1 To k
Cells(bcnt, 1).Value = b(bcnt)
Next bcnt
But if the X variable is a multiple column range this won't work and it has to be the following:
For bcnt = 1 To k
Cells(bcnt, 1).Value = b(bcnt,1)
Next bcnt
I can't understand why as by my understanding b should always be a one dimensional array.
Would appreciate any help.
The actual sub:
Sub OLSregress(y As Variant, X As Variant)
Dim Xtrans, XtransX, XtransXinv, Xtransy As Variant
Dim outputsheet As Worksheet
Dim b As Variant
' The equation for this estimator is b=[X'X]^(-1)X'Y
Xtrans = Application.WorksheetFunction.Transpose(X)
XtransX = Application.WorksheetFunction.MMult(Xtrans, X)
XtransXinv = Application.WorksheetFunction.MInverse(XtransX)
Xtransy = Application.WorksheetFunction.MMult(Xtrans, y)
b = Application.WorksheetFunction.MMult(XtransXinv, Xtransy)
k = Application.WorksheetFunction.Count(b)
Set ouputsheet = Sheets.Add(, ActiveSheet)
ActiveSheet.Name = "Regression Output"
For bcnt = 1 To k
Cells(bcnt, 1).Value = b(bcnt, 1)
Next bcnt
End Sub

When you are referring to a range or are bringing in data from a sheet the array is always a 2 dimensional array. The first dimension is rows and the second is the columns.
This is a common point of confusion in VBA for excel because it's done without your intervention.
Your code is correct.
For more in-depth information check out this post

Related

Variables will not convert to integers

Currently messing around with macros in excel. One that generates a range that fills 3 columns of cells with 1-9.
Another that plots these numbers kind of like battle ship with x,y and v for the value.
I've gotten the number generation part working and I'm stuck on the plotting bit.
Currently the issue it that I'm getting an error "13" which means that my variables don't match up.
But i'm using a CInt to convert the variant to a int.
Debugging it seems like the for loop is getting all the values properly but just not converting.
Here is the code I have so far and a screenshot of the whole thing.
Sub random()
Dim MyRange As Range
Dim c As Integer, r As Integer
Set MyRange = Workbooks("test random gen").Sheets("Sheet1").Range("G16:I30")
For c = 1 To MyRange.Columns.Count
For r = 1 To MyRange.Rows.Count
Randomize
MyRange.Cells(r, c) = Int((9 - 1 + 1) * Rnd + 1)
Next r
Next c
End Sub
Sub Button6_Click()
Dim Board As Range
Dim Table As Range
Dim c As Integer, r As Integer
Dim Xboard As Integer, Yboard As Integer, Vboard As Integer
Dim Xboardv As Variant, Yboardv As Variant, Vboardv As Variant
Set Table = Workbooks("test random gen").Sheets("Sheet1").Range("G16:G30")
Set Board = Workbooks("test random gen").Sheets("Sheet1").Range("M16:U24")
For r = 1 To Table.Rows.Count
Xboardv = Table.Cells.Value
Yboardv = Table.Cells.Offset(columnOffset:=1).Value
Vboardv = Table.Cells.Offset(columnOffset:=2).Value
Xboard = CInt(Xboardv)
Yboard = CInt(Yboardv)
Vboard = CInt(Vboardv)
Board.Cells(Xboard, Yboard).Value = (Vboard)
Next r
End Sub
Hm, oke I'll recap what I mentioned in my comments above:
"It is getting stuck on the Xboard = CInt(Xboardv) line with the error "13""
You are creating an array of values with Xboardv = Table.Cells.Value. The array is sized 1 To 15, 1 To 1 and you need to use these index numbers as row and column parameters when you refer to any element in the array. So basically: Xboard = CInt(Xboardv(<X>,<Y>)).
"That did it but now it only does it for the first value of the array due to it being called out as (1,1)"
That is because you are constantly refering to the same element. However, you have created a loop with r variable allready. So you can use that to call different elements: Xboard = CInt(Xboardv(r,1))
I've neglected the fact that your structure is somewhat strange and you are creating the same array in a loop. So move that outside your loop and possibly use a For R = Lbound(Xboard) to Ubound(Xboard) loop instead. And you can just address values without having to convert them too.
Btw, no need for Integer variables at all. Use Long instead.
What's the reason for doing a conversion to begin with?
In cell 'D4', I've put the value 325, and I've run following piece of code:
Dim b As Integer
b = Range("D4").Value
Value b is 325, no problem. No conversion needed.

Draw a Tree/Lattice with VBA using an Array

I'm trying to generate a binomial tree for option evaluation, and I want to draw a lattice using values that comes from an array where values are already there.
So basically I have an array in my VBA that ressemble to this:
My array in vba
And I want to paste in an Excel sheet in this form:
How i want to paste in my worksheet
I don't necessarily ask for a code, I would just like to have an idea of the algorithm used to create the tree. I really tried to see any pattern and couldn't find.
What I identified is that if the number of columns of the array is (n+1), then then number of rows will be (2n-1).
Thanks in advance
This might give you an idea. You would need to tweak it if your array is not 1-based:
Sub InsertTree(Nodes As Variant, RootNode As Range)
'Assumes that Nodes is a 1-based array
'That has data 1-element in its first
'column, 2 in its second, etc. And that RootNode
'Has been chosen so that there is enough room
'for the tree
Dim i As Long, j As Long, n As Long
Dim TopNode As Range 'top node in each column
n = UBound(Nodes, 2)
Set TopNode = RootNode
For j = 1 To n
For i = 1 To j
TopNode.Offset(2 * (i - 1)).Value = Nodes(i, j)
Next i
If j < n Then Set TopNode = TopNode.Offset(-1, 1)
Next j
End Sub
As a test, I set my spreadsheet up so that beginning at A1 I had:
a b d g
c e h
f i
j
Then I ran:
InsertTree Range("A1:D4").Value,Range("F10")
And the result looks like this:

Subtracting Variants

I am having trouble getting Variants to subtract. I am pulling data from a spreadsheet and if one cell states a phrase then I need the code to subtract one cell from another. If the cell does not state a phrase then I need it to copy one cell to another. I can get the code to run but nothing happens.
Private Sub CommandButton1_Click()
Dim x As Variant, y As Variant, z As Variant, a As Integer, B As String
'getting values for data
x = Range("D2:D48").Value
y = Range("I2:I48").Value
z = Range("E2:E48").Value
B = "Total ISU Days: "
'The the cells are empty then subtract. This is not what I wanted to do but I can't think of extracting strings from variants.
If IsEmpty(Range("D2:D48").Value) = True Then
a = y - z
End If
Range("N2:N48").Value = a
Range("M2:M48").Value = B
End Sub
x = Range("D2:D48").Value
y = Range("I2:I48").Value
z = Range("E2:E48").Value
A Variant contains metadata about its subtype. In this case, x, y, and z are all arrays of variants.
a = y - z
The right-hand side of this expression simply cannot be evaluated, because {array1} - {array2} means nothing: operators (arithmetic or logical) work off values, not array of values.
What is a supposed to be? It's declared As Integer, so its value is capped at 32,767 (should probably be a Long). If you mean to add up all the values in y and subtract that total from the sum of all values in z, then you need to be more explicit about how you do that - you could use Application[.WorksheetFunction].Sum to add things up:
sumOfY = Application.Sum(Range("I2:I48"))
sumOfZ = Application.Sum(Range("E2:E48"))
a = sumOfY - sumOfZ
And then...
Range("N2:N48").Value = a
That will put the value of a in every single cell in the N2:N48 range - is that really what you mean to do?
Or maybe you meant to do this instead?
Range("N2:N48").Formula = "=IF(D2="""",I2-E2,0)"
That would make each cell in N2:N48 calculate the difference between I and E for each row where D is empty... and there's not really any need for any VBA code to do this.
Let's simplify a bit the task and say that the idea is to substract the values in Range("C1:C6") from the corresponding values in the left - Range("B1:B6"). Then write the corresponding results in column E:
Of course, this would be done only in case that all values in column A are empty. This is one way to do it:
Sub TestMe()
Dim checkNotEmpty As Boolean: checkNotEmpty = False
Dim substractFrom As Range: Set substractFrom = Worksheets(1).Range("B1:B6")
Dim substractTo As Range: Set substractTo = Worksheets(1).Range("C1:C6")
Dim MyCell As Range
Dim result() As Variant
ReDim result(substractFrom.Cells.Count - 1)
Dim areCellsEmpty As Boolean
For Each MyCell In substractFrom
If Len(MyCell) > 0 Then checkNotEmpty = True
Next
Dim i As Long
For i = LBound(result) + 1 To UBound(result) + 1
result(i - 1) = substractFrom.Cells(i) - substractTo.Cells(i)
Next
Worksheets(1).Range("E1").Resize(UBound(result) + 1) = Application.Transpose(result)
End Sub
The code could be improved further, saving all ranges to an Array, but it works quite ok so far.
The part with the +1 and -1 in the For-loop is needed as a workaround:
For i = LBound(result) + 1 To UBound(result) + 1
result(i - 1) = substractFrom.Cells(i) - substractTo.Cells(i)
Next
because the arrays start from index 0, but the Cells in a range start with row 1.
Worksheets(1).Range("E1").Resize(UBound(result) + 1) = Application.Transpose(result) is needed, to write the values of the result array to the column E, without defining the length of the range in E.

VBA if statement function multiply data from two columns

I am new to Vba and I have been trying to figure out how after an if statement to multiply two numbers in two different columns. the data in excel is laid out as below.What I am trying to do is to multiply the cost with the weight if the freighttype is for example store transfer but my code below does not work.Your help would be much appreciated.I do not know if I need two extra for loops for the cost and weight.
freighttype
Column(b)
Store Transfer
Ecommerce
Cost
Column(c)
7
6
Weight
column (e)
2
3
And the code is:
Option Explicit
Function essay(ft As Range) As Long
Dim x As Variant
For Each x In ft
If ft = "store transfer" Then
essay = Range("b2:b365").Offset(0, 1) * Range("b2:b365").Offset(0, 3)
Else
essay = 0
End If
Next x
End Function
Unlike Excel, you cannot multiply two arrays together in VBA.
For the equivalent, you can either loop through all the cells, multiplying them one by one and keeping a running total, or you can use the SUMPRODUCT worksheet function inside EVALUATE
Assuming, for example, that your ft range is in column B, starting with B2, you could use something like:
Option Explicit
Option Compare Text
Function essay(ft As Range) As Long
essay = Evaluate("=SUMPRODUCT((" & ft.Address & "=""store transfer"")*OFFSET(" & ft.Address & ",0,1)*OFFSET(" & ft.Address & ",0,3))")
End Function
for looping:
Function essay2(ft As Range) As Long
Dim c As Range
Dim L As Long
For Each c In ft
If c = "store transfer" Then _
L = L + c.Offset(0, 1) * c.Offset(0, 3)
Next c
essay2 = L
End Function
Note that the Option Compare Text statement makes the routine case insensitive.
Hi Guys I managed to solve the problem with your help ,please find the solution below.
Option Explicit
Function ecco(ft As Range) As Long
Dim x As Variant
Dim L As Long
For Each x In ft
If ft = "st" Then
L = x.Offset(0, 1) * x.Offset(0, 3)
Else
ecco = 0
End If
ecco = L
Next x
End Function

Excel Geomean returns #value! sometimes?

I've modified the vba function below to suit my needs.
I have many workbooks with sheets that contain 4500+ rows, and I use the function to search for two given values (as boundaries). Then, it selects the rows as the range. Finally, do whatever on that range. The function:
Function GeoM(A, B)
Application.Volatile
Dim x As Integer
Dim y As Integer
Dim rng As Range
x = Application.WorksheetFunction.Match(A, Range("B:B"), 0) ' looking in col B
y = Application.WorksheetFunction.Match(B, Range("B:B"), 0) ' looking in col B
Set rng = Range(Cells(x, 18), Cells(y, 18)) 'Im working on col 18
GeoM = Application.WorksheetFunction.GeoMean(rng)
End Function
The problem is, this code works just fine except with GeoMeann. I noticed when the range of data is relatively small (number of data cells) it returns a value. However, if the range is larger than approx. 126 cells, it returns #value!.
I'm stuck and working on solving this issue. Is the GeoMean function limited to a given number of data?
Thanks
There appears to be a 170 character limit on my testing for earlier Excel versions (I tested in xl03), validated in this Mr Excel thread
(Xl10 worked fine on the longer dataset)
I also tried:
Using Evaluate
Using a 1D array
failed samples
Dim X
Set rng1 = Range("A1:A171")
MsgBox Evaluate("GeoMean(A1:A171)")
X = Application.Transpose(rng1)
MsgBox Application.WorksheetFunction.GeoMean(X)
to no avail.
So I think your two workarounds are either:
Inserting a formula via VBA into Excel and using this result
As per the MrExcel thread use the derivation of GeoMean, ie =EXP(AVERAGE(LN(Range)))
Suggested Approach
MsgBox Evaluate("EXP(AVERAGE(LN(A1:A171)))")
Thanks to brettdj, I fixed the function and it works now:
Function GeoM(A, B)
Application.Volatile
Dim x As Integer
Dim y As Integer
Dim rng As Range
Dim LnValue As Double
Dim count As Integer
x = Application.WorksheetFunction.Match(A, Range("B:B"), 0) 'look in col. B
y = Application.WorksheetFunction.Match(B, Range("B:B"), 0) 'look in col. B
Set rng = Range(Cells(x, 18), Cells(y, 18)) 'set range of rows on col# 18
Do
LnValue = LnValue + Math.Log(Cells(x, 18)) 'calculates sum of ln(value)
x = x + 1
count = count + 1 'calculates the total number of values
Loop Until x > y 'stop when x (upper row#) is greater than y (lower row#)
GeoM = Math.Exp((1 / count) * LnValue) 'GeoMean formula
End Function
This function searches a specified column for two values as upper and lower limits (Note: that means you shouldn't have repeated values in that column. In another words, the column should have unique values). Then, it finds the GeoMean of the values on other column, which has values fall in the same range of rows.

Resources