I am researching bird migration patterns and I am having trouble trying to figure out the best and easiest method of moving data around in excel. I am pretty good at excel, but I am terrible at macros and VBA coding, so I apologize in advance if my thinking of coding this looks completely wrong, and that there is nothing wrong with seeking expert advice. So far, I have used a pivot table to narrow out birds based on species count, location and dates.
After that, I moved the data from the dates per species and stack them from a range to a single column.
I did find a vba code that works (even though the output is actually moving the data sideways from left to right, it still is the same thing “moves B4:P4, B5:P5, B6:P6, etc..”), but this is only a single range at a time:
Sub main()
Dim i As Long
Dim cell As Range
For Each cell In Range("B4:P13")
Range("S4").Offset(i).Value = cell.Value
i = i + 1
Next cell
End Sub
My problem is that there are 56 species and 3 locations. So I would need to move the data 168 times, which is ridiculous. After I arrange them, I run a single factor analysis 56 times per species in each of the three locations. If anyone can help, that would be amazing and be very helpful for science.
My idea / hopes and dreams:
If I can repeat the code within the same VBA code module and change the values of the ranges and output locations for each species. All 3 locations have the same general format and location of the ranges (plus minus two extra dates), or if I can set the location to another sheet. Like so…
Sub main()
Dim i As Long
Dim cell As Range
For Each cell In Range("B4:P13")
Range("S4").Offset(i).Value = cell.Value
i = i + 1
For Each cell In Range("B15:P24")
Range("U4").Offset(i).Value = cell.Value
i = i + 1
For Each cell In Range("B26:P35")
Range("W4").Offset(i).Value = cell.Value
i = i + 1
For Each cell In Range("B37:P46")
Range("Y4").Offset(i).Value = cell.Value
i = i + 1
etc…
Next cell
End Sub
To look something like this:
Or more preferably this:
Again thank you for the help and contributions. :D
Bit more involved that it seemed at first glance. I've made a few assumptions so might need some tweaking if these are not tenable:
the starting workbook has only one sheet for each location, i.e. the number of sheets equals the number of locations
data starts in B4 on each sheet (and species names in A3, A14 etc)
each location sheet has the same number of species
Do use more meaningful procedure and variable names for your actual code.
Sub x()
Dim nSpec As Long, nLoc As Long, i As Long, vSpec(), j As Long, k As Long, wsOut As Worksheet, r As Range
nLoc = Worksheets.Count 'number of locations
Set r = Worksheets(1).Range("A3")
Do Until IsEmpty(r)
i = i + 1
ReDim Preserve vSpec(1 To i)
vSpec(i) = r.Value
Set r = r.Offset(11)
Loop
nSpec = UBound(vSpec) 'number of species
Set wsOut = Worksheets.Add(after:=Worksheets(Worksheets.Count)) 'add results sheet
wsOut.Name = "Results"
For i = 1 To nLoc 'headings for results sheet
With Worksheets(i) 'for each location
For j = 1 To nSpec 'for each species
wsOut.Cells(1, (j - 1) * (nLoc + 1) + 1).Value = vSpec(j) 'species heading
wsOut.Cells(2, (j - 1) * (nLoc + 1) + i).Value = .Name 'location heading
Set r = .Range("B4").Offset((j - 1) * 11).Resize(10) 'assumes B4 is top left cell of data
Do Until IsEmpty(r(1))
wsOut.Cells(Rows.Count, (j - 1) * (nLoc + 1) + i).End(xlUp)(2).Resize(10).Value = r.Value 'transfer data
k = k + 1 'move to next column
Set r = .Range("B4").Offset((j - 1) * 11, k).Resize(10)
Loop
k = 0
Next j
End With
Next i
End Sub
I'm trying to generate a binomial tree for option evaluation, and I want to draw a lattice using values that comes from an array where values are already there.
So basically I have an array in my VBA that ressemble to this:
My array in vba
And I want to paste in an Excel sheet in this form:
How i want to paste in my worksheet
I don't necessarily ask for a code, I would just like to have an idea of the algorithm used to create the tree. I really tried to see any pattern and couldn't find.
What I identified is that if the number of columns of the array is (n+1), then then number of rows will be (2n-1).
Thanks in advance
This might give you an idea. You would need to tweak it if your array is not 1-based:
Sub InsertTree(Nodes As Variant, RootNode As Range)
'Assumes that Nodes is a 1-based array
'That has data 1-element in its first
'column, 2 in its second, etc. And that RootNode
'Has been chosen so that there is enough room
'for the tree
Dim i As Long, j As Long, n As Long
Dim TopNode As Range 'top node in each column
n = UBound(Nodes, 2)
Set TopNode = RootNode
For j = 1 To n
For i = 1 To j
TopNode.Offset(2 * (i - 1)).Value = Nodes(i, j)
Next i
If j < n Then Set TopNode = TopNode.Offset(-1, 1)
Next j
End Sub
As a test, I set my spreadsheet up so that beginning at A1 I had:
a b d g
c e h
f i
j
Then I ran:
InsertTree Range("A1:D4").Value,Range("F10")
And the result looks like this:
Need help with VBA code to remove approximate matching numbers from two columns that is Amt1 and Amt 2:
Amt 1 Amt 2
412.82 0
671.44 0
54.25 412.83
574.89 671.44
0 484.2
0 370.53
0 54.25
0 574.9
0 594.43
Desired Result: I want 412.82 from column Amt 1 and 412.83 from column Amt 2 should be removed from both the columns.
Sub removedup()
Dim source As Range
Dim iCol1 As Long
Dim iCol2 As Long
Dim nRow1 As Long
Dim nRow2 As Long
Dim nCol As Long
Dim nRow As Long
Set source = Selection
nCol = source.Columns.Count
nRow = source.Rows.Count
iCol1 = 1
iCol2 = 2
For iRow1 = 1 To nRow
For iRow2 = 1 To nRow
If (Cells(iRow1, iCol1) - Cells(iRow2, iCol2) >= -3) And (Cells(iRow1, iCol1) - Cells(iRow2, iCol2)) <= 3 Then
Cells(iRow1, iCol1) = ""
Cells(iRow2, iCol2) = ""
End If
Next iRow2
Next iRow1
End Sub
Actually this task would be much more complicated as you think
To find similar values you would need to work with the distances between these values and if the distance is smaller than a defined threshold then they are considered as being "similar".
But it gets really hard if there are more similar distance.
My thoughts, imagine the following data:
If you define that a distance <= 0.02 is considered as similar then the following pairs are considered as similar:
Scenario 1
You start comparing from the top and you find that 412,84 and 412,83 are similar and delete them immediately. Then you will remain with 412,81 and 412,85 which are not similar (distance is 0.04) and they will be kept.
Scenario 2
You compare first 412,84 and 412,85 and delete them as similar then you will remain with 412,81 and 412,83 and they will also be deleted as similar. No values will be kept at all.
What does that mean?
There is not only one solution for this szenario and you will get different results on the same data-set (with differently ordered values). So you have to calculate all of the scenarios and decide which one is the correct one, because your algorithm can't decide that.
What to do now?
Re-think what your actual problem is. Define new rules so that there will be only one definite solution for a case like this. Otherwise you will get random results.
Probably you asked the wrong question.
I am new to Vba and I have been trying to figure out how after an if statement to multiply two numbers in two different columns. the data in excel is laid out as below.What I am trying to do is to multiply the cost with the weight if the freighttype is for example store transfer but my code below does not work.Your help would be much appreciated.I do not know if I need two extra for loops for the cost and weight.
freighttype
Column(b)
Store Transfer
Ecommerce
Cost
Column(c)
7
6
Weight
column (e)
2
3
And the code is:
Option Explicit
Function essay(ft As Range) As Long
Dim x As Variant
For Each x In ft
If ft = "store transfer" Then
essay = Range("b2:b365").Offset(0, 1) * Range("b2:b365").Offset(0, 3)
Else
essay = 0
End If
Next x
End Function
Unlike Excel, you cannot multiply two arrays together in VBA.
For the equivalent, you can either loop through all the cells, multiplying them one by one and keeping a running total, or you can use the SUMPRODUCT worksheet function inside EVALUATE
Assuming, for example, that your ft range is in column B, starting with B2, you could use something like:
Option Explicit
Option Compare Text
Function essay(ft As Range) As Long
essay = Evaluate("=SUMPRODUCT((" & ft.Address & "=""store transfer"")*OFFSET(" & ft.Address & ",0,1)*OFFSET(" & ft.Address & ",0,3))")
End Function
for looping:
Function essay2(ft As Range) As Long
Dim c As Range
Dim L As Long
For Each c In ft
If c = "store transfer" Then _
L = L + c.Offset(0, 1) * c.Offset(0, 3)
Next c
essay2 = L
End Function
Note that the Option Compare Text statement makes the routine case insensitive.
Hi Guys I managed to solve the problem with your help ,please find the solution below.
Option Explicit
Function ecco(ft As Range) As Long
Dim x As Variant
Dim L As Long
For Each x In ft
If ft = "st" Then
L = x.Offset(0, 1) * x.Offset(0, 3)
Else
ecco = 0
End If
ecco = L
Next x
End Function
I sometimes work with symmetric matrices in MS-Excel (both v2007 and v2003).
Is there an option to help me to copy expressions from the lower triangle to the upper one?
It should be something like copy and paste/transponse but those functions normally work only with rectangular areas.
in the added picture you can see an exemple of an expression that I have to replicate by linking the symmetric value in the superior triangle of the matrix.
To get the number in the appropriate cell, we can use OFFSET and the cell address the forms the base of the table. Note that the formula will produce a *Circular Reference` error if entered in on the diagonal. The formula will work for both sides of the diagonal - you just have to decide which one will hold the data, and which will hold the formula.
Offset takes Row and Column to decide the target. By subtracting the base cell row and column from the current position, we can invert the row and columns, and get the data.
Using your example, with the origin of the table in B2, we end up with the following formula:
=OFFSET($B$2,COLUMN()-COLUMN($B$2),ROW()-ROW($B$2))
you can copy this formula into the cells, and get the reflection. Now you have the number, you can do any calculation you require on the reflection. Using your example, this would make the formula:
=10-OFFSET($B$2,COLUMN()-COLUMN($B$2),ROW()-ROW($B$2))
Result:
Using INDEX to make it non volatile would change the formula slightly. First, we would need a reference to the entire table, not just the top cell. Second, we would need to add 1 to the row/column calculation, as it refers to the first cell as row/column 1, not an offset of 0 as the previous formula.
=INDEX($B$2:$K$11,COLUMN()-COLUMN($B$2)+1,ROW()-ROW($B$2)+1)
and your example of 10-Cell would become:
=10-INDEX($B$2:$K$11,COLUMN()-COLUMN($B$2)+1,ROW()-ROW($B$2)+1)
As one of the above answers demonstrates, this can be done by using Excel formulas. I however find this to be a very tedious procedure. Especially if this is something you need to do on a regular basis. In that case VBA could save you a lot of time.
The following code will work on a square selection and fill the rest of the matrix no matter if it is the lower- or upper part of the matrix that is pre-filled.
Option Explicit
Sub FillSymetricMatrix()
Dim i As Integer, j As Integer
Dim SelRng As Range
Dim FillArea As String
Dim FRow As Integer
Dim FCol As Integer
Set SelRng = Selection
FRow = SelRng.Rows(1).Row
FCol = SelRng.Columns(1).Column
'Returns information about which area to fill
If ActiveSheet.Cells(FRow + SelRng.Rows.Count - 1, FCol).Value <> vbNullString Then 'Lower filled
If ActiveSheet.Cells(FRow, FCol + SelRng.Columns.Count - 1).Value = vbNullString Then 'Upper empty
FillArea = "Upper"
Else
FillArea = "Error"
End If
Else
If ActiveSheet.Cells(FRow, FCol + SelRng.Columns.Count - 1).Value <> vbNullString Then 'Upper filled
FillArea = "Lower"
Else
FillArea = "Error"
End If
End If
'Determines if the selection is square
If SelRng.Rows.Count <> SelRng.Columns.Count Then FillArea = "Error"
'Fills empty area of the square (symetric) matrix
Select Case FillArea
Case Is = "Upper"
For i = 0 To SelRng.Rows.Count - 1 Step 1
For j = 0 To SelRng.Columns.Count - 1 Step 1
If i <= j Then ActiveSheet.Cells(i + FRow, j + FCol).Value = ActiveSheet.Cells(j + FRow, i + FCol).Value
Next j
Next i
Case Is = "Lower"
For i = 0 To SelRng.Rows.Count - 1 Step 1
For j = 0 To SelRng.Columns.Count - 1 Step 1
If i <= j Then ActiveSheet.Cells(j + FRow, i + FCol).Value = ActiveSheet.Cells(i + FRow, j + FCol).Value
Next j
Next i
Case Else
MsgBox "The procedure cannot be performed on the current selection!"
End Select
End Sub
I guess what you need is a function which returns the "diagonal" value of a square matrix, e.g. for any X(j,k) return X(k,j)
Try this:
Function DIAGONAL(Arg As Range, Reference As Range) As Variant
Dim MyRow As Long, MyCol As Long
If Reference.Rows.Count <> Reference.Columns.Count Then
DIAGONAL = CVErr(xlErrRef)
Else
MyRow = Arg.Row - Reference.Row + 1
MyCol = Arg.Column - Reference.Column + 1
If MyRow < 1 Or MyCol < 1 Or MyRow > Reference.Rows.Count Or MyCol > Reference.Columns.Count Then
DIAGONAL = CVErr(xlErrNA)
Else
DIAGONAL = Reference(MyCol, MyRow)
End If
End If
End Function
once you entered this function in VBA, you can use it inside or outside your square matrix ... you just need to ensure that your argument (parameter: Arg) is within the matrix (parameter: Reference) ... or you get an #N/A error. Or you get a #REF error if the matrix isn't square.
So in your example you would enter into B4: =10-DIAGONAL(B4,$B$2:$K$11) and copy this throughout the lower triangle.
You can even transpose a complete matrix ... in your screen shot, move to cell B13, enter =DIAGONAL(B2,$B$2:$K$11) and copy 9x down & right
No buttons, no need to explicitely start a Sub ... any size of n x n matrix, handles strings and numbers, ...
Here is an example with VBA. Start with an un-filled table and a button.
Then make the button run the code:
Option Explicit
Private Sub symmButton_Click()
MakeSymmetric Range("B2")
End Sub
Public Sub MakeSymmetric(ByRef r As Range)
Dim M As Long
M = CountCols(r)
Dim vals() As Variant
vals = r.Resize(M, M).Value2
Dim i As Long, j As Long
For i = 2 To M
For j = 1 To i - 1
vals(i, j) = vals(j, i)
Next j
Next i
r.Resize(M, M).Value2 = vals
End Sub
Public Function CountCols(ByRef r As Range) As Long
If IsEmpty(r) Then
CountCols = 0
ElseIf IsEmpty(r.Offset(0, 1)) Then
CountCols = 1
Else
CountCols = r.Worksheet.Range(r, r.End(xlToRight)).Columns.Count
End If
End Function
and finally observe the results
Similar to Sean's solution, I would also use formulas. In order to get the transposed value, use this formula:
=INDEX($B$2:$G$7,COLUMN()-COLUMN($B$2)+1,ROW()-ROW($B$2)+1)
If you want to do a more complex operation (e.g. =10-[transposedValue]), I'd recommend you use a named range: Insert a new name, e.g. TransposedValuein the Name Manager. Instead of a cell link, provide the above formula. Now you can literally write the following formula in your matrix:
=10-TransposedValue
I have this way. As you said copy paste transpose work on rectangular range. And your problem is that you have a triangular range.
You will love this....
1). Select the square range containing your upper triangular matrix and Copy.
2). Select a cell in an empty place and do the following two steps
a.) Paste Special - Values
b.) Paste Special - Values - Transpose - Skip Blanks
And you have got your symmetric matrix :-)
Anil.
Mixing together Ja72's fill code with SeanC c's Excel function code, I think I can make a generic matrix template that is properly prefilled with the dynamic Excel formula. So dynamic, and can be reused without any copy and paste.
Public Sub MakeSymmetric(ByRef r As Range)
Dim M As Long
M = 300
' Was CountCols(r), but I just limited to 300 columns for now
Dim vals() As Variant
vals = r.Resize(M, M).Value2
Dim i As Long, j As Long
For i = 2 To M
For j = 1 To i - 1
vals(j, i) = "=OFFSET($B$2,COLUMN()-COLUMN($B$2),ROW()-ROW($B$2))"
Next j
'Make diagonal down the middle show ---
vals(j, i) = "---"
Next i
vals(1, 1) = "---"
r.Resize(M, M).Value2 = vals
End Sub
Sub FillSymmetric()
MakeSymmetric Range("B2")
End Sub
I don't really know any VB though, so I haven't quite figured out how to fill the header yet. I don't know Stackoverflow yet either, but I will try to add a picture.
Original List to Matrixize
Dynamically transposing values typed in SouthWest half to NorthEast half
Short answer: INDIRECT(ADDRESS(COLUMN(D2), ROW(D2)))
Explnation: you may remember we use coordinates with numbers to represent a location in Cartesian Coordinates System. So, it's easy to get a diagonal symmetric value e.g. just change (2, 3) to (3, 2).
But in Excel, we need a wordaround if we want to do so. Because, address is marked by a combination of a letter and a digit, say B2. You can't just change B2 to 2B.
Luckily, we can still use numbers to represent a cell by leveraging the power of COW() and COLUMN().
In the image below, C2 and B3 are symmetrical. This shows how to put the value of C2 to B3.
Making the formula from C.W. more generic (similar to Peter Albert), this will help when your matrix is not starting at A1 but e.g. in C10:
=INDIRECT(ADDRESS(COLUMN(C11)-COLUMN($C$10)+1,ROW(C11)-ROW($C$10)+1))
So, subtract the origin row/column and add 1.