i have a excel sheet that looks like this:
Category Value
X 1
X 2
X 3
X 4
Y 1
Z 1
Z 2
And I would like to transform it into categories and get value rows into columns. Like that:
Category Value1 Value2 Value3 Value4
X 1 2 3 4
Y 1
Z 1 2
Guys, any idea how to do it? I am really stuck with it.
many thanks.
Walk up your data from the bottom to the top, reorganizing as you go.
Option Explicit
Sub expandValues()
Dim i As Long, mx As Long, arr As Variant, tmp As Variant
With Worksheets("sheet7")
'determine no of values and create new headers
For i = 1 To Application.Max(.Columns("B"))
.Cells(1, i + 1) = "value" & i
Next i
'transfer values
For i = .Cells(.Rows.Count, "A").End(xlUp).Row To 3 Step -1
If .Cells(i, "A").Value2 = .Cells(i - 1, "A").Value2 Then
With .Range(.Cells(i, "B"), .Cells(i, .Columns.Count).End(xlToLeft))
.Parent.Cells(i - 1, 3).Resize(1, .Columns.Count) = .Value2
End With
.Cells(i, "A").EntireRow.Delete
End If
Next i
End With
End Sub
If I m not mistaking you can simply use a pivot table:
It will make you pass from this:
to this:
you just need to put the two columns in the rows columns and values as of below:
After that you can just change the names of your columns
Here's the formula approach ,
First write this array formula in cell C2 and press Ctrl+Shift+Enter to find unique values from column A (i.e. X, Y, Z) and drag it down.
=IFERROR(INDEX($A$2:$A$8, MATCH(0, COUNTIF($C$1:C1, $A$2:$A$8), 0)),"")
Now fill your column headers for value1 , value2 and so on.
Finally write this formula in cell D2 and drag it to right and down.
=IF(COUNTIFS($A$2:$A$8,$C2,$B$2:$B$8,RIGHT(D$1,1))>0,RIGHT(D$1,1)*1,"")
Related
I am having 2 columns containing data in this format:
ColA ColB
a x
b y
c z
x a
y b
z c
I am having difficulty to filter out the matching pairs such as values a,x and x,a.
Any help would be greatly appreciated!
EDIT:
Ideally I'd like to have a third colum with unique values to each pair such as
ColA ColB ColC
a x 1
b y 2
c z 3
x a 1
y b 2
z c 3
So that I can filter them out or do a pivot table quickly.
Please give this a try...
Sub PairCount()
Dim x, y(), dict
Dim i As Long, cnt As Long
x = Range("A1").CurrentRegion.Value
ReDim y(1 To UBound(x, 1))
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(x, 1)
If Not dict.exists(x(i, 1) & x(i, 2)) Or Not dict.exists(x(i, 2) & x(i, 1)) Then
cnt = cnt + 1
dict.Item(x(i, 1) & x(i, 2)) = cnt
dict.Item(x(i, 2) & x(i, 1)) = cnt
End If
Next i
For i = 1 To UBound(x, 1)
y(i) = dict.Item(x(i, 1) & x(i, 2))
Next i
Columns(3).Clear
Range("C1").Resize(UBound(y)).Value = Application.Transpose(y)
Set dict = Nothing
End Sub
How about using this kind of formula in the third column?
=IF(A1<B1,A1 & B1, B1 & A1)
Thank you all for your answers.
I just solved the problem using a simple semi-automatic way:
I first wrote a "match" in first cell in the third column (C2). Then I wrote this formula in the next cell (C3) based on an expanding VLOOKUP
=IF(ISNONTEXT(VLOOKUP(B3,$A$1:C2,3,0)),"match","")
I basically search for the current value in the second column, if its corresponding value in the first column has a match in the third column. If not then place "match"
That way I have "match" in the third column in front of unique values, which makes it very easy for me to manipulate further.
I'm attempting to transpose - if that's the right application of the term - pairs of columns into repeating rows. In concrete terms, I need to go from this:
Thing1 6 0.29 5 0.23 7 0.19 8 0.11
to this:
Thing1 6 0.29
Thing1 5 0.23
Thing1 7 0.19
Thing1 8 0.11
This operation will occur with at least 7 pairs of columns for several hundred "things." The part I can't figure out is how to group/lock the pairs to be treated as one unit.
In some ways, I'm trying to do the opposite of what is normally done. One example is here: Transpose and group data but it doesn't quite fit, even if I attempt to look at it backwards.
EDIT: Another example that is similar, but I need to do almost the reverse: How to transpose one or more column pairs to the matching record in Excel?
My VBA kung fu is weak, but I'm willing to try whatever your collective wisdom suggests.
Ideas are welcome, and in any case, thank you for reading.
Here is the Excel formula solution just in case. If the source data starts at A1, then formula in the first destination cell will be =$A$1 and the 2 formulas to the right will be
= OFFSET( A$1, 0, ROW( A1 ) * 2 - 1 )
and
= OFFSET( A$1, 0, ROW( A1 ) * 2 )
copy the 3 formula cells and paste in the range below them
Update
VBA version (set r to the source range and replace c3 with the first cell in the destination range)
Set r = [a1:i1]
set d = [c3].Resize(r.Count \ 2, 3)
d.Formula = "=index(" & r.Address & ",if(column(a1)=1,1,row(a1)*2-2+column(a1)))"
Here is a VBA solution.
To implement this, press Alt+F11 to open the VBA editor.
Right click to the left side and select "Insert Module"
Paste the code into the right side of this.
You may want to change the output sheet name as I have shown in the code.
I use Sheet2 to place the transposed data, but you can use whatever you want.
After you have done this, you may close the editor and select the sheet with your non-transposed data.
Run the macro by pressing Alt+F8, clicking on the macro, and pressing Run
Sheet2 should contain the results you are looking for.
Sub ForJeremy() 'You can call this whatever you want
Dim EndCol, OutSheet, OutRow, c, x
Application.ScreenUpdating = False
EndCol = ActiveSheet.UsedRange.columns.Count
'What sheet do I put these values on?
Set OutSheet = Sheets("Sheet2") 'Put the name in the quotes
OutSheet.Cells.Delete xlShiftUp 'This clears the output sheet.
OutRow = 1
For Each c In Intersect(ActiveSheet.UsedRange, ActiveSheet.Range("A:A"))
For x = 2 To EndCol Step 2
OutSheet.Cells(OutRow, 1) = c.Value
OutSheet.Cells(OutRow, 2) = Cells(c.Row, x)
OutSheet.Cells(OutRow, 3) = Cells(c.Row, x + 1)
OutRow = OutRow + 1
Next x
Next c
OutSheet.Select
Application.ScreenUpdating = True
End Sub
Input:
Output:
Edit: If you wanted to add an additional column to the beginning that would also just display to the side, you would change the code like this:
For Each c In Intersect(ActiveSheet.UsedRange, ActiveSheet.Range("A:A"))
For x = 3 To EndCol Step 2 'Changed 2 to 3
OutSheet.Cells(OutRow, 1) = c.Value
OutSheet.Cells(OutRow, 2) = Cells(c.Row, 2) 'Added this line
OutSheet.Cells(OutRow, 3) = Cells(c.Row, x) 'Changed to Col 3
OutSheet.Cells(OutRow, 4) = Cells(c.Row, x + 1) 'Changed to Col 4
OutRow = OutRow + 1
Next x
Next c
To better explain this loop,
It goes through each cell in column A from the top to the bottom.
The inner loop scoots over 2 columns at a time.
So we start at column B, and next is D, and next is F .. and so on.
So once we have that value, we grab the value to the right of it as well.
That's what the Cells(c.Row, x) and Cells(c.Row, x + 1) does.
The OutSheet.Cells(OutRow, 1) = c.Value says - just make the first column match the first column.
When we add the second one, OutSheet.Cells(OutRow, 2) = Cells(c.Row, 2) 'Added this line we are saying, match the second column too.
Hope I did a decent job explaining.
I have 2 columns, one with dates (column A:A, of type dd/mm/yyyy hh:mm) the other with values of a parameter (column B:B), registered at each date. But not all dates have registered values (in which case in column B I will have -9999.
What I need is to copy to other columns (say D:D and E:E) only the cells where there is a value other than -9999 and the correspondent date too. For example:
Example
My data series is pretty long, it can get to 10000 or more lines, so I cannot do this selection “manually”. I would prefer macros, not array formulae, because I want to choose the moment of the calculation.
This code should do what you are looking for. This will copy all rows with a value in column B, into columns D and E.'
Sub copyrows()
Dim RowNo, newRowNo As Long
RowNo = 2
newRowNo = 2
With ThisWorkbook.ActiveSheet
.Cells(1, 4).Value = "Date"
.Cells(1, 5).Value = "H_Selected"
Do Until .Cells(RowNo, 1) = ""
If .Cells(RowNo, 2) <> "-9999" Then
.Cells(newRowNo, 4) = .Cells(RowNo, 1)
.Cells(newRowNo, 5) = .Cells(RowNo, 2)
newRowNo = newRowNo + 1
End If
RowNo = RowNo + 1
Loop
End With
End Sub
I have wbk1.worksheet(1) and wbk2.worksheet(1).
wbk1.worksheet(1) has a list of values in column A
wbk2.worksheet(2) has the same list of values that may occur multiple times in column A with a number value in the offset(0,1) cell.
I need to do an index or match to find all of values in wbk2 and sum all of the offset(0,1) values. Then take that sum and put it in the offset(0,1) cell in wbk1.worksheets(1).
Example:
Workbook 1, sheet 1
Column A Column B
value 1
value 2
value 3
Workbook 2, sheet 1
Column A Column B
value 1 15
value 2 2
value 1 3
value 1 12
End Result:
Workbook 1, sheet 1
Column A Column B
value 1 30
value 2 2
value 3 0
I've tried doing a for each loop, but I'm still a noob to vb, so clearly not doing something right:
For Each x In rngWbk1
Set cellrngwbk2 = wbk2.Worksheets(1).Cells.Find(What:=x, LookIn:=xlValues)
If Not cellrngwbk2 Is Nothing Then
For Each y In rngwbk1
If y = cellrngwbk2 Then
total = total + cellrngwbk2.Offset(0, 1).Value
Else
End If
Next y
x.Offset(0, 1).Value = total
total = 0 'resets total value for next x value
Else
End If
next x
If VBA is not a requirement, a simple =SUMIF() statement has the same effect.
The function would look something like this:
=SUMIF([Wbk2.xlsx]Sheet1!A2:A5,Table1[[#This Row],[ID]],[Wbk2.xlsx]Sheet1!B2:B5)
There is more efficient way. You can use SUMIF formula to calculate values and then rewrite formula with result values. If rngWbk1 corresponds to the values in column A in wbk1.worksheets(1), you can use following code:
Dim frm As String
Dim startCell As String
startCell = Replace(rngWbk1.Cells(1, 1).Offset(0, -1).Address, "$", "")
frm = "=SUMIF('[" & wbk2.Name & "]" & wbk2.Worksheets(1).Name & "'!A:A," & startCell & ", '[" & wbk2.Name & "]" & wbk2.Worksheets(1).Name & "'!B:B)"
With rngWbk1.Offset(0, 1)
.Formula = frm
.Calculate
.Value = .Value
End With
If rngWbk1 doesn't correspond values in column A, you need to adjust startCell in example to startCell = "A2" and change With rngWbk1.Offset(0, 1) to sth like With wbk1.Worksheets(1).Range("B1:B100")
I need some help converting three colums into a matrix using excel macro.
Here is an example:
From this:
A A 0
A B 23
A C 3
B A 7
B B 56
B C 33
C A 31
C B 6
C C 5
to this:
A B C
A 0 23 3
B 7 56 33
C 31 6 5
Hope you can help me.
Thanks
Not quite sure what exactly you are meaning by matrix. For the code below I assumed you were looking for a way to read the data in the first two columns as Row and Column data of the output table. Assume the input data is in the Columns 1 - 3 of "Sheet1"
Sub ConvertTableOfData()
Dim testArray(1 to 3)
Dim chkROW as Integer
Dim chkCOL as Integer
Dim chkVAL as Integer
'// index the Row and Column headers
testArray(1) = "A"
testArray(2) = "B"
testArray(3) = "C"
'// Iterate through every row in the initial dataset
For i = 1 to Worksheets("Sheet1").Cells(1, 1).End(xlDown).Row
With Worksheets("Sheet1")
'// Assign the Output Row and Column values
'// based on the array indices
For j = 1 to UBound(testArray, 1)
If .Cells(i, 1) = testArray(j) Then
chkROW = j
End If
If .Cells(i, 2) = testArray(j) Then
chkCOL = j
End If
Next j
'// store the actual value
chkVAL = .Cells(i, 3)
End With
'// output table (in Sheet2)
With Worksheets("Sheet2")
.Cells(chkROW, chkCOL) = chkVAL
End With
Next i
'// Add headers to Output table
For i = 1 to 3
With Worksheets("Sheet2")
.Cells(i + 1, 1) = testArray(i)
.Cells(i, i + 1) = testArray(i)
End With
Next i
End Sub
You can also perform this without VBA.
Assume your table of data is in the range A1:C9.
Assume the first number (0) in the 3 by 3 grid of data is cell F3, with A, B, C in the row above, and A, B, C in the column to the left.
Enter the formula in cell F3 as
=INDEX($C$1:$C$9,SUMPRODUCT(--($A$1:$A$9=$E3),--($B$1:$B$9=F$2),ROW($A$1:$A$9)))
Copy this formula to all 9 cells in the 3 by 3 grid.
This generalized to any size of data.