【问题标题】:How do I select subrange from a range object in VBA?如何从 VBA 中的范围对象中选择子范围?
【发布时间】:2015-10-23 20:57:12
【问题描述】:

我正在尝试编写一个 VBA 函数,该函数将为我提供一个分位数数据的平均值和标准差(假设总共 5 个分位数)。数据可以是行向量或列向量。到目前为止,我已经写下了以下内容:

    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?

感谢您的帮助,我已经尝试了几个小时,但无法解决。

【问题讨论】:

    标签: vba excel


    【解决方案1】:

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

    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
    

    【讨论】:

      【解决方案2】:

      需要对较大范围的子范围的引用并不少见(至少对我而言)。 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()
      

      不要忘记子范围是对源范围的一部分的引用,而不是其中一部分的副本:您对子范围所做的任何更改都是对源范围的更改。但我想这已经足够明显了。

      【讨论】:

      • 还有很长的路要走。 Set getSubRange = Range(sourceRange.Cells(iRow1,iCol1), sourceRange.Cells(iRow2,iCol2)) 怎么样?
      【解决方案3】:

      那是 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'。

      【讨论】:

        猜你喜欢
        • 2014-12-17
        • 1970-01-01
        • 2019-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-29
        • 1970-01-01
        相关资源
        最近更新 更多