我终于设法弄清楚如何对数据进行排序,以便该函数可以处理连续和不连续范围,这非常感谢来自 SJR 和 Ralph 以及来自 answer on this question 的问题的 cmets。
允许不连续范围的方法是使用 ParamArray,然后检查输入的所有参数并检查它们包含的内容(这是我最初失败的地方,因为我不知道如何让 Excel 检查我输入的每个参数的内容到函数)。棘手的部分是,如果它当前检查的参数仅包含一个单元格,那么与包含连续范围的情况相比,它需要处理的方式是不同的。
例如,如果对仅包含一个单元格的参数使用 UBound,则检查 ParamArray 中的所有参数将失败。此外,为了正确寻址参数中连续范围内的每个单元格,需要循环通过 InputParameters(i).Cells(j),而如果参数只是单个单元格,则对其进行寻址就足够了作为 InputParameters(i)。
我现在生成的代码可以按我的意愿工作;我可以选择任何范围的单元格并计算标准偏差和平均值,同时排除 NaN 值。我将它与内置的 STDEV.S、STDEV.P 和 AVERAGE 进行了比较,它产生了完全相同的结果*。我不知道为什么内置函数默认不排除 NaN 值,但我为任何想要使用它的人提供了以下函数的代码。
不包括 NaN 值的 STDEV.S 代码
Function NaNStdev_S(ParamArray xRange() As Variant) As Double
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'A function to calculate the sample standard deviation of any ranges of cells
'while excluding text, logicals, empty cells and cells containing #N/A.
'Can handle both continuous and discontinuous ranges.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim CellsUsed As Integer
Dim NumArg As Integer
Dim NumCell As Integer
Dim xAvg As Double
Dim xSum As Double
Dim xTemp As Variant
Dim xVect() As String
NumArg = UBound(xRange) 'Counts the number of input arguments (i.e., number of discontinuous regions)
For i = 0 To NumArg 'Goes through each discontinuous region
xTemp = xRange(i) 'Stores the current region in a temporary variable as several of the later operations cannot be performed on the full input array
If IsArray(xTemp) Then 'Checks if the current region is an array; if yes, then that array will be continuous
NumCell = UBound(xTemp, 1) * UBound(xTemp, 2) 'Checks how many cells are in the array
For j = 1 To NumCell 'Goes through all cells in the current region
If IsEmpty(xRange(i).Cells(j)) Then 'do nothing
ElseIf Application.IsLogical(xRange(i).Cells(j)) Then 'do nothing
ElseIf IsNumeric(xRange(i).Cells(j)) Then 'If the content of the cell is numeric, then use it
xSum = xSum + xRange(i).Cells(j) 'Add the current cell value to the sum of all cell values
CellsUsed = CellsUsed + 1 'Counts how many of the cell values that are actually used
ReDim Preserve xVect(CellsUsed) 'Adjusts the size of xVect
xVect(CellsUsed) = xRange(i).Cells(j) 'Reformats all usable values into one single vector for later use
Else
End If
Next j
Else 'If the current region is not an array, then it's just a single value
If IsEmpty(xRange(i)) Then 'do nothing
ElseIf IsNumeric(xRange(i)) Then 'If the content of the current region is numeric, then use it
xSum = xSum + xRange(i) 'Add the current cell (region) value to the sum of all cell values
CellsUsed = CellsUsed + 1 'Increase the counter of used values
ReDim Preserve xVect(CellsUsed) 'Adjusts the size of xVect
xVect(CellsUsed) = xRange(i) 'Adds the current value into the reformatted vector for later use
Else
End If
End If
Next i
xAvg = xSum / CellsUsed 'Average of all cells which contains numbers
xSum = 0 'resets the sum as it's no longer needed
For i = 1 To CellsUsed 'Goes through the reformatted vector and calculates the sum of (x - x_avg) ^ 2
xSum = xSum + (xVect(i) - xAvg) ^ 2 'This is the dividend of the variance equation
Next i
NaNStdev_S = (xSum / (CellsUsed - 1)) ^ 0.5 'the sample standard deviation is the square root of the corrected variance
End Function
不包括 NaN 值的 STDEV.P 代码
Function NaNStdev_P(ParamArray xRange() As Variant) As Double
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'A function to calculate the population standard deviation of any ranges of cells
'while excluding text, logicals, empty cells and cells containing #N/A.
'Can handle both continuous and discontinuous ranges.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim CellsUsed As Integer
Dim NumArg As Integer
Dim NumCell As Integer
Dim xAvg As Double
Dim xSum As Double
Dim xTemp As Variant
Dim xVect() As String
NumArg = UBound(xRange) 'Counts the number of input arguments (i.e., number of discontinuous regions)
For i = 0 To NumArg 'Goes through each discontinuous region
xTemp = xRange(i) 'Stores the current region in a temporary variable as several of the later operations cannot be performed on the full input array
If IsArray(xTemp) Then 'Checks if the current region is an array; if yes, then that array will be continuous
NumCell = UBound(xTemp, 1) * UBound(xTemp, 2) 'Checks how many cells are in the array
For j = 1 To NumCell 'Goes through all cells in the current region
If IsEmpty(xRange(i).Cells(j)) Then 'do nothing
ElseIf Application.IsLogical(xRange(i).Cells(j)) Then 'do nothing
ElseIf IsNumeric(xRange(i).Cells(j)) Then 'If the content of the cell is numeric, then use it
xSum = xSum + xRange(i).Cells(j) 'Add the current cell value to the sum of all cell values
CellsUsed = CellsUsed + 1 'Counts how many of the cell values that are actually used
ReDim Preserve xVect(CellsUsed) 'Adjusts the size of xVect
xVect(CellsUsed) = xRange(i).Cells(j) 'Reformats all usable values into one single vector for later use
Else
End If
Next j
Else 'If the current region is not an array, then it's just a single value
If IsEmpty(xRange(i)) Then 'do nothing
ElseIf IsNumeric(xRange(i)) Then 'If the content of the current region is numeric, then use it
xSum = xSum + xRange(i) 'Add the current cell (region) value to the sum of all cell values
CellsUsed = CellsUsed + 1 'Increase the counter of used values
ReDim Preserve xVect(CellsUsed) 'Adjusts the size of xVect
xVect(CellsUsed) = xRange(i) 'Adds the current value into the reformatted vector for later use
Else
End If
End If
Next i
xAvg = xSum / CellsUsed 'Average of all cells which contains numbers
xSum = 0 'resets the sum as it's no longer needed
For i = 1 To CellsUsed 'Goes through the reformatted vector and calculates the sum of (x - x_avg) ^ 2
xSum = xSum + (xVect(i) - xAvg) ^ 2 'This is the dividend of the variance equation
Next i
NaNStdev_P = (xSum / CellsUsed) ^ 0.5 'the population standard deviation is the square root of the variance
End Function
不包括 NaN 值的 AVERAGE 代码
Function NaNAverage(ParamArray xRange() As Variant) As Double
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'A function to calculate the average of any ranges of cells
'while excluding text, logicals, empty cells and cells containing #N/A.
'Can handle both continuous and discontinuous ranges.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim CellsUsed As Integer
Dim NumArg As Integer
Dim NumCell As Integer
Dim xSum As Double
Dim xTemp As Variant
NumArg = UBound(xRange) 'Counts the number of input arguments (i.e., number of discontinuous regions)
For i = 0 To NumArg 'Goes through each discontinuous region
xTemp = xRange(i) 'Stores the current region in a temporary variable as several of the later operations cannot be performed on the full input array
If IsArray(xTemp) Then 'Checks if the current region is an array; if yes, then that array will be continuous
NumCell = UBound(xTemp, 1) * UBound(xTemp, 2) 'Checks how many cells are in the array
For j = 1 To NumCell 'Goes through all cells in the current region
If IsEmpty(xRange(i).Cells(j)) Then 'do nothing
ElseIf Application.IsLogical(xRange(i).Cells(j)) Then 'do nothing
ElseIf IsNumeric(xRange(i).Cells(j)) Then 'If the content of the cell is numeric, then use it
xSum = xSum + xRange(i).Cells(j) 'Add the current cell value to the sum of all cell values
CellsUsed = CellsUsed + 1 'Counts how many of the cell values that are actually used
Else
End If
Next j
Else 'If the current region is not an array, then it's just a single value
If IsEmpty(xRange(i)) Then 'do nothing
ElseIf IsNumeric(xRange(i)) Then 'If the content of the current region is numeric, then use it
xSum = xSum + xRange(i) 'Add the current cell (region) value to the sum of all cell values
CellsUsed = CellsUsed + 1 'Increase the counter of used values
Else
End If
End If
Next i
NaNAverage = xSum / CellsUsed 'Average of all cells which contains numbers
End Function
*免责声明
我提到代码产生的值与内置函数完全相同 - 但是,我确实注意到有一次它没有。
我将以下随机选择的值作为随机大小和定位的范围放置在我的 Excel 工作表中:
(00:01:00, -10, -33, 10, 33, 20, 66, 30, 40, 300, TRUE, {empty cell} , #N/A)
如果它们是随机分布的(即,我将它们放在以下单元格中 (P22:Q23;R22:R23;S22:T22;S21:V21;Q28)),那么它们与 STDEV.S 产生的值不同(我已经从 STDEV.S 函数中手动排除了带有 #N/A 的单元格),但它们仅在小数点后 13 位不同(我的函数给出 93.5950714912684,而 STDEV.S 给出 93.5950714912683),这应该是一个足够小的错误无关紧要。有趣的是,如果我将所有值放在一行中(即,我将所有值放在例如 (M34:Y34) 上),那么我的函数和内置函数都会给出完全相同的结果(即 93.5950714912683) .错误似乎源于包含 1 分钟的单元格;如果我将 00:01:00 更改为任何其他时间值(例如 00:01:01 或 01:01:00),那么无论这些值是放在一行上还是随机分布,这两个函数都会产生完全相同的结果工作表上的区域。
我无法解释这种奇怪的行为,但到目前为止它似乎只产生了一个微不足道的错误,所以我会假设我的代码按预期工作。