Grouping Large Amounts Of Data in a hierarchy in Excel - excel

Hi this is my first post so please be gentle. I have a large amount of data being exported from Access into Excel. Access reports exported into Excel don't behave nicely otherwise I would use that method. All the calculations are being done in Access due to complexity and size of the data involved. I need to have a user friendly way of drilling down the data hence grouping in Excel would be perfect if I could get it to work.
The data has a hierarchy as per the below:
State
Store
Category
Item
At a top level you would just see the states with all the data so you can compare performance. You would then click the plus in the state you wanted to analyse which would show all the stores in the state, then you would click on a store to show the categories and then click on the category to show the items in the category.
I can add a Group index if required as per the below (I have included the data format also if that helps).
There are a number of states with up to 100 stores in each state 10 categories and then 100 items per category - so a lot of data and hence a macro.
Any help would be greatly appreciated as I am tearing my hair out.
Group Description Data Format
1 State AA
2 Store 1
3 Sub category Text
4 Item 2222
4 Item 2223
3 Sub category Text A
4 Item 2225
4 Item 2226
3 Sub category Text B
4 Item 2228
4 Item 2229
3 Sub category Text C
4 Item 2231
4 Item 2232
2 Store 2
3 Sub category Text
4 Item 2222
4 Item 2223
3 Sub category Text A
4 Item 2225
4 Item 2226
3 Sub category Text B
4 Item 2228
4 Item 2229
3 Sub category Text C
4 Item 2231
4 Item 2232
1 State BB
2 Store 3
3 Sub category Text
4 Item 2222
4 Item 2223
3 Sub category Text A
4 Item 2225
4 Item 2226
3 Sub category Text B
4 Item 2228
4 Item 2229
3 Sub category Text C
4 Item 2231
4 Item 2232
2 Store 4
3 Sub category Text
4 Item 2222
4 Item 2223
3 Sub category Text A
4 Item 2225
4 Item 2226
3 Sub category Text B
4 Item 2228
4 Item 2229
3 Sub category Text C
4 Item 2231
4 Item 2232

From my answer in a different post:
Sub subGroupTest()
Dim sRng As Range, eRng As Range
Dim groupMap() As Variant
Dim subGrp As Long, i As Long, j As Long
Dim startRow As Range, lastRow As Range
Dim startGrp As Range, lastGrp As Range
ReDim groupMap(1 To 2, 1 To 1)
subGrp = 0
i = 0
Set startRow = Range("A1")
' Create a map of the groups with their cell addresses and an index of the lowest subgrouping
Do While (startRow.Offset(i).Value <> "")
groupMap(1, i + 1) = startRow.Offset(i).Address
groupMap(2, i + 1) = UBound(Split(startRow.Offset(i).Value, "."))
If subGrp < groupMap(2, i + 1) Then subGrp = groupMap(2, i + 1)
ReDim Preserve groupMap(1 To 2, 1 To (i + 2))
Set lastRow = Range(groupMap(1, i + 1))
i = i + 1
Loop
' Destroy already existing groups, otherwise we get errors
On Error Resume Next
For k = 1 To 10
Rows(startRow.Row & ":" & lastRow.Row).EntireRow.Ungroup
Next k
On Error GoTo 0
' Create the groups
' We do them by levels in descending order, ie. all groups with an index of 3 are grouped individually before we move to index 2
Do While (subGrp > 0)
For j = LBound(groupMap, 2) To UBound(groupMap, 2)
If groupMap(2, j) >= CStr(subGrp) Then
' If current value in the map matches the current group index
' Update group range references
If startGrp Is Nothing Then
Set startGrp = Range(groupMap(1, j))
End If
Set lastGrp = Range(groupMap(1, j))
Else
' If/when we reach this loop, it means we've reached the end of a subgroup
' Create the group we found in the previous loops
If Not startGrp Is Nothing And Not lastGrp Is Nothing Then Range(startGrp, lastGrp).EntireRow.Group
' Then, reset the group ranges so they're ready for the next group we encounter
If Not startGrp Is Nothing Then Set startGrp = Nothing
If Not lastGrp Is Nothing Then Set lastGrp = Nothing
End If
Next j
' Decrement the index
subGrp = subGrp - 1
Loop
End Sub

Related

Split up quantities to print labels

I’m looking for a function or Macro to help find a solution between an order sheet and a sheet dedicated to printing labels for the items ordered.
The order sheet has 3 columns of info: qtyordered, packaging and item. When someone orders more than 1 case of something, I need a label tag for each item.
I’m looking for a way to split the value in qtyordered column into individual rows that will repeat as many times (i.e.
| | | |
-|-|-
3.5|box|apples
becomes:
||||
-|-|-
1 | box | apples,
1 | box | apples,
1 | box | apples,
0.5 | box | apples,
All in consecutive rows.
I haven’t been able to get an idea of how to pull this off in excel with either functions or macros. I’m know I could use office scripts and handle it like I would in JavaScript; however, I don’t think this company has an up to date version of excel, so I have to go old school.
Any ideas on how to solve this riddle would be greatly appreciated
Thanks in advance!
Jimmy
Does this do what you want?
Contents of the "order" sheet, (range A1:C6):
qtyordered
packaging
item
3,5
box
apples
5
sack
plums
0
error
error
1
none
pinapples
3
wheelbarrow
melons
code:
Sub expandData2()
Dim rng As Range, arr() As Variant, targetCell As Range, splitSize As Double
'targetCell is where the result will start being written
Set targetCell = Worksheets("label").Range("A2")
splitSize = 1
'clear the previous labels, if there are any
With Worksheets("label")
If .Range("C2").Value <> "" Then
.Range("A2:C" & .Range("C" & .Rows.Count).End(xlUp).Row).Clear
End If
End With
'find the last row on the "order" sheet, assign the range with the order to the arr array
With Worksheets("order")
Set rng = .Range("A2:C" & .Range("C" & .Rows.Count).End(xlUp).Row)
End With
arr = rng
Dim quantity As Double, packaging As String, item As String, rowCounter
'loop through all rows of the order
For i = 1 To UBound(arr)
quantity = arr(i, 1)
packaging = arr(i, 2)
item = arr(i, 3)
'while quantity is larger than 0, subtract splitSize from it and create new label row
Do While quantity > 0
quantity = quantity - splitSize
If quantity < 0 Then
targetCell.Offset(rowCounter, 0) = quantity + splitSize
Else
targetCell.Offset(rowCounter, 0) = splitSize
End If
targetCell.Offset(rowCounter, 1) = packaging
targetCell.Offset(rowCounter, 2) = item
rowCounter = rowCounter + 1
Loop
Next i
End Sub
Result on "label" sheet:
qtyordered
packaging
item
1
box
apples
1
box
apples
1
box
apples
0,5
box
apples
1
sack
plums
1
sack
plums
1
sack
plums
1
sack
plums
1
sack
plums
1
none
pinapples
1
wheelbarrow
melons
1
wheelbarrow
melons
1
wheelbarrow
melons

Separating responses from multiple response survey into separate columns with sorting

I need help separating responses from a survey into different columns. Each "check all that apply" question has the responses from each respondent in one cell (e.g. 1,3,4 or 1,2 or 2,4, etc.). For example, I want to create x number of columns for all the answer choices, then code the responses 'yes' or 'no' in excel.
Q2
1,2,3
2,3,4
3,4
1,3,4
1,2,4
...
I learned how to separate the column by comma using Text to column but this is the code after I separate it:
Q2
1 2 3
2 3 4
3 4
1 3 4
1 2 4
...
What I want is each column have a similar value per row. Here is an example :
Q2
1 2 3
2 3 4
3 4
1 3 4
1 2 4
...
Is there a way to do it without moving each cell manually since there is like 100 answer? Thanks
For Office 365 Insider Channel:
=LET(ζ,0+TEXTSPLIT(A1,","),XLOOKUP(SEQUENCE(,MAX(ζ)),ζ,ζ,""))
Copy down to get similar results for the strings in A2, A3, etc.
If you didn't have access to Office 365 insider, you could do a similar thing using Split in VBA:
Sub test()
Dim LString As String
Dim LArray() As String
' Change to Long for larger ranges (question only required 100 rows)
Dim i As Integer, j As Integer, k As Integer, lastElement As Integer, LR As Integer
Const LC = 5
LR = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To LR
LString = Cells(i, 1)
LArray = Split(LString, ",")
lastElement = UBound(LArray, 1)
k = 0
For j = 1 To LC
If k > lastElement Then
Exit For
Else
If LArray(k) = j Then
Cells(i, j + 1) = LArray(k)
k = k + 1
End If
End If
Next j
Next i
End Sub
Assumes responses in each row are in ascending order and output range is initially blank.

How to automatically nest rows of an Excel spreadsheet using level values?

I am attempting to apply grouping to an Excel 2016 spreadsheet so that it can be more easily viewed and interpreted. The data in the spreadsheet is in a format similar to the following:
A B C
1 1 x y
2 1 x z
3 2 y y
4 2 x z
5 2 z x
6 1 x y
Column A already contains the numbers corresponding to the nesting levels I want in my spreadsheet i.e. rows 3, 4 and 5 are "children" of row 2, so should be grouped together accordingly. The highest level reached in this particular spreadsheet is 5. I do not need to have any further interaction between rows in the spreadsheet, such as calculating subtotals. The spreadsheet is approximately 800 lines and a good solution will be used elsewhere, so doing this manually is not an ideal solution.
How can I get the group function in Excel 2016 to recognise Column A as my grouping and apply the outline accordingly?
This VBA script has been updated to include more levels of grouping.
It will do what you have requested, grouping rows to the row above, based on the increment number.
How it works is explained as comments within the script, including what could cause a possible failure.
Just to note it will fail if anything other than a number is in column A and also if it does not meet the criteria specified in the example comments.
Sub GroupRanges()
' Group levels must start at one and increase by one for each group level
' An error is produced if any levels are skipped
' Excel can only handle eight groups, the script will give a message and end if there are more than eight level groups
' Example: 1 1 2 3 3 4 4 5 will work
' Example: 1 1 2 2 2 4 4 5 will fail and produce an error, in this case group level 3 was skipped.
' Example: 1 2 3 4 5 6 7 8 9 Will fail, too many levels (more than 8)
Dim Sht As Worksheet
Dim LastRow As Long
Dim CurRow As Long
Dim StartRng As Integer
Dim EndRng As Integer
Dim GrpLvl As Integer
Dim MaxLvl As Integer
' This can be changed to define a sheet name
Set Sht = ActiveSheet
' find the highest number in the range to set as a group level
MaxLvl = WorksheetFunction.Max(Range("A:A"))
' If the Max level is greater than 8, then end the script as grouping cannot go beyond 8 levels
If MaxLvl >= 9 Then
MsgBox "You have " & MaxLvl & " group levels, Excel can only handle up to eight groups. This script will now end."
Exit Sub ' end the script if above eight groups
End If
'Set the Starting Group Level.
GrpLvl = 2
' find the last used row
LastRow = Sht.Cells(Sht.Rows.Count, "A").End(xlUp).Row
' Change the grouping to the cell above the range
Sht.Outline.SummaryRow = xlAbove
' Remove existing groups to prevent unrequired group levels.
' We now need to suppress error massages when trying to remove group levels that may not exist.
On Error Resume Next ' disable error messages
For x = 1 To 10 ' Repeat 10 times
Sht.Rows.Ungroup ' Remove Groups
Next x
On Error GoTo 0 ' Now it is important re-enable error messages
' Start the first loop to go through for each group level
For y = 2 To MaxLvl
'Reset the variables for each group level pass
CurRow = 1
StartRng = 0
EndRng = 0
' Start the inner loop through each row
For Z = 1 To LastRow
' Check value of cell, if value is 1 less than current group level then clear the Start/End Range Values
If Sht.Range("A" & CurRow) = GrpLvl - 1 Then
StartRng = 0
EndRng = 0
End If
' If cell value equals the group level then set Range Values accordingly
If Sht.Range("A" & CurRow) >= GrpLvl Then
' Check if row is the first of the range
If Sht.Range("A" & CurRow - 1) = GrpLvl - 1 Then
StartRng = CurRow
End If
' Check if row is the Last of the range
If Sht.Range("A" & CurRow + 1) <= 1 Then
EndRng = CurRow
End If
' If both range values are greater than 0 then group the range
If StartRng > 0 And EndRng > 0 Then
Sht.Rows(StartRng & ":" & EndRng).Rows.Group
End If
End If
CurRow = CurRow + 1 ' increase for the next row
Next Z ' repeat the inner loop
' Increase to the next group Level
GrpLvl = GrpLvl + 1
Next y ' repeat the first loop
End Sub

Application or User Defined Error

I have the following code in one of my workbooks. Basically, there are two sheets - one with a matrix where the top row is names, and the left column is dates. As of now, there are 735 rows (or dates) in the "attendance" spreadsheet, and around 80 names of individuals. It's supposed to track peoples' attendance.
I need to find out how much each person works per week. The code below attempts to do the following:
For each individual, scan the first period of 7 days (or 7 rows). Sum up that period, and place the value in a dictionary at an incremeneted item. So for instance, in 700 days, there will be a dictionary 100 units large. Do this only if the value which is going to be added to the dictionary is greater than 0 (if they worked that week)
Take this dictionary, and sum up the value of all the items in the dictionary, then divide it by the count of that dictionary, to get an average work week during a 7 day block.
Then, place the value of that dictionary into another dictionary, where the individuals name (top row in attendance sheet) is the key, and the value of the previous dictionary is the item.
In the "Summary Sheet", place the item associated with each key in the 9th column. For instance -
Individual | Weekly Hours
John | 20
Jane | 15
Joe | 12
the hope is to be able to derive the amount of work each person puts in per week unit (not 7 unit block) directly from attendance data that is entered into the spreadsheet.
The error occurs at the following line:
For k = 2 To attendanceSheet.Range("a1").End(xlRight).Row
Excel says "Application or user defined error: #1004"
Also, any assistance with optimization would be appreciated as this seems to be rather bulky code.
Public Sub calculateAverageWeek()
Dim i As Long
Dim attendanceSheet As Worksheet
Set attendanceSheet = ActiveWorkbook.Worksheets("Attendance")
'calculate week block
Dim lastRow As Long
lastRow = attendanceSheet.Range("a1").End(xlDown).Row
Dim indivName As Dictionary
Set indivName = New Dictionary
Dim k As Long
For k = 2 To attendanceSheet.Range("a1").End(xlRight).Row
Dim total As Long
Dim v As Variant
Dim totalWeeklyHours As Dictionary
Set totalWeeklyHours = New Dictionary
Dim j As Long
j = 1
Dim curTotal As Double
curTotal = 0
'scan attendance worksheet
For i = 2 To lastRow
curTotal = curTotal + attendanceSheet.Cells(i, 2)
If (i - 1) Mod 7 = 0 Then
If curTotal > 0 Then
totalWeeklyHours.Add j, curTotal
j = j + 1
curTotal = 0
Else
End If
End If
If i = lastRow Then
For Each v In totalWeeklyHours
total = total + totalWeeklyHours.Item(v)
Next
' Worksheets("Summary").Cells(2, 9) = CLng(total / totalWeeklyHours.Count)
indivName.Add attendanceSheet.Cells(k, 1), attendanceSheet.Cells(k, CLng(total / totalWeeklyHours.Count))
End If
Next i
Next k
For i = 2 To Worksheets("Summary").Range("A2").End(xlDown).Row
Worksheets("Summary").Cells(i, 9) = indivName.Item(Cells(i, 1))
Next i
End Sub

How can I filter and copy data in Excel?

I have a lot of data in an excel worksheet. For calculations, I would like to restrict this data to the relevant data only. That is: filter the data and put the subset in another worksheet.
Relevant data is data that falls within a given minimum and maximum value.
For example:
Suppose I want to filter column A for values between 1 and 2, and column B for values between 0 and 1. Result should become like this.
A B C = Data
1 0 0 0
2 1 1 0
3 2 0 3
4 2 2 1
A B C = Result
1 1 1 0
2 2 0 3
Is there an easy solution for this?
The fact that I don't filter on exact matches apparently makes the problem more difficult.
Thanks in advance!
I've got a quick VBA procedure that will do just what you want...
Private Sub MultiFilter(DataRange As Range, CriteriaRange As Range, OutputRangeTL As Range)
Dim intRowCounter As Integer
Dim intColCounter As Integer
Dim varCurrentValue As Variant
Dim blnCriteriaError As Boolean
Dim rngOutputCurrent As Range
If CriteriaRange.Columns.Count <> DataRange.Columns.Count Then
Err.Raise Number:=513, Description:="CriteriaRange and DataRange must have same column count"
End If
If CriteriaRange.Rows.Count <> 2 Then
Err.Raise Number:=513, Description:="CriteriaRange must be of 2 rows"
End If
Set rngOutputCurrent = OutputRangeTL.Resize(1, DataRange.Columns.Count)
For intRowCounter = 1 To DataRange.Rows.Count
For intColCounter = 1 To DataRange.Columns.Count
varCurrentValue = DataRange.Cells(intRowCounter, intColCounter).Value
If Not (varCurrentValue >= CriteriaRange.Cells(1, intColCounter) _
And varCurrentValue <= CriteriaRange.Cells(2, intColCounter)) Then
''#i.e. criteria doesn't match
blnCriteriaError = True
Exit For
End If
Next intColCounter
If Not blnCriteriaError Then
''#i.e. matched all criteria
rngOutputCurrent.Value = DataRange.Resize(1).Offset(intRowCounter - 1).Value
Set rngOutputCurrent = rngOutputCurrent.Offset(1)
End If
blnCriteriaError = False
Next intRowCounter
End Sub
Usage:
DataRange:
0 0 0
1 1 0
2 0 3
2 2 1
CriteriaRange:
1 0 0
2 1 10
Then do:
Public Sub DoTheFilter()
MultiFilter Range("MyDataRange"), Range("MyCriteriaRange"), Range("MyOutputRangeTopLeft")
End Sub
The CriteriaRange is simply a 2 row range giving minimum and maximum values for each column.
This isn't the most elegant of most efficient way I'm sure, but I used it as a quick fix as I've needed to do this once or twice.
If you're not comfortable with using VBA code then let me know and I'm sure I can manage to convert it into a worksheet function for you (this would also have the added advantage of updating if you changed the criteria...)
Simon

Resources