How to average multiple cells in same column? - excel

Is there a way to find the average for a block of data in the same column?
This is the code I was using before which wasn't working the way i had hoped for data with fewer numbers:
Sub Macro1()
'Macro1
Do Until ActiveCell.Value = ""
'Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Range("A1").Select
Selection.FormulaR1C1 = "=AVERAGE(R[-13]C:R[-1]C)"
Selection.End(xlDown).Select
Loop
End Sub
Example: the image is the file I'm working on. I've got thousands of rows of this data and it's all separated by three blank rows. Is there a way to find the averages of each block of data and display it in the first blank row directly underneath the data?

Try this: (for columnA considering 3 gaps after each block including last block)
Sub DoAvginColumn()
Dim i as long, q as long, avgBlock
Dim lastrow as Range, firstrow as Range, rngcolA as Range, rngBlock as Range
Set lastrow = Range("A:A").Find(What:="*", After:=[A1], SearchDirection:=xlPrevious)
Set firstrow = Range("A:A").Find(What:="*", After:=[A1], SearchDirection:=xlNext)
Set rngcolA = Range("A" & firstrow.Row & ":" & "A" & lastrow.Row)
q = 1
For i = 1 To rngcolA.Rows.Count + 1
If rngcolA(i).Value = "" And rngcolA(i + 1).Value = "" And rngcolA(i + 2).Value = "" Then
Set rngBlock = Range(rngcolA(q), rngcolA(i - 1))
avgBlock = WorksheetFunction.Average(rngBlock)
rngcolA(i).Value = avgBlock
rngcolA(i).Font.Bold = True
q = i + 3
End If
Next
End Sub
APPENDED: Steps I followed: From top to bottom of respective range:
search for 3 consecutive blank.
If found, then define an appropriate range for each block starting with 1st cell of that block and ending with last cell of that block.
Then applied avg function on that block and put result below the block.

Related

Excel VBA how can I find where two blank rows appear and delete one of those rows?

My worksheet contains blank rows which I want to keep.
However it also contains groups of two blank rows and I want to keep one of them but delete/remove the other one.
END RESULT: sheet contains only single blank rows.
First attachment shows before (highlighted where two blank rows) and second attachment shows desired final result (worksheet only contains single blank rows).
What is the VBA code to achieve this please?
Something like:
select all
identify where two blank rows are and delete one of those rows
Thanks in advance!
In an attempt to improve the question and show my efforts with my own VBA code.... this is what I had got starting with a variable counter of 0 and when it gets to 2 it would delete a row, it sort of works as in it finds and deletes the desired row but it appears to run an infinite loop :(
Sub EmptyRows()
Dim x As Integer
Dim row As Integer
NumRows = ActiveSheet.UsedRange.Rows.Count
' Select cell A2.
Range("A2").Select
row = 0
' Establish "For" loop to loop "numrows" number of times.
For x = 1 To NumRows
If Application.CountA(ActiveCell.EntireRow) = 0 Then
row = row + 1
End If
ActiveCell.Offset(1, 0).Select
If Application.CountA(ActiveCell.EntireRow) = 0 Then
row = row + 1
End If
If row >= 2 Then
MsgBox "2 Rows!"
ActiveCell.EntireRow.Delete
End If
' Selects cell down 1 row from active cell.
ActiveCell.Offset(1, 0).Select
row = 0
Next
End Sub
Try the next code, please. It will check if really whole analyzed rows are empty:
Sub deleteSecondBlankRow()
Dim sh As Worksheet, arr, rngDel As Range, lastR As Long, i As Long
Set sh = ActiveSheet
lastR = sh.Range("A" & sh.rows.Count).End(xlUp).row
arr = sh.Range("A2:A" & lastR).value
For i = 1 To UBound(arr)
If arr(i, 1) = "" Then
If WorksheetFunction.CountA(rows(i + 1)) = 0 Then
If arr(i + 1, 1) = "" Then
If WorksheetFunction.CountA(rows(i + 2)) = 0 Then
If rngDel Is Nothing Then
Set rngDel = sh.Range("A" & i + 2)
Else
Set rngDel = Union(rngDel, sh.Range("A" & i + 2))
End If
End If
End If
End If
End If
Next i
If Not rngDel Is Nothing Then rngDel.EntireRow.Select
End Sub
The code only selects the rows to be deleted. If you check it and what selected is convenient, you should only replace Select with Delete on the last code line...

performance issue - Rearranging columns based on column header

I have an Excel Workbook with hundreds of columns to be rearranged. Having tried different approaches to rearrange those columns I have developed my own solution, because it's faster than what I have found here and elsewhere:
How to rearrange the excel columns by the columns header name
https://code.adonline.id.au/rearrange-columns-excel-vba/
My code:
What I basically do is searching the header row for a certain string and copy that column to a temp/helper sheet, when done I search for the next term and so on until all categories are searched. Afterwards I copy the chunk back to the main sheet in the correct order.
edit: it is of vital importance to keep the formatting of each column, so putting everything in an array does not work, because the formatting information will be gone.
Sub cutColumnsToTempAndMoveBackSorted()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Call declareVariables
iCountCompanies = lngLastCol - iColStart + 1
' Timer
Dim StartTime As Double
Dim SecondsElapsed As Double
' Remember time when macro starts
StartTime = Timer
iStartColTemp = 0
wsTempCompanies.UsedRange.Delete
' First copy all columns with "ABC"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "ABC" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
' Then copy all columns with "DDD"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "DDD" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
' Then copy all columns with "CCC"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "CCC" Or ws.Cells(iRowCategory, i) = "" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
' Then copy all columns with "EEE"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "EEE" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
Dim iLastColTemp As Integer: iLastColTemp = iStartColTemp
iStartColTemp = 1
ws.Range(Col_Letter(iColStart) & ":" & Col_Letter(lngLastCol)).Delete 'Col_Letter function gives back the column ist characters instead of column ID
' Move back to Main Sheet
wsTempCompanies.Range(Col_Letter(iStartColTemp) & ":" & Col_Letter(iLastColTemp)).Copy
ws.Range(Col_Letter(iColStart + 1) & ":" & Col_Letter(lngLastCol + 1)).Insert
ws.Columns(iColStart).Delete
'Determine how many seconds code took to run
SecondsElapsed = Round(Timer - StartTime, 2)
'Notify user in seconds
Debug.Print "Time: " & SecondsElapsed & " Sekunden."
ende:
Application.ScreenUpdating = True
Call activateApplication ' All kinds of screenupdates, such as enableevents, calculations, ...
End Sub
I am still not happy with my solution as it takes just too much time when there are more than 50 columns. Sometimes I have over 300.
Any suggestion to boost the performance?
The below might be of some help, if it is not too much effort.
Sample Dataset in one sheet (let's call this the Main sheet) with,
(Row 2) Sample Header row (includes the lookup keywords - ABC, DDD, CCC, EEE)
(Row 1) A Temp Row (formulated to show Header Order numbers)
References sheet which lists the lookup keywords in required left-to-right sort order
Back in the Main sheet, we'd like to generate the sequence numbers in Row 1.
As highlighted in the 1st image, it can be done with the below MATCH formula in the cell A1,
=MATCH(TRUE,ISNUMBER(SEARCH(References!$A$2:$A$5,A2)),0)
This is required as an array formula and hence should be executed by hitting Ctrl+Shift+Enter
Now copy the cell A1 across columns (in Row 1) through the last column
Row 1 will now contain sequence numbers 1..n, where n is the numbers of rows found in the References sheet. It may also contain #N/A error value returned by the MATCH formula if no match is found from the 'References' sheet
Now, apply sort (Sort Option: Left to Right) and Sort By Row 1.
The columns should now be sorted as per requirement and with formatting intact.
Result (Sorted)
Please note that a column header not matching any keywords has been moved to the end.
Once you find everything in place, now you can go ahead and delete the (Row 1) temp row in the Main sheet
P.S: While I haven't computed the performance of this approach on a large dataset, I'm sure it will be fairly quick.
Please test the next code, please. Most of the credit must go to #Karthick Ganesan for his idea. The code only puts his idea in VBA:
Sub reorderColumnsByRanking()
Dim sh As Worksheet, arrOrd As Variant, lastCol As Long, i As Long
Dim El As Variant, boolFound As Boolean, isF As Long
Set sh = ActiveSheet 'use here your necessary sheet
lastCol = sh.Cells(1, Columns.Count).End(xlToLeft).Column
arrOrd = Split("ABC|1,DDD|2,CCC|3,EEE|4", ",") 'load criteria and their rank
'insert a helping row____________________
sh.Range("A1").EntireRow.Insert xlAbove
'________________________________________
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Rank the columns_______________________________________________________________
For i = 1 To lastCol
For Each El In arrOrd
If IsFound(sh.Cells(2, i), CStr(Split(El, "|")(0))) Then
sh.Cells(1, i).Value = Split(El, "|")(1): boolFound = True: Exit For
End If
Next
If Not boolFound Then sh.Cells(1, i).Value = 16000
boolFound = False
Next i
'_______________________________________________________________________________
'Sort LeftToRight_____________________________________________________________
sh.Sort.SortFields.Add2 key:=sh.Range(sh.Cells(1, 1), sh.Cells(1, lastCol)) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With sh.Sort
.SetRange sh.Range(sh.Cells(1, 1), sh.Cells(1, lastCol)).EntireColumn
.Header = xlYes
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
'____________________________________________________________________________
'Delete helping first row____
sh.Rows(1).Delete xlDown
'____________________________
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationManual
End Sub
Private Function IsFound(rng As Range, strS As String) As Boolean
Dim fC As Range
Set fC = rng.Find(strS)
If Not fC Is Nothing Then
IsFound = True
Else
IsFound = False
End If
End Function
Here's my take on the solution. It's pretty similar to the one in your first link by #BruceWayne except this will go straight to the correct column rather than checking each one.
At the moment the code looks for partial matches - so "ABCDEF" would be found for both "ABC" and "DEF". Change xlPart to xlWhole in the FIND command to have it match against exact headings.
Sub Test()
Dim CorrectOrder() As Variant
Dim OrderItem As Variant
Dim FoundItem As Range
Dim FirstAddress As String
Dim NewOrder As Collection
Dim LastColumn As Range
Dim NewPosition As Long
Dim tmpsht As Worksheet
CorrectOrder = Array("ABC", "DEF", "GHI", "JKL")
With ThisWorkbook.Worksheets("Sheet1")
Set LastColumn = .Cells(2, .Columns.Count).End(xlToLeft) 'Return a reference to last column on row 2.
Set NewOrder = New Collection
With .Range(.Cells(2, 1), LastColumn) 'Refer to the range A2:LastColumn.
'Search for each occurrence of each value and add the column number to a collection in the order found.
For Each OrderItem In CorrectOrder
Set FoundItem = .Find(What:=OrderItem, After:=.Cells(1, 1), LookIn:=xlValues, LookAt:=xlPart)
If Not FoundItem Is Nothing Then
FirstAddress = FoundItem.Address
Do
NewOrder.Add FoundItem.Column
Set FoundItem = .FindNext(FoundItem)
Loop While FoundItem.Address <> FirstAddress
End If
Next OrderItem
End With
End With
'Providing some columns have been found then move them in order to a temporary sheet.
If NewOrder.Count > 1 Then
NewPosition = 2
Set tmpsht = ThisWorkbook.Worksheets.Add
For Each OrderItem In NewOrder
ThisWorkbook.Worksheets("Sheet1").Columns(OrderItem).Cut _
tmpsht.Columns(NewPosition)
NewPosition = NewPosition + 1
Next OrderItem
'Copy the reordered columns back to the original sheet.
tmpsht.Columns(2).Resize(, NewOrder.Count).Cut _
ThisWorkbook.Worksheets("Sheet1").Columns(2)
'Delete the temp sheet.
Application.DisplayAlerts = False
tmpsht.Delete
Application.DisplayAlerts = True
End If
End Sub
You can use Cut which is significantly faster (on PC it is around 20-30 times faster than Copy/Insert approach. Cut also preserves formatting.
Here, is an example how it can be implemented into your code:
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "EEE" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Cut wsTempCompanies.Columns(iStartColTemp)
End If
Next i
If for some reason, you are not allowed to cut elements from ws, then it is probably good idea to create temporary copy of that working to work on.

How to delete the rows based in excel sheet using column values

I have excel with 5 different sheets.
sheet3 and sheet4 i want delete rows based on the single column cell value.
in sheet 3 i want to delete rows based on H column cell values if H2="#N/A" and H503="#N/A" then delete entire rows.
in sheet 4 i want to delete rows based on b column cell values if B2="320857876",B3="32085678",B4="12133435" the delete the entire rows where B column cell values starts with 302.
and i want to delete all Data from 'C' column
My excel sheet is like this
Using excel file
Sub Create()
Dim LastRow As Long
Dim i As Long
LastRow = Range("B10000").End(xlUp).Row
For i = LastRow To 1 Step -1
If Range("B" & i) = "#N/A" Then
Range("B" & i).EntireRow.Delete
End If
Next
End Sub
You've got a few requirements there and your code is fairly light but regarding the #N/A part of it, you can't just test for that text using the value approach, which is the default property returned for a range object.
Sub Create()
Dim LastRow As Long, i As Long
LastRow = Range("B10000").End(xlUp).Row
For i = LastRow To 1 Step -1
If Range("B" & i).Text = "#N/A" Then
Range("B" & i).EntireRow.Delete
End If
Next
End Sub
... you need to use .Text to get that to work, or, If IsError(Range("B" & i)) Then is another approach.
The rest of your requirements is just logic. The rest of your code is relatively sound so you just need to work through it.
I hope that helps.
Sub delete_rows()
Dim sheet As Worksheet, cell As Range
Count = 1
For Each sheet In ThisWorkbook.Worksheets
If Count = 3 Then
lastrow = sheet.Cells(sheet.Rows.Count, "H").End(xlUp).Row
Set Rng = sheet.Range("H1:H" & lastrow)
For i = Rng.Cells.Count To 1 Step -1
If Application.WorksheetFunction.IsNA(Rng(i).Value) Then
Rng(i).EntireRow.Delete
ElseIf Rng(i).Value = "#NA" Then
Rng(i).EntireRow.Delete
End If
Next
ElseIf Count = 4 Then
lastrow = sheet.Cells(sheet.Rows.Count, "B").End(xlUp).Row
Set Rng = sheet.Range("B1:B" & lastrow)
Debug.Print (Rng(4).Text)
If Rng(2).Value = "320857876" And Rng(3).Value = "32085678" And Rng(4).Value = "12133435" Then
For i = Rng.Cells.Count To 1 Step -1
If Left(Rng(i).Value, 3) = "302" Then
Rng(i).EntireRow.Delete
End If
Next
End If
lastrow = sheet.Cells(sheet.Rows.Count, "C").End(xlUp).Row
Set Rng = sheet.Range("C1:C" & lastrow)
For Each cell In Rng
cell.Value = ""
Next cell
End If
Count = Count + 1
Next
End Sub

Copy cell values to rows above based on a cell value

I am trying to develop a simple visualisation of a rack layout. I am able to get each item to appear in the rack at its lowest rack position (i.e. A 5 RU tall item that occupies slots 1-5 will appear in slot 1) (e.g. if my rack has 20 RUs, slot 1 (bottom of the rack) will be in row 20 and slot 20 (top of the rack) will be in row 1).
However i want to be able to merge the data in filled rows with the blank cells above.
So the item in slot 1 will have data in row 20, the next 4 rows will be blank until the next item appears in slot 6 (Row 15).
Each row has 4 cells on information to merge (i.e. range B:E or that row)
Item Name, RU height, ID1, ID2
I have realised I cannot use merge functions directly as it will overwrite the cells with the blanks in the top row. I believe i would need a function to copy the data row multiple times into the blank cells, based on the value in the RU height cell, before merging each column individually based on merging cells containing identical values.
I haven't been able to find any existing code that does something like this, I have however been able to adapt some code to handle the merge half of the problem, so if the data has been copied into the blank cells above it will merge successfully.
Sub MergeCells()
'set your data rows here
Dim Rows As Integer: Rows = 38
Dim First As Integer: First = 19
Dim Last As Integer: Last = 0
Dim Rng As Range
Application.DisplayAlerts = False
With ActiveSheet
For i = 1 To Rows + 1
If .Range("B" & i).Value <> .Range("B" & First).Value Then
If i - 1 > First Then
Last = i - 1
Set Rng = .Range("B" & First, "B" & Last)
Rng.MergeCells = True
Set Rng = .Range("C" & First, "C" & Last)
Rng.MergeCells = True
Set Rng = .Range("D" & First, "D" & Last)
Rng.MergeCells = True
Set Rng = .Range("E" & First, "E" & Last)
Rng.MergeCells = True
End If
First = i
Last = 0
End If
Next i
End With
Application.DisplayAlerts = True
End Sub
If someone can advise on how to get the data copied I should be able to cobble together a solution.
UPDATE..based on #TimWilliam answers i have put together the following code:
Sub MergeCellsX()
'set your data rows here
Dim Rows As Integer: Rows = 38
Dim col As Range
Dim First As Integer: First = 19
Dim Last As Integer: Last = 51
Dim rng As Range
With ActiveSheet
Set rng = .Range("B" & First, "B" & Last)
rng.Cells(1).Value = rng.Cells(rng.Cells.Count).Value 'copy last value to first cell
rng.MergeCells = True
Application.DisplayAlerts = False
For Each col In .Range("B" & First & ":E" & Last).Columns
MergeWithLastValue col
Next col
End With
Application.DisplayAlerts = True
End Sub
However it is putting the data in the very top on the range. It isnt taking into account the RU height value in column C.
I am not sure where the
Sub MergeWithLastValue(rng As Range)
With rng
.Cells(1).Value = .Cells(.Cells.Count).Value
.MergeCells = True
End With
End Sub
line of code should sit to reference this value?
Before and After:
EDIT - replaced everything with an approach based off the value in the "RU" cell
Sub MergeAreas()
Dim rw As Long, x As Long, rng As Range
Dim RU As Long, rngMerge As Range, col As Range
Dim rwEnd As Long
rw = 23
rwEnd = rw - 20
Do While rw >= rwEnd
' "Item#" column is 2/B
Set rng = ActiveSheet.Cells(rw, 3).Resize(1, 4)
If rng.Cells(1) <> "" Then
RU = rng.Cells(2).Value
'Here you need to check that the "RU space" doesn't extend
' past the top of the block
Set rngMerge = rng.Offset(-(RU - 1), 0).Resize(RU)
'here you should check for "collisions" between this
' item and anything above it in its RU space, otherwise
' the item above will get wiped out
For Each col In rngMerge.Columns
col.Cells(1).Value = col.Cells(col.Cells.Count).Value
Application.DisplayAlerts = False
col.MergeCells = True
Application.DisplayAlerts = True
Next col
rw = rw - RU
Else
rw = rw - 1
End If
Loop
End Sub

VBA Excel - Search rows for string and if found copy entire cell to a specific location

I got 3000 rows of data in Excel.
Each row contains the same type of information but not in the right order.
What i need to do is to gather the same type of information under the same column.I would like to create a macro that is going to:
Search a row for a partial string (some values have similar strings but fall under different categories)
If the string is part of a cell copy the entire cell in a
new location
Repeat for the next row
Thanks in advance
Sub MoveColumns()
Dim LastRow As Long
Dim rFind As Range
Dim r As String
Dim m As Integer
LastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
MsgBox (LastRow)
For n = 1 To LastRow
r = n & ":" & n
Range(r).Select
With Range(r)
Set rFind = .Find(What:="Spain*", LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not rFind Is Nothing Then
If rFind.Column < 6 Then
m = 6 - rFind.Column
Range(Cells(n, 1), Cells(n, m)).Insert Shift:=xlToRight
ElseIf rFind.Column > 6 Then
m = rFind.Column - 6
Range(Cells(n, 1), Cells(n, m)).Delete Shift:=xlToLeft
End If
End If
End With
Next
End Sub
UPDATED
If row doesn't contain any value starts with "Spain", this row is simply ignored and skipped.
I hope you can modify and customize the way suitable for your data.

Resources