How to apply Linest function in VBA? - vba

I trying to get a third order LinEst function in VBA. However, the error always come out as Expected array when it reaches Ubound(xl).
Option Explicit
Sub RB()
Dim xl As Range, e As Double
Dim yl As Range, s As Variant
Dim X
With ThisWorkbook.Worksheets("Sheet1")
Set yl = .Range(.Cells(17, 7), .Cells(93, 7))
Set xl = .Range(.Cells(17, 1), .Cells(93, 1))
ReDim arrX3(1 To UBound(xl), 1 To 3) As Double
For i = LBound(xl) To UBound(xl)
arrX2(i, 1) = xl(i, 1)
arrX2(i, 2) = xl(i, 1) * xl(i, 1)
arrX2(i, 3) = xl(i, 1) * xl(i, 1) * xl(i, 1)
Next
X = Application.LinEst(yl, arrX3)
.Range(.Cells(12, 12), .Cells(15, 14)).Value = Application.Transpose(X)
End With
End Sub

xl is a Range and not an array. So, Ubound(xl) won't work. While I do not understand what you're code is trying to achieve, I believe that you are looking for something along the line like this:
Option Base 1
Option Explicit
Sub RB()
Dim xl As Range, e As Double
Dim yl As Range, s As Variant
Dim X As Variant, i As Long
e = 76
With ThisWorkbook.Worksheets("Sheet1")
Set yl = .Range(.Cells(17, 7), .Cells(e - 1, 7))
Set xl = .Range(.Cells(17, 1), .Cells(e - 1, 1))
Debug.Print "First row in xl is " & xl.Row
Debug.Print "Range xl has " & xl.Rows.Count & " rows"
Debug.Print "Last row in xl is " & xl.Rows.Count + xl.Row - 1
ReDim arrX3(1 To xl.Rows.Count, 1 To 3) As Double
For i = 1 To xl.Rows.Count
arrX3(i, 1) = xl.Cells(i, 1)
arrX3(i, 2) = xl.Cells(i, 1) * xl.Cells(i, 1)
arrX3(i, 3) = xl.Cells(i, 1) * xl.Cells(i, 1) * xl.Cells(i, 1)
Next i
X = Application.LinEst(yl, arrX3)
.Range(.Cells(12, 12), .Cells(15, 14)).Value = Application.Transpose(X)
End With
End Sub
Note, that I added a few Debug.Print which you might want to have a look at.

xl is declared to be a range and ranges don't have a Ubound.
Change the declaration of xl from Range to Variant and replace the line
Set xl = .Range(.Cells(17, 1), .Cells(93, 1))
by
xl = .Range(.Cells(17, 1), .Cells(93, 1)).Value
I'm not sure if this will be enough to make your code run as expected, but it will at least get rid of the error that you describe.

Related

VBA Matrix addition after previous multiplication

I want to add two matrices, after I recieved one of the two by a matrix multiplication. The Formular I want to calculate is: ((TS x TI) + TI) x PK = TK
Dim TS_Matrix As Variant, TI_Matrix As Variant, Dummy_Matrix As Variant, PK_Matrix As Variant, TK_Matrix As Variant
'Read matrices
TS_Matrix = Worksheets(1).Range("B2:E5")
TI_Matrix = Worksheets(2).Range("B2:E5")
PK_Matrix = Worksheets(3).Range("B2:B5")
'Calculation
Dummy_Matrix = Application.MMult(TS_Matrix, TI_Matrix)
Dummy_Matrix = Dummy_Matrix + TI_Matrix
TK_Matrix = Application.MMult(Dummy_Matrix, PK_Matrix)
'Write
Worksheets(4).Range("B2:B5") = TK_Matrix
Without the addition it works perfectly. How do I fix it? The following line gives me a
run-time error ‘13’: Type mismatch.
Dummy_Matrix = Dummy_Matrix + TI_Matrix
Thank you in advance!
You cannot add 2 matrices like this Dummy_Matrix = Dummy_Matrix + TI_Matrix because VBA doesn't support to add 2 arrays out of the box. Instead you would need to loop through all elements of the array to add each by each.
Here is an example:
Option Explicit
Public Sub TestMatrixAdd()
Dim MatrixA As Variant
Dim MatrixB As Variant
Dim MatrixOut As Range 'note output must be a range
With Worksheets("Sheet1") 'adjust to your sheet
MatrixA = .Range("A1:B5")
MatrixB = .Range("D1:E5")
Set MatrixOut = .Range("G1:H5")
End With
MatrixOut = AddMatrices(MatrixA, MatrixB)
End Sub
Public Function AddMatrices(MatrixA As Variant, MatrixB As Variant) As Variant
'matrices must be of the same size
If LBound(MatrixA, 1) <> LBound(MatrixB, 1) Or _
LBound(MatrixA, 2) <> LBound(MatrixB, 2) Or _
UBound(MatrixA, 1) <> UBound(MatrixB, 1) Or _
UBound(MatrixA, 2) <> UBound(MatrixB, 2) Then
GoTo SIZE_ERROR
End If
Dim MatrixOut As Variant
ReDim MatrixOut(LBound(MatrixA, 1) To UBound(MatrixA, 1), LBound(MatrixA, 2) To UBound(MatrixA, 2))
'matrix addition
Dim i As Long, j As Long
For i = LBound(MatrixA, 1) To UBound(MatrixA, 1)
For j = LBound(MatrixA, 2) To UBound(MatrixA, 2)
MatrixOut(i, j) = MatrixA(i, j) + MatrixB(i, j)
Next j
Next i
AddMatrices = MatrixOut
Exit Function
SIZE_ERROR:
AddMatrices = "Matrices must be of the same size"
End Function

VBA refining range

I am attempting to draw data from a separate sheet and put it into a corresponding cell if the conditions are met. My code works, but it is not efficient. I do not know how to change the For Next loop so that it attempts to draw data only until the final entry. Right now I have it set to go a hundred or so cells further than I need so that I wouldn't have to update the code as often when I input new data to the data sheet (or at least that was the thought). Here is my code:
Sub LRearTest()
Dim R As Integer
Dim j As Integer
For j = 89 To 250
For R = 1 To 300
If Worksheets("Input").Cells(j, 22).Value >= Worksheets("1036L").Cells(R, 5).Value And Worksheets("Input").Cells(j, 22).Value <= Worksheets("1036L").Cells(R, 6).Value Then
Worksheets("Input").Cells(j, 20).Value = Worksheets("1036L").Cells(R, 3).Value
End If
Next R
Next j
End Sub
The problem is when I run this code it takes almost two minutes before it is over. I am not sure if it is because I have used j and r as integers or what. Also I have a dozen of these on one module so I am not sure if that contributes. The code works like I said, it is just far too slow. Help is greatly appreciated.
The point that I am checking is in Column V of Sheet "Input". Each of my columns that I want to populate, F - U, use the same data in column V. The sheets that I am comparing the data in column V against are labeled as 1030L, 1030R, 1031L, 1031R, 1032L, 1032R, 1033L, 1033R, 1034L, 1034R, 1034LA, 1034RA, 1035L, 1035R, 1036L, and 1036R. The data being compared is in the same columns in every sheet. Thank you
Something like this should work for you:
Sub LRearTest()
Dim wb As Workbook
Dim wsInput As Worksheet
Dim wsData As Worksheet
Dim aDataParams() As String
Dim aInput As Variant
Dim aData As Variant
Dim InputIndex As Long
Dim DataIndex As Long
Dim ParamIndex As Long
Dim MinCol As Long
Set wb = ActiveWorkbook
Set wsInput = wb.Sheets("Input")
'Adjust the column associations for each sheet as necessary
ReDim aDataParams(1 To 16, 1 To 3)
aDataParams(1, 1) = "1030L": aDataParams(1, 2) = "F"
aDataParams(2, 1) = "1030R": aDataParams(2, 2) = "G"
aDataParams(3, 1) = "1031L": aDataParams(3, 2) = "H"
aDataParams(4, 1) = "1031R": aDataParams(4, 2) = "I"
aDataParams(5, 1) = "1032L": aDataParams(5, 2) = "J"
aDataParams(6, 1) = "1032R": aDataParams(6, 2) = "K"
aDataParams(7, 1) = "1033L": aDataParams(7, 2) = "L"
aDataParams(8, 1) = "1033R": aDataParams(8, 2) = "M"
aDataParams(9, 1) = "1034L": aDataParams(9, 2) = "N"
aDataParams(10, 1) = "1034R": aDataParams(10, 2) = "O"
aDataParams(11, 1) = "1034LA": aDataParams(11, 2) = "P"
aDataParams(12, 1) = "1034RA": aDataParams(12, 2) = "Q"
aDataParams(13, 1) = "1035L": aDataParams(13, 2) = "R"
aDataParams(14, 1) = "1035R": aDataParams(14, 2) = "S"
aDataParams(15, 1) = "1036L": aDataParams(15, 2) = "T"
aDataParams(16, 1) = "1036R": aDataParams(16, 2) = "U"
'Find minimum column
MinCol = wsInput.Columns.Count
For ParamIndex = LBound(aDataParams, 1) To UBound(aDataParams, 1)
If wsInput.Columns(aDataParams(ParamIndex, 2)).Column < MinCol Then MinCol = wsInput.Columns(aDataParams(ParamIndex, 2)).Column
Next ParamIndex
'Based on minimum column, determine column indexes for each sheet/column pair
For ParamIndex = LBound(aDataParams, 1) To UBound(aDataParams, 1)
aDataParams(ParamIndex, 3) = wsInput.Columns(aDataParams(ParamIndex, 2)).Column - MinCol + 1
Next ParamIndex
With wsInput.Range("F89", wsInput.Cells(wsInput.Rows.Count, "V").End(xlUp))
If .Row < 89 Then
MsgBox "No data in sheet [" & wsInput.Name & "]"
Exit Sub
End If
aInput = .Value
End With
For ParamIndex = LBound(aDataParams, 1) To UBound(aDataParams, 1)
'Define data sheet based on current column
Set wsData = wb.Sheets(aDataParams(ParamIndex, 1))
aData = wsData.Range("C1", wsData.Cells(wsData.Rows.Count, "F").End(xlUp)).Value
For InputIndex = LBound(aInput, 1) To UBound(aInput, 1)
For DataIndex = LBound(aData, 1) To UBound(aData, 1)
If aInput(InputIndex, UBound(aInput, 2)) >= aData(DataIndex, 3) _
And aInput(InputIndex, UBound(aInput, 2)) <= aData(DataIndex, 4) Then
aInput(InputIndex, aDataParams(ParamIndex, 3)) = aData(DataIndex, 1)
Exit For
End If
Next DataIndex
Next InputIndex
Set wsData = Nothing
Erase aData
Next ParamIndex
wsInput.Range("F89").Resize(UBound(aInput, 1), UBound(aInput, 2)) = aInput
Set wb = Nothing
Set wsInput = Nothing
Set wsData = Nothing
Erase aInput
Erase aData
Erase aDataParams
End Sub

excel vba I need to transpose data from columns to rows

I am looking for a VBA solution to transform data from a scenario similar to the illustration below. From Sheet1 copy first three cell values (A3,B3,C3) only if there is a value in any cell to the left of them (D3,E3,...) in Sheet2 past first 3 cell values (A2,B2,C2), and the first cell after that with a value (D3) and also copy the header value into the adjacent cell. Any additional values to the left get the same treatment and become the next row, again copying (A3,B3,C3). Then the next adjacent cell value (E3) along with the header value into the adjacent cell. Then move down to the next row in Sheet1 where there are values after the first 3 cells until it has looped all the way through sheet1 to produce the example in Sheet2.
I have searched for other similar solutions but cannot find anything that works. This is the closest I've found with minor edits on my part but doesn’t work, any help is greatly appreciated.
Sub Sample()
Dim wsThis As Worksheet
Dim wsThat As Worksheet
Dim ThisAr As Variant
Dim ThatAr As Variant
Dim Lrow As Long
Dim Col As Long
Dim i As Long
Dim k As Long
Set wsThis = Sheet1: Set wsThat = Sheet2
With wsThis
'~~> Find Last Row in Col A
Lrow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Find total value in D,E,F so that we can define output array
Col = Application.WorksheetFunction.CountA(.Range("C2:G" & Lrow))
'~~> Store the values from the range in an array
ThisAr = .Range("A2:G" & Lrow).Value
'~~> Define your new array
ReDim ThatAr(1 To Col, 1 To 7)
'~~> Loop through the array and store values in new array
For i = LBound(ThisAr) To UBound(ThisAr)
k = k + 1
ThatAr(k, 1) = ThisAr(i, 1)
ThatAr(k, 2) = ThisAr(i, 2)
ThatAr(k, 3) = ThisAr(i, 3)
'~~> Check for Color 1
If ThisAr(i, 5) <> "" Then 'ThatAr(k, 4) = ThisAr(i, 4)
k = k + 1
ThatAr(k, 1) = ThisAr(i, 1)
ThatAr(k, 2) = ThisAr(i, 2)
ThatAr(k, 3) = ThisAr(i, 3)
ThatAr(k, 4) = ThisAr(i, 4)
ThatAr(k, 5) = ThisAr(i, 5)
End If
'~~> Check for Color 2
If ThisAr(i, 7) <> "" Then
k = k + 1
ThatAr(k, 1) = ThisAr(i, 1)
ThatAr(k, 2) = ThisAr(i, 2)
ThatAr(k, 3) = ThisAr(i, 3)
ThatAr(k, 6) = ThisAr(i, 6)
ThatAr(k, 7) = ThisAr(i, 7)
End If
'~~> Check for Color 3
'If ThisAr(i, 6) <> "" Then
'k = k + 1
'ThatAr(k, 1) = ThisAr(i, 1)
'ThatAr(k, 2) = ThisAr(i, 2)
'ThatAr(k, 3) = ThisAr(i, 3)
'ThatAr(k, 4) = ThisAr(i, 6)
'End If
Next i
End With
'~~> Create headers in Sheet2
Sheet2.Range("A1:D1").Value = Sheet1.Range("A1:D1").Value
'~~> Output the array
wsThat.Range("A2").Resize(Col, 4).Value = ThatAr
End Sub
Using a variant array(dynamic array) is simple and fast.
Sub test()
Dim wsThis As Worksheet, wsThat As Worksheet
Dim vDB As Variant, vR() As Variant
Dim r As Long, i As Long, n As Long
Dim c As Integer, j As Integer, k As Integer
Set wsThis = Sheet1: Set wsThat = Sheet2
vDB = wsThis.Range("a1").CurrentRegion
r = UBound(vDB, 1)
c = UBound(vDB, 2)
For i = 2 To r
For j = 4 To c
If vDB(i, j) <> "" Then
n = n + 1
ReDim Preserve vR(1 To 5, 1 To n)
For k = 1 To 3
vR(k, n) = vDB(i, k)
Next k
vR(4, n) = vDB(i, j)
vR(5, n) = vDB(1, j)
End If
Next j
Next i
With wsThat
.UsedRange.Clear
.Range("a1").Resize(1, 3) = wsThis.Range("a1").Resize(1, 3).Value
.Range("d1").Resize(1, 2) = Array("Value", "ID#")
.Range("a2").Resize(n, 5) = WorksheetFunction.Transpose(vR)
End With
End Sub
Sorry, I'm not sure why I couldn't open your attached images.
But you might want to try this code:
Change this line:
wsThat.Range("A2").Resize(Col, 4).Value = ThatAr
To
wsThat.Range("A2").Resize(4, Col).Value = WorksheetFunction.Transpose(ThatAr)
Hope this help

Referencing worksheets to draw data

I am writing a vba macro to allow me to reference data from a worksheet and summarize some of the data rather than using a ton of formulas to do so.
I am having difficulties in referencing worksheets and have reverted to activating sheets. I'm not sure what I am doing incorrectly. For example:
Sheets("Rainfall").Activate
Set x = Range(Range("C2"), Range("C2").End(xlDown))
rather than
Set x = Sheets("Rainfall").Range(Range("C2"), Range("C2").End(xlDown))
When I attempt to reference code such as
Cells(2 + j, 3) = Application.WorksheetFunction.VLookup(Cells(2 + j, 2), Worksheets("Raw Data").Range(Range("C4"), Range("H4").End(xlDown)), 6, False)
I get a 1004 error. Below is my code and if anyone has any suggestions on the simplification of the code that would be great as well.
Sub selectall()
Dim x, y As Range
Dim nv, rd As Long
Set Wkb = Workbooks("DWH Calculations V1.xlsm")
Sheets("Rainfall").Activate
Set x = Range(Range("C2"), Range("C2").End(xlDown))
nv = x.Rows.Count
'MsgBox (nv)
Sheets("Raw Data").Activate
Set y = Range(Range("E4"), Range("E4").End(xlDown))
rd = y.Rows.Count
'MsgBox (rd)
MinD = Round(Application.WorksheetFunction.Min(y), 0)
MaxD = Round(Application.WorksheetFunction.Max(y), 0)
Ndays = MaxD - MinD
'MsgBox (Ndays)
Sheets("Rainfall").Activate
Cells(2, 2) = MinD
For j = 1 To Ndays - 1
Cells(2 + j, 2) = Cells(1 + j, 2) + 1
Cells(2 + j, 3) = Application.WorksheetFunction.VLookup(Cells(2 + j, 2), Worksheets("Raw Data").Range(Range("C4"), Range("H4").End(xlDown)), 6, False)
Next j
End Sub
Thank you all for your help
This has been asked many times before - you need to qualify all the Range calls with a worksheet object, so:
Set x = Sheets("Rainfall").Range(Sheets("Rainfall").Range("C2"), Sheets("Rainfall").Range("C2").End(xlDown))
or use a With...End With block:
With Sheets("Rainfall")
Set x = .Range(.Range("C2"), .Range("C2").End(xlDown))
End With
and note the periods before all three Range calls. You can also use a Worksheet variable:
Dim ws as Worksheet
Set ws = Sheets("Rainfall")
Set x = ws.Range(ws.Range("C2"), ws.Range("C2").End(xlDown))
The problem is the range-within-range:
replace:
Set x = Range(Range("C2"), Range("C2").End(xlDown))
with:
With Sheets("Rainfall")
Set x = .Range(.Range("C2"), .Range("C2").End(xlDown))
End With
Activate is not needed to Set ranges.

How to add items from a list to cells in a sheet

I'm trying to add the items from a list to some rows in an Excel Sheet.
I tried to do it this way:
Dim Rand As Long
Dim ws As Worksheet
Set ws = Worksheets("Necmontage")
Rand = ws.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
Range(ws.Cells(Rand, 1), ws.Cells(Rand + necesar.ListCount - 1, 1)).Merge
ws.Cells(Rand, 1) = "K"
Range(ws.Cells(Rand, 2), ws.Cells(Rand + necesar.ListCount - 1, 2)).Merge
ws.Cells(Rand, 2) = "Montage"
Range(ws.Cells(Rand, 3), ws.Cells(Rand + necesar.ListCount - 1, 3)).Merge
ws.Cells(Rand, 3) = comanda.Caption
Dim i As Integer
i = 0
Do While i = necesar.ListCount - 1
ws.Cells(Rand + i, 4) = necesar.List(i, 0)
i = i + 1
Loop
End Sub
It adds all the values I want except the values from the List (where I do that While Loop). I don't know why but it doesn't take the values. Any idea about this problem?
Did you mean in your code:
Do While i <= necesar.ListCount - 1 'instead of =
ws.Cells(Rand + i, 4) = necesar.List(i, 0)
i = i + 1
Loop
Btw, you can see in debug mode by putting a breakpoint on the Do While line if the program goes where you wanted it to.