Computing the ChiSquare - vba

I am writing a user-defined function in excel vba. So this new function:
takes 4 input value
some calculation to generate into 8 numbers. ( 2 arrays - each array has 4 numbers)
do a chisquare test
return 1 output value
Code:
Sub test()
Dim A, B, C, D As Variant
A = 33
B = 710
C = 54
D = 656
'Observed Value
Dim O_A As Variant
Dim O_B As Variant
Dim O_V As Variant
Dim O_D As Variant
'Define Observer Value
O_C_A = 'Some Calucation'
O_C_B = 'Some Calucation'
O_T_C = 'Some Calucation'
O_T_C = 'Some Calucation'
'Expected Value
Dim E_C_A As Variant
Dim E_C_B As Variant
Dim E_T_C As Variant
Dim E_T_D As Variant
'Define Expected Value
E_C_A = 'Some Calucation'
E_C_B = 'Some Calucation'
E_T_C = 'Some Calucation'
E_T_D = 'Some Calucation'
'Create array(2x2)
Dim Chi_square_result As Variant
Dim my_array(1, 1)
my_array(0, 0) = O_C_Mesaurement
my_array(0, 1) = O_C_Balance
my_array(1, 0) = O_T_Measurement
my_array(1, 1) = O_T_Balance
Dim my_array2(1, 1)
my_array2(0, 0) = E_C_Mesaurement
my_array2(0, 1) = E_C_Balance
my_array2(1, 0) = E_T_Measurement
my_array2(1, 1) = E_T_Balance
'Create a chi square test formula'
Dim formula(1 To 5) As String
formula(1) = "CHITEST("
formula(2) = my_array
formula(3) = ","
formula(4) = my_array2
formula(5) = ")"
'Chi Square
Chi_square_result = evaluate(Join(formula, ""))
end sub
It gives a runtime error '13', saving type mismatch. This is because of the concatenation of the formula.

If you are writing a function, you have your format wrong.
Function Chi_square_result(A as Long, B as Long, C as Long, D as Long) as Double
'All your manipulations here
Chi_square_result = (Your math equation)
End Function
You also never defined my_array1, I am assuming it is supposed to be where you typed 'my_array'. I also do not think Join is your best bet. You are trying to do an awful lot of array manipulation, and I think your dimensions are getting you. It would be better to do it in a more straight forward way.

The evaluate is expecting worksheet cell ranges. Use the Excel Application object or WorksheetFunction object to compute the function within VBA.
This proofs out.
Dim dbl As Double
Dim my_array1(1, 1)
my_array1(0, 0) = 1
my_array1(0, 1) = 2
my_array1(1, 0) = 3
my_array1(1, 1) = 4
Dim my_array2(1, 1)
my_array2(0, 0) = 2
my_array2(0, 1) = 3
my_array2(1, 0) = 4
my_array2(1, 1) = 5
dbl = Application.ChiTest(my_array1, my_array2)
Debug.Print dbl
Result from the VBE's Immediate window: 0.257280177154182.

Related

Change variable type mysteriously

I have a sub with a variant variable set up that changes its type midway for now apparent reason.
I have the variables declared at the beginning of the procedure:
Dim acsp As Variant
Dim oldmaster As Variant
Dim acontacts As Variant
Dim avp As Variant
Dim acctst As Variant
Dim ashipto As Variant
Dim abillto As Variant
Dim found, found1, found2 As Boolean
acsp = Sheet6.UsedRange.Value2
acontacts = Sheet5.UsedRange.Value2
avp = Sheet9.UsedRange.Value2
acctst = Sheet20.UsedRange.Value2
ashipto = Sheet11.UsedRange.Value
abillto = Sheet15.UsedRange.Value
The code runs and based on an IF condition it might call this code (inside the same sub):
c = UBound(acsp) + 1
shipto = Trim(UCase(acctst(aa, 27)))
billto = Trim(UCase(acctst(aa, 38)))
shiptofound = False
For shiptorow = 2 To UBound(ashipto)
ashipto1 = Trim(UCase(ashipto(shiptorow, 2)))
If ashipto1 = shipto Then
shiptofound = True
Exit For
End If
Next shiptorow
Up until the shiptofound=False line the abillto variant is shown as variant/variant (1 to 677, 1 to 18) which is correct.
But as soon it runs the line For shiptorow = 2 To UBound(ashipto) then the abillto variant changes to a type variant/long with a value of 2?
At no point prior to this code abillto is used in the code.
Why does VBA do this?

Split text lines into words and decide which one is correct based on voting

The following code splits each lines into words and store the first words in each line into array list and the second words into another array list and so on. Then it selects the most frequent word from each list as correct word.
Module Module1
Sub Main()
Dim correctLine As String = ""
Dim line1 As String = "Canda has more than ones official language"
Dim line2 As String = "Canada has more than one oficial languages"
Dim line3 As String = "Canada has nore than one official lnguage"
Dim line4 As String = "Canada has nore than one offical language"
Dim wordsOfLine1() As String = line1.Split(" ")
Dim wordsOfLine2() As String = line2.Split(" ")
Dim wordsOfLine3() As String = line3.Split(" ")
Dim wordsOfLine4() As String = line4.Split(" ")
For i As Integer = 0 To wordsOfLine1.Length - 1
Dim wordAllLinesTemp As New List(Of String)(New String() {wordsOfLine1(i), wordsOfLine2(i), wordsOfLine3(i), wordsOfLine4(i)})
Dim counts = From n In wordAllLinesTemp
Group n By n Into Group
Order By Group.Count() Descending
Select Group.First
correctLine = correctLine & counts.First & " "
Next
correctLine = correctLine.Remove(correctLine.Length - 1)
Console.WriteLine(correctLine)
Console.ReadKey()
End Sub
End Module
My Question: How can I make it works with lines of different number of words. I mean that the length of each lines here is 7 words and the for loop works with this length (length-1). Suppose that line 3 contains 5 words.
EDIT: Accidentally had correctIndex where shortest should have been.
From what I can tell you are trying to see which line is the closest to the correctLine.
You can get the levenshtein distance using the following code:
Public Function LevDist(ByVal s As String,
ByVal t As String) As Integer
Dim n As Integer = s.Length
Dim m As Integer = t.Length
Dim d(n + 1, m + 1) As Integer
If n = 0 Then
Return m
End If
If m = 0 Then
Return n
End If
Dim i As Integer
Dim j As Integer
For i = 0 To n
d(i, 0) = i
Next
For j = 0 To m
d(0, j) = j
Next
For i = 1 To n
For j = 1 To m
Dim cost As Integer
If t(j - 1) = s(i - 1) Then
cost = 0
Else
cost = 1
End If
d(i, j) = Math.Min(Math.Min(d(i - 1, j) + 1, d(i, j - 1) + 1),
d(i - 1, j - 1) + cost)
Next
Next
Return d(n, m)
End Function
And then, this would be used to figure out which line is closest:
Dim correctLine As String = ""
Dim line1 As String = "Canda has more than ones official language"
Dim line2 As String = "Canada has more than one oficial languages"
Dim line3 As String = "Canada has nore than one official lnguage"
Dim line4 As String = "Canada has nore than one offical language"
Dim lineArray As new ArrayList
Dim countArray As new ArrayList
lineArray.Add(line1)
lineArray.Add(line2)
lineArray.Add(line3)
lineArray.Add(line4)
For i = 0 To lineArray.Count - 1
countArray.Add(LevDist(lineArray(i), correctLine))
Next
Dim shortest As Integer = Integer.MaxValue
Dim correctIndex As Integer = 0
For i = 0 To countArray.Count - 1
If countArray(i) <= shortest Then
correctIndex = i
shortest = countArray(i)
End If
Next
Console.WriteLine(lineArray(correctIndex))

loop through worksheet with different names using VBA

Dim Var_1 As String
Dim Var_2 As String
Dim Var_3 As String
Dim Var_4 As String
Dim Var_5 As String
Dim Var_6 As String
Dim Var_7 As String
Dim Var_8 As String
Dim Var_9 As String
Dim Var_10 As String
Dim Var_11 As String
Dim Var_12 As String
Dim Var_13 As String
Dim Var_14 As String
Dim Var_15 As String
Dim Var_16 As String
Dim Var_17 As String
Dim Var_18 As String
Dim Var_19 As String
Dim Var_20 As String
Dim Var_21 As String
Dim Var_22 As String
Dim Var_23 As String
Dim Var_24 As String
Dim Var_25 As String
Dim Var_26 As String
Dim Var_27 As String
Dim Var_28 As String
Dim Var_29 As String
Var_1 = "Ex-Bidadi"
Var_2 = "Ex-Hospet"
Var_3 = "Ex-Chennai"
Var_4 = "Ex-Coimbatore"
Var_5 = "Ex-Gangaikondan"
Var_6 = "Ex-Pune"
Var_7 = "Ex-Goa"
Var_8 = "Ex-Mumbai"
Var_9 = "Ex-Nashik"
Var_10 = "Ex-Aurangabad"
Var_11 = "Ex-Goblej"
Var_12 = "Ex-Hyderabad"
Var_13 = Ex - Vizag
Var_14 = "Ex-Vijayawada"
Var_15 = "Ex-Chittoor"
Var_16 = "Ex - Siliguri"
Var_17 = "Ex-odhisha"
Var_18 = "Ex-Jharkhand"
Var_19 = "Ex-Bihar"
Var_20 = "Ex-NorthEast"
Var_21 = "Ex-Delhi"
Var_22 = "Ex-Udaipur"
Var_23 = "Ex-Jammu"
Var_24 = "Ex-Haridwar"
Var_25 = "Ex-Dasna"
Var_26 = "Ex-Kanpur"
Var_27 = "Ex-Unnao"
Var_28 = "Ex-Var_anasi"
Var_29 = "Ex-Bhopal"
I am showing you a part of my code and i just want to known how to loop these I tried using this representation
For n = 1 to 29
For i = 3 To 445
For m = 28 To 40
ActiveWorkbook.Sheets("Var_" & n).Cells(i, m) = 999999
least(i, m, n) = ActiveWorkbook.Sheets("Var_" & n).Cells(i, m)
Next m
Next i
Next n
I have defined the least(i,m,n) array but the loop is showing an error at the subscript is out of Range I have tried using all the possibilities but the loop doesn't work
You need to store the sheet names in an array:
Var = Array("Ex-Bidadi", "Ex-Hospet", "Ex-Chennai", "Ex-Coimbatore", "Ex-Gangaikondan", "Ex-Pune", "Ex-Goa", "Ex-Mumbai", "Ex-Nashik", "Ex-Aurangabad", "Ex-Goblej", "Ex-Hyderabad", "Ex - Vizag", "Ex-Vijayawada", "Ex-Chittoor", "Ex - Siliguri", "Ex-odhisha", "Ex-Jharkhand", "Ex-Bihar", "Ex-NorthEast", "Ex-Delhi", "Ex-Udaipur", "Ex-Jammu", "Ex-Haridwar", "Ex-Dasna", "Ex-Kanpur", "Ex-Unnao", "Ex-Var_anasi", "Ex-Bhopal")
Then inside your loop use the following:
least(i, m, n) = ActiveWorkbook.Sheets(Var(n)).Cells(i, m)
Create an array of your sheet
Dim Var
Var = Array("Ex-Bidadi"", "Ex-Hospet", "Ex-Chennai" ... till the last sheet)
Count the number of sheets that included on your array. Array starts at index 0.
For n = 0 to 28 '29 sheets less 1, because of the array index 0
For i = 3 To 445
For m = 28 To 40
ActiveWorkbook.Sheets(Var(n)).Cells(i, m) = 999999
least(i, m, n) = ActiveWorkbook.Sheets(Var(n)).Cells(i, m)
Next m
Next i
Next n
Loop through all sheets and pick just the ones you need in a SELECT CASE statement:
Sub Test()
Dim wrkSht As Worksheet
Dim i As Long, m As Long
Dim cLeast As Collection
Set cLeast = New Collection
For Each wrkSht In ThisWorkbook.Worksheets
'NB: If you want sheets that start with "Ex-" use commented lines instead:
'Select Case Left(wrkSht.Name, 3)
Select Case wrkSht.Name
'Case "Ex-"
Case "Ex-Bidadi", "Ex-Hospet", "Ex-Chnnai"
For i = 3 To 445
For m = 28 To 40
cLeast.Add wrkSht.Cells(i, m), wrkSht.Name & "|" & i & "|" & m
Next m
Next i
Case Else
'Code if not the sheet you're after.
End Select
Next wrkSht
Debug.Print cLeast("Ex-Bidadi|3|28")
End Sub
NB: I've used a collection in the loop as not sure what you're after. Dictionaries are probably the better way to go.

VBA - Setting multidimensional array values in one line

Right, so using Python I would create a multidimensional list and set the values on one line of code (as per the below).
aryTitle = [["Desciption", "Value"],["Description2", "Value2"]]
print(aryTitle[0,0] + aryTitle[0,1])
I like the way I can set the values on one line. In VBA I am doing this by:
Dim aryTitle(0 To 1, 0 To 1) As String
aryTitle(0, 0) = "Description"
aryTitle(0, 1) = "Value"
aryTitle(1, 0) = "Description2"
aryTitle(1, 1) = "Value2"
MsgBox (aryTitle(0, 0) & aryTitle(0, 1))
Is there a way to set the values in one line of code?
Not natively, no. But you can write a function for it. The only reason Python can do that is someone wrote a function to do it. The difference is that they had access to the source so they could make the syntax whatever they like. You'll be limited to VBA function syntax. Here's a function to create a 2-dim array. It's not technically 'one line of code', but throw it in your MUtilities module and forget about it and it will feel like one line of code.
Public Function FillTwoDim(ParamArray KeyValue() As Variant) As Variant
Dim aReturn() As Variant
Dim i As Long
Dim lCnt As Long
ReDim aReturn(0 To ((UBound(KeyValue) + 1) \ 2) - 1, 0 To 1)
For i = LBound(KeyValue) To UBound(KeyValue) Step 2
If i + 1 <= UBound(KeyValue) Then
aReturn(lCnt, 0) = KeyValue(i)
aReturn(lCnt, 1) = KeyValue(i + 1)
lCnt = lCnt + 1
End If
Next i
FillTwoDim = aReturn
End Function
Sub test()
Dim vaArr As Variant
Dim i As Long
Dim j As Long
vaArr = FillTwoDim("Description", "Value", "Description2", "Value2")
For i = LBound(vaArr, 1) To UBound(vaArr, 1)
For j = LBound(vaArr, 2) To UBound(vaArr, 2)
Debug.Print i, j, vaArr(i, j)
Next j
Next i
End Sub
If you supply an odd number of arguments, it ignores the last one. If you use 3-dim arrays, you could write a function for that. You could also write a fancy function that could handle any dims, but I'm not sure it's worth it. And if you're using more than 3-dim arrays, you probably don't need my help writing a function.
The output from the above
0 0 Description
0 1 Value
1 0 Description2
1 1 Value2
You can write a helper function:
Function MultiSplit(s As String, Optional delim1 As String = ",", Optional delim2 As String = ";") As Variant
Dim V As Variant, W As Variant, A As Variant
Dim i As Long, j As Long, m As Long, n As Long
V = Split(s, delim2)
m = UBound(V)
n = UBound(Split(V(0), delim1))
ReDim A(0 To m, 0 To n)
For i = 0 To m
For j = 0 To n
W = Split(V(i), delim1)
A(i, j) = Trim(W(j))
Next j
Next i
MultiSplit = A
End Function
Used like this:
Sub test()
Dim A As Variant
A = MultiSplit("Desciption, Value; Description2, Value2")
Range("A1:B2").Value = A
End Sub

Using Worksheet Functions In A Macro

I have an array of integers in VBA from which I would like to get the upper and lower quartiles.
I would like to use this method to get them: https://msdn.microsoft.com/en-us/library/office/ff836118.aspx
The documentation suggests you can use an array to do this, but when I try to run my code (below) I get an error saying Unable to get the Quartile property of the WorksheetFunction class
Please assist.
Dim totalsalesthatday() As String
Dim doINeedTo As Boolean
Dim totalsalesthatdayAverage As Integer
Dim randomnumberthingy As Integer
Dim quartile1 As Integer
Dim quartile3 As Integer
Dim iqr As Integer
Dim upper As Integer
Dim lower As Integer
quantity = 0
For Each queryaddress In worksheetname.Range("A2:A21")
query = queryaddress.Value
offsetnum = 0
If offsetnum = 0 Then
doINeedTo = True
End If
For Each daysoftheweek In Sheets
quantity = 0
If InStr(1, daysoftheweek.Name, worksheetnamename, vbTextCompare) > 0 And daysoftheweek.ListObjects.Count > 0 Then
Set itemaddress = daysoftheweek.Columns(5).Find(query, , xlValues, xlWhole)
If Not itemaddress Is Nothing Then
firstAddress = itemaddress.Address
Do
Set itemrow = itemaddress.EntireRow
quantity = quantity + itemrow.Columns(6).Value
Set itemaddress = daysoftheweek.Columns(5).FindNext(itemaddress)
Loop While Not itemaddress Is Nothing And itemaddress.Address <> firstAddress
End If
offsetnum = offsetnum + 1
ReDim Preserve totalsalesthatday(offsetnum)
totalsalesthatday(offsetnum) = daysoftheweek.ListObjects.Item(1).ListRows.Count
queryaddress.Offset(0, offsetnum).Value = quantity
worksheetname.Range("A1").Offset(0, offsetnum).Value = daysoftheweek.Name
End If
Next
If doINeedTo Then
quartile1 = WorksheetFunction.Quartile(totalsalesthatday, 1)
quartile3 = WorksheetFunction.Quartile_Inc(totalsalesthatday, 3)
iqr = quartile3 - quartile1
upper = quartile3 + (iqr * 1.5)
lower = quartile1 - (iqr * 1.5)
The error in question is at this line: quartile1 = WorksheetFunction.Quartile(totalsalesthatday, 1)
The .Quartile function parameters are an array and a double. Try changing your data types.