I have created the following functions to calculate Median, Percentiles and Quartiles (they work properly, I work for the American Statistical Association!).
Sorry for the post being so long, but I wanted to be sure you had all you would need. Others might find them useful as well.
I use "TextWrap" all the time. It will take a fieldname, and SQL string, and loop through the records to create a CSV list of the values in the given fieldname.
Let me know if you have any questions.
Basically, you'll need the following functions:
CountCSVWords, GetCSVWord, TextWrap, Median, Quartile and Percentile.
Here they are:
===========================================================
Public Function Median(varValues As String) As Variant
Dim varTemp1 As Variant
Dim varTemp2 As Variant
Dim varCount As Variant
varCount = CountCSVWords(varValues)
If varCount > 0 Then
If varCount Mod 2 = 0 Then
' Even number of values, need to average middle two
varTemp1 = GetCSVWord(varValues, varCount / 2)
varTemp2 = GetCSVWord(varValues, (varCount / 2) + 1)
Median = Int((Val(varTemp1) + Val(varTemp2)) / 2)
Else
' Odd number of values, take middle value
Median = Int(GetCSVWord(varValues, (varCount + 1) / 2))
End If
End If
End Function
===========================================================
Public Function Quartile(varValues As String, varIndex As Integer) As Variant
' figure this one out!
' Given n = number of values
' 1st => z = (n+1)/4
' 2nd => z = (n+1)/2
' 3rd => z = 3*((n+1)/4)
' 1st => z = (n+1)/20
' 19th => z = 19*(n+1)/20
' If n is odd, take the zth number in the set
' If n is even, take the zth number, then take the remainder of z * the difference of the zth number and the next number
'MsgBox varIndex, vbOKCancel
If varIndex > 0 Or varIndex < 4 Then
Dim n As Integer
Dim z As Variant
Dim varTemp1 As Variant
Dim varTemp2 As Variant
Dim varDiff As Variant
n = CountCSVWords(varValues)
If n = 1 Then
Quartile = Int(GetCSVWord(varValues, 1))
Else
Select Case varIndex
Case 1
' 1st => z = (n+1)/4 given n = number of values
z = (n + 1) / 4
Case 2
' 2nd => z = (n+1)/2 given n = number of values
z = (n + 1) / 2
Case 3
' 3rd => z = 3*((n+1)/4) given n = number of values
z = 3 * ((n + 1) / 4)
End Select
If n Mod 2 = 0 Then 'Even Number of values
varTemp1 = GetCSVWord(varValues, Int(z))
varTemp2 = GetCSVWord(varValues, Int(z) + 1)
varDiff = varTemp2 - varTemp1
Quartile = Int(varTemp1 + (varDiff * (z - Int(z))))
Else ' Odd number of values
Quartile = Int(GetCSVWord(varValues, Int(z)))
End If
End If
Else
Quartile = "N/A"
End If
End Function
===========================================================
Public Function Percentile(varValues As String, varIndex As Integer) As Variant
' Given n = number of values
' 1st => z = (n+1)/20
' 19th => z = 19*(n+1)/20
' If n is odd, take the zth number in the set
' If n is even, take the zth number, then take the remainder of z * the difference of the zth number and the next number
'MsgBox varIndex, vbOKCancel
If varIndex >= 1 And varIndex <= 20 Then
Dim n As Integer
Dim z As Variant
Dim varTemp1 As Variant
Dim varTemp2 As Variant
Dim varDiff As Variant
'How many values are there?
n = CountCSVWords(varValues)
'MsgBox n
If n = 1 Then
Percentile = Int(GetCSVWord(varValues, 1))
Else
z = varIndex * (n + 1) / 20
'MsgBox z
If n Mod 2 = 0 And n <> Int(z) Then 'Even Number of values
varTemp1 = GetCSVWord(varValues, Int(z))
varTemp2 = GetCSVWord(varValues, Int(z) + 1)
varDiff = varTemp2 - varTemp1
Percentile = Int(varTemp1 + (varDiff * (z - Int(z))))
Else ' Odd number of values
Percentile = Int(GetCSVWord(varValues, Int(z)))
End If
End If
Else
Percentile = "N/A"
End If
End Function
===========================================================
Public Function textwrap(FieldName As String, SQLALL As String) As String
On Error GoTo Error_TextWrap
' Creates a comma separated list of values from the given fieldname for the recordset in SQLALL
Dim TextHolder As String
Dim MyTable As Recordset
Dim mydb As DATABASE
TextHolder = ""
Set mydb = CurrentDb
Set MyTable = mydb.OpenRecordset(SQLALL, DB_OPEN_DYNASET)
If Not MyTable.EOF Then
MyTable.MoveFirst
Do Until MyTable.EOF
TextHolder = TextHolder & MyTable(FieldName) & ", "
MyTable.MoveNext
Loop
End If
If TextHolder = "" Then
textwrap = ""
Else
textwrap = Left$(TextHolder, Len(TextHolder) - 2)
End If
Exit Function
Error_TextWrap:
MsgBox Err.Description, , Err.Number
Exit Function
End Function
===========================================================
Function CountCSVWords(S) As Integer
'
' Counts words in a string separated by commas
'
Dim WC As Integer, Pos As Integer
If VarType(S) <> 8 Or Len(S) = 0 Then
CountCSVWords = 0
Exit Function
End If
WC = 1
Pos = InStr(S, ","

Do While Pos > 0
WC = WC + 1
Pos = InStr(Pos + 1, S, ","

Loop
CountCSVWords = WC
End Function
===========================================================
Function GetCSVWord(S, Indx As Integer)
Dim WC As Integer, Count As Integer, SPos As Integer, EPos As Integer
WC = CountCSVWords(S)
If Indx < 1 Or Indx > WC Then
GetCSVWord = Null
Exit Function
End If
Count = 1
SPos = 1
For Count = 2 To Indx
SPos = InStr(SPos, S, ","

+ 1
Next Count
EPos = InStr(SPos, S, ","

- 1
If EPos <= 0 Then EPos = Len(S)
GetCSVWord = Mid(S, SPos, EPos - SPos + 1)
End Function
===========================================================
Tim Gill
Gill Consulting