Finding average of selection and then assigning it to a cell - vba

I am attempting to create some dynamic code that, at this point, will select a bunch of cells, move the selection over two columns, then find the average of that selection and send that value to a cell. This is what I have so far, I am getting stuck at averaging the selection I've made:
Sub StatMakersub(Rng1 As Range)
Dim MyRange As Range
Dim Cell As Object
Dim InQuestion As Range
Dim SelAvg As Object
'Check every cell in the range for matching criteria.
For Each Cell In Rng1
If Cell.Value = 2000 Then
If MyRange Is Nothing Then
Set MyRange = Range(Cell.Address)
Else
Set MyRange = Union(MyRange, Range(Cell.Address))
End If
End If
Next
'Select the new range of only matching criteria
MyRange.Select
Selection.Offset(0, 2).Select
Set InQuestion = Selection
Range("P2").Formula = "=Average(Selection)"
Range("Q2").Formula = "=STDDEVA(Selection)"
End Sub
I can't find much on the web about how to average range variables.

You can calculate the average of a selection in this way:
Application.WorksheetFunction.Average("Here you put your range")
The result is a value and not an object, so you should use a variable. Taking names from your case you should use it like this:
SelAvgResult = Application.WorksheetFunction.Average(InQuestion)
I put another name for the variable, but you may still use SelAvg if you like. Just remind to define it as a variable (you may choose your desired format depending on the data size) instead of object if you do not need it anymore.
You may use then this variable for setting the value of your desired cell.
I have a last note: your code seems to replicate the already existing formula AVERAGEIF. If your criteria column is for instance column A and value you should use for calculating the average are in column C, You could directly set the value of the cell where you want the average like this:
=AVERAGEIF(A:A, "2000", C:C)
In this case you would avoid VBA.

Have you tried using the Sum worksheet function for calculating the sum of the range?
Xsum = WorksheetFunction.Sum(arrayX)
and dividing the Xsum value with the length of the array?

One thing I should metion is that you do not need to select the range to work with it. You can use it directly and doing so will also improve how fast your code runs.
To insert your worksheet functions, use the Range.Address function to generate a cell reference to put into the formulas.
https://msdn.microsoft.com/en-us/library/office/ff837625.aspx

Related

Excel VBA: How do I assign a range to a variable?

The current code is as follows:
With Sheets(sheetChk)
a = .Range(chkCell, .Range(Left(chkCell, 1) & Rows.Count).End(xlUp)).Value
End With
sheetChk is defined as the desired sheet index.
chkCell is defined as the desired "first cell" in the range.
I am attempting to assign a range to a. The problem is that the above code grabs the last cell in the column, entirely. Rather, I want to end at a specific row as I have other data in the same column after the desired range that needs to be ignored. How can I accomplish this?
First to assign a range, you need to have a Set first.
Example: Set a = Range("A1")
Another problem I see is that you're putting a .value at the end. That doesn't make sense for a range.
If you want to specify a specific row, then you need to include that in the code instead of using end(xlUp).
Example if it's row 50:
With Sheets(sheetChk)
Set a = .Range(chkCell, .Range(Left(chkCell, 1) & 50).End(xlUp))
End With
What you're currently using is finding the bottom row being used, which doesn't sound like you want. If you want to go the other direction (i.e. from the starting cell down until there's an empty cell), you can use this code:
With Sheets(sheetChk)
Set a = .Range(chkCell, .Range(Left(chkCell, 1) & chkCell.Row).End(xlDown))
End With
Based on your code, it looks like you might be putting an address in whatever cell chkCell is. If you have the row in that cell, and assuming you never exceed column z, then you could use this code to find the row:
With Sheets(sheetChk)
Set a = .Range(chkCell, .Range(Left(chkCell, 1) & Right(chkCell,Len(chkCell)-1))
End With
If that doesn't work, you need to figure out some method determine what row to use. Hope that helps.
dim rng as range
set rng = thisworkbook.sheets("sheet1").range("a1:a666")

Trying to create a range from a set of values

So I have a range of cells that contains a list of Cell Addresses.
Column B & C show where a block of information starts and ends. Column D states whether it is the start of a combination of tables, the end of the combination, or whether it's a single Table.
So basically I am having some difficulty combining the answers from B & C to form a combined range. So in the picture, Column E shows the start as A170 and the End as A596. (I don't think this is necessary tbh) I need to make a range containing A170:A543, A548:A554, etc. and it needs to be dynamic. So these should create themselves based on the values in Column D. I'm looking to do this in VBA, but if it's easier to do in Excel Formulas, that's okay too.
Can anyone give me some hints how to accomplish this? My brain is currently mush.
You can create a small User Defined Function (aka UDF) that will stitch together the non-contiguous cell ranges from textual representations of their respective addresses. This can return a range for a native worksheet function that uses a cell range like the SUM function or COUNTA function (to use two very simple examples).
Function makeNoncontiguousRange(startRNGs As Range)
Dim rng As Range, rUNION As Range
For Each rng In startRNGs
If rUNION Is Nothing Then
Set rUNION = Range(rng.Value2, rng.Offset(0, 1).Value2)
Else
Set rUNION = Union(rUNION, Range(rng.Value2, rng.Offset(0, 1).Value2))
End If
Next rng
'Debug.Print rUNION.Address
Set makeNoncontiguousRange = rUNION
End Function
The function could be used on a worksheet like,
=SUM(makeNoncontiguousRange(B2:B4))
In your data sample this would be like writing,
=SUM($A$170:$A$543,$A$548:$A$554,$A$558:$A$566)
Note that I am only passing in the start of the range in column B and gaining the end range with .Offset. If you need to expand the functionality to pass in the end range then you will need to check if both the start and end ranges are the same size.

Excel VBA get a range value from visible cells after applying autofilter

Is it not possible to do something simple like this to get the range value of B2?
crdata.Range("B2").SpecialCells(xlCellTypeVisible).Value
I have applied the autofilter to filter out with the given criteria and trying to return the range of B2 as a function.
Set the .SpecialCells(xlCellTypeVisible) to a Range, then use Cells(row, column) on this range to pick out the value that you require. If you are using headers in the result then you may also have to use Offset(1,0) to address your data. So where 'MySheet' has been defined as a Worksheet object, smething like:
Set rsltRng = MySheet.Autofilter.Range.SpecialCells(xlCellTypeVisible)
msgbox rsltRng.cells(2,2)

Is there a VBA method equivalent to the SQL SELECT query?

With an Excel Worksheet, I'm trying to search rows that matches some criteria in their cells value.
I need something like the SQL SELECT query to proceed search through Excel Worksheet, is there such method in VBA? A method that returns a Range of rows that matches the search criteria.
I have tried with the Range AutoFilter method but I can not use the returned object as a Range to access cells in the returned rows.
I'm really new to Excel VBA so sorry if my question looks strange or stupid
You can use the Union() function to create a range. Here's an simplified example (say you wanted to select the entire rows in which the value in the A column was an even number and highlight them yellow):
Sub UnionTest()
Dim myRange As Range
Dim cell As Range
For Each cell In Range("A1:A100")
If cell Mod 2 = 0 Then
If myRange Is Nothing Then
Set myRange = cell
Else
Set myRange = Union(myRange, cell)
End If
End If
Next
myRange.EntireRow.Interior.Color = vbYellow
End Sub
Please note that you need to check if the range is empty or not before you use union on it (and color it but to make the code easier to read I have omitted it here), thus the If-Then statement. Depending on what you want to do to each row, it might be more effecient to just do the process on the row during the for-each loop.
Hava a look at these funcitons
VLOOKUP
HLOOKUP - same as VLookup but for rows
LOOKUP()
INDEX() and MATCH()
OFFSET() and MATCH()
How to find data in an Excel table
How to Use VLOOKUP or HLOOKUP to find an exact match
Hope these help

Copy/Paste cells next to cells that have certain string

I am trying to look up cells in a certain column that have a string (e.g. Names), copy the corresponding cells in the column to its right (i.e. offset(0,1) ), then paste it to a column in a different sheet. I have the following code to find the range variable that I want. However, I can't select it from a different sheet!
When I use Sheets(1).MyRange.Copy, it doesn't accept it. Am I referring to the range in a wrong way? What am I doing wrong?
Here's code that I use to get MyRange:
Option Explicit
Sub SelectByValue(Rng1 As Range, Value As Double)
Dim MyRange As Range
Dim Cell As Object
'Check every cell in the range for matching criteria.
For Each Cell In Rng1
If Cell.Value = Value Then
If MyRange Is Nothing Then
Set MyRange = Range(Cell.Address)
Else
Set MyRange = Union(MyRange, Range(Cell.Address))
End If
End If
Next
End Sub
Sub CallSelectByValue()
'Call the macro and pass all the required variables to it.
'In the line below, change the Range, Minimum Value, and Maximum Value as needed
Call SelectByValue(Sheets(1).Range("A1:A20"), "Tom")
End Sub
One More Question: Rather than specifying the exact range to look at (e.g. "A1:A20"), I would LOVE to look at all of column A. But I don't want to use ("A:A") so it wouldn't look at all rows of A. Isn't there a method to look only in cells that have entries in column A?
Thank you VERY much.
Al
You only need MyRange.Copy.
To restrict only to cells in column A which might have values, you could use
With Sheet1
Set rngToSearch = Application.Intersect(.Columns(1), .UsedRange)
End With
...or maybe look at .SpecialsCells()