I have a multidimensional array with a layout as set out below:
Banana 10 20 30 40
Coconut 5 10 2 4
Apple 3 4 5 6
I want to loop through a specific column range in a worksheet to check if the values are either 'Banana', 'Coconut' or 'Apple'. When the cell value equate to a value in the first column of my array, I want to then output the array values next to that specific identifier. So for instance I want the output to be as below:
Shark
Banana 10 20 30 40
Pear
Apple 3 4 5 6
I understand that I need to loop through each cell in my range and then evaluate if the cell is equal to the values in the first column of the array. However, I am not sure how to do this. Typically I just use the setup below but I would like to understand how I can create a better solution in this case where I only want to loop through the first column in the array.
For Each cell In ws.Range("OUTPUT")
For y = LBound(arr, 2) To UBound(arr, 2)
If cell.Value = y Then
For m = 1 To x
ws.Cells(cell.Row, n + 1) = arr(n, m)
Next m
n = n + 1
End If
Next y
Next cell
Related
I'd like to take a single array lets say 3x5 as follows
1 3 5
1 3 5
2 8 6
4 5 7
4 5 8
and write a code that will create a new array that adds the third column together with the previous number if the numbers in the first and second columns equal the numbers in the row below it.
since the first two values in row 1 and 2, then add the third elements in row 1 and 2 together
so the output from the array above should look like this
1 3 10
2 8 6
4 5 15
The function accumarray(subs,val) accumulate elements of vector val using the subscripts subs. So we can use this function to sum the elements in the third column having the same value in the first and second column. We can use unique(...,'rows') to determine which pairs of value are unique.
%Example data
A = [1 3 5,
1 3 5,
2 3 6,
4 5 7,
4 5 7]
%Get the unique pair of value based on the first two column (uni) and the associated index.
[uni,~,sub] = unique(A(:,1:2),'rows');
%Create the result using accumarray.
R = [uni, accumarray(sub,A(:,3))]
If the orders matters the script would be a little bit more complex:
%Get the unique pair of value based on the first two column (uni) and the associated index.
[uni,~,sub] = unique(A(:,1:2),'rows');
%Locate the consecutive similar row with this small tricks
dsub = diff([0;sub])~=0;
%Create the adjusted index
subo = cumsum(dsub);
%Create the new array
R = [uni(sub(dsub),:), accumarray(subo,A(:,3))]
Or you can get an identical result with a for loop:
R = A(1,:)
for ii = 2:length(A)
if all(A(ii-1,1:2)==A(ii,1:2))
R(end,3) = R(end,3)+A(ii,3)
else
R(end+1,:) = A(ii,:)
end
end
Benchmark:
With an array A of size 100000x3 on the mathworks live editor:
The for loop take about 5.5s (no pre-allocation, so it's pretty slow)
The vectorized method take about 0.012s
I'm having some trouble writing a MATLAB code that needs to locate the max value of each cell of my one cell array, vel_data, a 1x430 cell containing several excel sheets worth of data consisting of M rows x 1 column. I want to extract the max value, as well as every value before and after that max value until the first 0 is reached into a new cell array.
e.g. if the first cell in the array were [3 2 1 0 2 6 4 3 0 1 0] it would extract the values [0 2 6 4 3 0] and do so for every cell in the array.
I know the following extracts the max values of the cell array but I would like for it to do as I mentioned above.
d=dir(f);
for n=1:numel(d)
max_vel{n} = deal(max(vel_data{n}));
end
Any advice/sample code would be very much appreciated.
First index of max value extracted as idx. Then indexes of all elements that are 0 extracted as f1. Index of the element that is 0 and is immediately before max value extracted as f2. and f3 is index of the element that is 0 and is immediately after max value.
vel_data = {[3 1 0 2 6 4 0 1 0] , [1 1 0 9 3 0 4 6 9]}
for n=1:numel(vel_data)
data = vel_data{n};
[~,idx] = max(data);
f1 = find(data==0);
if isempty(f1)
max_vel{n} = data;
continue;
end
f2 = find(f1 < idx,1,'last');
f3 = find(f1 > idx,1);
if isempty(f2)
idx_first = 1;
else
idx_first =f1(f2);
end
if isempty(f3)
idx_last = numel(data);
else
idx_last =f1(f3);
end
max_vel{n} = data(idx_first:idx_last);
end
I think this question might be related to Ms Excel -> 2 columns into a 2 dimensional array but I can't quite make the connection.
I have a VBA script for filling missing missing data. I select two adjacent columns, and it finds any gaps in the second column and linearly interpolates based on (possibly irregular) spacing in the first column. For instance, I could use it on this data:
1 7
2 14
3 21
5 35
5.1
6 42
7
8
9 45
to get this output
1 7
2 14
3 21
5 35
5.1 35.7 <---1/10th the way between 35&42
6 42
7 43 <-- 1/3 the way between 42 & 45
8 44 <-- 2/3 the way between 42 & 45
9 45
This is very useful for me.
My trouble is that it only works on contiguous columns. I would like to be able to select two columns that are not adjacent to each other and have it work the same way. My code starts out like this:
Dim addr As String
addr = Selection.Address
Dim nR As Long
Dim nC As Long
'Reads Selected Cells' Row and Column Information
nR = Range(addr).Rows.Count
nC = Range(addr).Columns.Count
When I run this with contiguous columns selected, addr shows up in the Locals window with a value like "$A$2:$B$8" and nC = 2
When I run this with non-contiguous columns selected, addr shows up in the Locals window with a value like "$A$2:$A$8,$C$2:$C$8" and nC = 1.
Later on in the script, I collect the values in each column into an array. Here's how I deal with the second column, for example:
'Creates a Column 2 (col1) array, determines cells needed to interpolate for, changes font to bold and red, and reads its values
Dim col2() As Double
ReDim col2(0 To nR + 1)
i = 1
Do Until i > nR
If IsEmpty(Selection(i, 2)) Or Selection(i, 2) = 0 Or Selection(i, 2) = -901 Then
Selection(i, 2).Font.Bold = True
Selection(i, 2).Font.Color = RGB(255, 69, 0)
col2(i) = 9999999
Else
col2(i) = Selection(i, 2)
End If
i = i + 1
Loop
This is also busted, because even if my selection is "$A$2:$A$8,$C$2:$C$8" VBA will treat Selection(1,2) as a reference to $B$2, not the desired $C$2.
Anyone have a suggestion for how I can get VBA to treat non-contiguous selection the way it treats contiguous?
You're dealing with "disjoint ranges." Use the Areas collection, e.g., as described here. The first column should be in Selection.Areas(1) and the second column should be in Selection.Areas(2).
Given a logical column vector (size n x 1) v and an array a (size m x n) how do I generate a new array consisting of all the columns in a where the numerical index of said column (1...n) is 1 at the corresponding location in v.
So for example if v was
1
0
0
1
and a was
1 4 7 10
2 5 8 11
3 6 9 12
the new array would be
1 10
2 11
3 12
because the first and fourth elements of v are 1 (true), so the new array should contain the first and fourth columns of a.
I have tried a bunch of things involving normal logical indexing and transpose but I can't get it to work. All help is appreciated
You want to use the logical indexing to select the columns and select all rows. In the example below, I have explicitly cast v as a logical just in case it's not a logical matrix already.
new = a(:, logical(v))
1 10
2 11
3 12
I'm trying to figure out how to put values from a [N x 2] matrix to cells on the same row on a different worksheet.
The matrix, which changes, is something like:
1 0
1 2
1 3
2 0
2 1
2 2
... so on.
On a different sheet, using the items in the matrix, I want to create a vector that omits the zero, such as:
A B
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
... so on.
I already have an array filled with the values from the matrix. I am having problems trying to extract values from the array.
Eventually, there will be certain criteria and some combinations, so # of columns and rows will increase. So, I need to do this in VBA. Can anyone guide me in the right direction or provide some example code that I can reference?
Please let me know if I need to clarify anything.
A sample for your reference
Sub testArr()
'Declare a 4-by-2 matrix
Dim Data(3, 1) As Variant
Dim i As Long, j As Long
For i = 0 To UBound(Data, 1)
For j = 0 To UBound(Data, 2)
Data(i, j) = Int((10 - 1 + 1) * Rnd + 1)
Next j
Next i
'You extract a value from the array like this
MsgBox "data(2,1) = " & Data(2, 1)
End Sub