如何从VBA中的范围对象中选择子范围?

时间:2015-10-23 20:57:13

标签: vba excel-vba excel

我正在尝试编写一个VBA函数,它可以给出数据的平均值和stdev(假设共有5个fractiles)。数据可以是行向量或列向量。到目前为止,我已写下以下内容:

    Function AverageFractile(ret As Range, fractile As Integer) As Variant

    Dim lenRange As Integer
    Dim startIndex As Integer
    Dim endIndex As Integer
    Dim subRange As Variant

    'Arrange the range object in ascending Order, doesn't work
    ' ret = Sort(ret, XlSortOrder = xlAscending)

    'Getting the indices that would be used to slice the input range to get the relevant fractile
    lenRange = Application.WorksheetFunction.Max(ret.Rows.Count, ret.Columns.Count)
    startIndex = (lenRange * (fractile - 1)) \ 5 + 1
    endIndex = (lenRange * fractile) \ 5

'    subRange = ret(Cells(startIndex,1),Cells(endIndex,1))
'    This is not working 


    End Function

到目前为止我被困在两个地方: - a)我正在尝试对数据进行排序,但是函数Sort不起作用,我如何按升序对作为输入的范围对象进行排序?

b)如何从范围对象中选择一个子范围,以便我可以计算其平均值和Stdev?

感谢您的帮助,我现在已经尝试了几个小时,并且没有能够解决这个问题。

3 个答案:

答案 0 :(得分:4)

正如我在评论中所说,你的问题中存在“太多问题”。但是,这是我对你的“标题”问题的回答:

Function subRange(r As Range, startPos As Integer, endPos As Integer) as Range
    Set subRange = r.Parent.Range(r.Cells(startPos), r.Cells(endPos))
End Function

答案 1 :(得分:0)

对于较大范围的子范围,需要引用(对我来说至少)并不罕见。 Range构造函数(属性?在此上下文中看起来像构造函数)似乎只接受" A4:G7"的字符串参数。品种 - 甚至不是" R4C1:R7C7"样式,从行和列索引中更容易动态编码。

我认为VBA不允许超载,但令人恼火的是,MS认为不适合为这种事情提供有用的功能。 AFAIK(在VBA中对我来说并不是那么遥远)没有类似于范围的构造函数,它可以获取行和列索引或单元格对。 .Rows和.Columns属性不会做我期望他们做的事情。除了.Rows.Count和.Columns.Count之外,我不确定它们有什么好处(我的经验不足吗?那没关系 - 帮助我。)

我厌倦了编写简单但繁琐的代码来将行和列索引转换为" A4:G7"样式字符串内联,所以我写了一个简单的函数来处理它。

在通话功能中:

dim row1 as long, col1 as long, row2 as long, col2 as long
dim subRng as Range
...
[Insert absolutely brilliant code here to compute row1, etc.,
 or in my case, hack away until it works]

set subRng = getSubRange(row1, col1, row2, col2, bigRange)
...
[do great things with subRng]

这里是getSubRange实用程序函数:

Function getSubRange(iRow1 As Long, iCol1 As Long, _
                     iRow2 As Long, iCol2 As Long, _
                     sourceRange As Range) As Range
' Returns a sub-range of the source range (non-boolean version of makeSubRange().
' Inputs:
'   iRow1       -  Row and colunn indices in the sourceRange of
'   iCol1          the upper left and lower right corners of
'   iRow2          the requested subrange.
'   iCol2
'   sourceRange - The range from which a sub-range is requested.
'
' Return: Reference to a sub-range of sourceRange bounded by the input row and
'         and column indices.
' Notes: A null range will be returned if the following is not true.
'        1 <= iRow1 <= SourceRange.Rows.count
'        1 <= iRow2 <= SourceRange.Rows.count
'        1 <= iCol1 <= SourceRange.Columns.count
'        1 <= iCol2 <= SourceRange.Columns.count

   Const AM1 = 64 'Ascii value of 'A' = 65; Asc('A') - 1 = 64
   Dim rangeStr As String

   If (1 <= iRow1) And (iRow1 <= sourceRange.Rows.Count) And _
      (1 <= iRow2) And (iRow2 <= sourceRange.Rows.Count) And _
      (1 <= iCol1) And (iCol1 <= sourceRange.Columns.Count) And _
      (1 <= iCol2) And (iCol2 <= sourceRange.Columns.Count) Then
      rangeStr = Chr(AM1 + iCol1) & CStr(iRow1) & ":" _
               & Chr(AM1 + iCol2) & CStr(iRow2)
      Set getSubRange = sourceRange.Range(rangeStr)
   Else
      Set getSubRange = Nothing
   End If

End Function 'getSubRange()

不要忘记子范围是对部分源范围的引用,而不是部分源范围的副本:您对子范围所做的任何更改都是对源范围的更改。但我觉得这很明显。

答案 2 :(得分:0)

那是 5 年后,但我在寻找类似的解决方案时偶然发现了这篇文章。 RiderBill 的解决方案是我正在寻找的解决方案,但它有一个巨大的缺点:它不支持“Z”之后的列。

这是一个更新代码的提议,可以支持超出该限制的列:

' This code is modified to support more than the first 26 columns
Public Function colToName(ByVal iCol As Integer) As String
    ' Construct col char by char, and finally inverse the string
    Dim colNameInverted As String
    Const AM1 = 64 ' ASCII for 'A'
    colNameInverted = Chr(AM1 + iCol Mod 26)
    While iCol > 26
        iCol = iCol / 26
        colNameInverted = colNameInverted & Chr(AM1 + iCol Mod 26)
    Wend
        
    Dim length As Integer
    length = Len(colNameInverted)
    
    For i = 0 To length - 1
        colToName = colToName & Mid(colNameInverted, (length - i), 1)
    Next i
End Function

Public Function getSubRange(iRow1 As Integer, iCol1 As Integer, iRow2 As Integer, iCol2 As Integer, sourceRange As Range) As Range
    ' Returns a sub-range of the source range (non-boolean version of makeSubRange().
    ' Inputs:
    '   iRow1       -  Row and colunn indices in the sourceRange of
    '   iCol1          the upper left and lower right corners of
    '   iRow2          the requested subrange.
    '   iCol2
    '   sourceRange - The range from which a sub-range is requested.
    '
    ' Return: Reference to a sub-range of sourceRange bounded by the input row and
    '         and column indices.
    ' Notes: A null range will be returned if the following is not true.
    '        1 <= iRow1 <= SourceRange.Rows.count
    '        1 <= iRow2 <= SourceRange.Rows.count
    '        1 <= iCol1 <= SourceRange.Columns.count
    '        1 <= iCol2 <= SourceRange.Columns.count
    Dim rangeStr As String
    
    If (1 <= iRow1) And (iRow1 <= sourceRange.Rows.Count) And _
       (1 <= iRow2) And (iRow2 <= sourceRange.Rows.Count) And _
       (1 <= iCol1) And (iCol1 <= sourceRange.Columns.Count) And _
       (1 <= iCol2) And (iCol2 <= sourceRange.Columns.Count) Then
       col1Name = colToName(iCol1)
       col2Name = colToName(iCol2)
       rangeStr = col1Name & CStr(iRow1) & ":" _
                & col2Name & CStr(iRow2)
        Set getSubRange = sourceRange.Range(rangeStr)
    Else
        Set getSubRange = Nothing
    End If
End Function

请注意,我将 Long 更改为 Integer 以适合我自己的代码,但当然 Integer 也能很好地工作。唯一的区别是添加了 colToName 函数,该函数将为任何列生成正确的名称,甚至超出 'Z'。