The code below sorts the cell array in descending order using column 2, I will like to extract the numbers in the cell array in column 4 and convert them to a matrix.
data = cell (9,4);
col1 = ['A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I'];
col2 = [-45 -90 -50 -54 -70 -57 -75 -64 -23];
col3 = [{'1,1'},{'1,5'},{'3,9'},{'4,2'},{'4,6'},{'6,2'},{'7,6'},{'6,9'},{'9,9'}];
col4 = [{2 3 4 5 8},{1 3 4 5 8},{1 2 5 7 8},{1 2 3 6 7},{3 4 7 8},{2 4 8 9},{2 4 5 9},{4 5 7 9},{2 6 7 8}];
for i = 1:length(data)
data{i,1} = col1(i);
data{i,2} = col2(i);
data{i,3} = col3(i);
data{i,4} = col4(i);
end
[trash, idx] = sort([data{:,2}], 'descend');
newData = data(idx,:)
Thank you for your help :)
You probably meant to use cell arrays throughout. Your code above doesn't do what you think it does. Here is a corrected version.
data = cell (9,4);
col1 = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'};
col2 = {-45, -90, -50, -54, -70, -57, -75, -64, -23};
col3 = {'1,1', '1,5', '3,9', '4,2', '4,6', '6,2', '7,6', '6,9', '9,9'}; %%%
col4 = {[2 3 4 5 8],[1 3 4 5 8],[1 2 5 7 8],[1 2 3 6 7],[3 4 7 8],[2 4 8 9],[2 4 5 9],[4 5 7 9],[2 6 7 8]}; %%%
for i = 1:length(data)
data{i,1} = col1{i};
data{i,2} = col2{i};
data{i,3} = col3{i}; %%%
data{i,4} = col4{i}; %%%
end
[~, idx] = sort([data{:,2}], 'descend'); %%%
newData = data(idx,:)
Now, as for "extracting to a matrix", if all rows contained the same number of elements, you could obtain this like so: [newData{:,4}]. But, as it stands now, you can't, because some rows have 4 elements, and some 5. So you need to decide whether you want to add zeros left or right etc to make them of equal size.
Also, it's not clear why you say 9x1. If your intention is to perform an operation on these rows such that they return a single value, you have not made this clear here.
Related
I have two different matrices A and B:
A =
[7 8 9;
4 5 6]
B =
[22 32 12;
9 8 10]
sortB =
[12 22 32;
8 9 10]
sortindex_B=[3 1 2;2 1 3];, i.e., 12 is in the third position of matrix B, 22 in first, and 32 in second position; similarly for the second row.
Now I want to sort A depending on Sortindex_B (i.e., in matrix A I want 7 as the third element, 8 as the first, and 9 as the second element of the first row; and similarly for the second row: 4 at the second, 5 at the first, and 6 as the third element). Hence the result should look like:
A_final =
[8 9 7;
5 4 6]
How can I achieve this?
You can ask for the sorting index matrix from sort command, when sorting B, and thereafter operate on A as a cell, in so making cellfun available:
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
[sortB, I] = sort(B,2);
Icell = mat2cell(I,ones(1, size(I,1)),size(I,2));
Acell = mat2cell(A,ones(1, size(I,1)),size(I,2));
sortA = cell2mat(...
cellfun(#(x,y) y(x), Icell, Acell, 'UniformOutput', false))
Output (you state first row output as 8 9 7, but did you really mean 9 7 8?)
sortA =
9 7 8
5 4 6
For sorting as specifically specified in your question; re-map index matrix I:
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
[sortB, I] = sort(B,2);
%// re-map I
for i = 1:size(I,1)
Itmp = I(i,:);
for j = 1:size(I,2)
I(i,Itmp(j)) = j;
end
end
Icell = mat2cell(I,ones(1, size(I,1)),size(I,2));
ImapCell = mat2cell(Imap,ones(1, size(I,1)),size(I,2));
Acell = mat2cell(A,ones(1, size(I,1)),size(I,2));
sortA = cell2mat(...
cellfun(#(x,y) y(x), Icell, Acell, 'UniformOutput', false))
Output
sortA =
8 9 7
5 4 6
Use a combination of sort as in dfri's answer and sub2ind:
A = [7 8 9;
4 5 6];
B = [22 32 12;
9 8 10];
[sortB, sortindex_B] = sort(B,2);
[~, colIdx] = sort(sortindex_B,2);
rowIdx = ndgrid(1:size(B,1),1:size(B,2));
idx = sub2ind(size(B),rowIdx,colIdx);
sortA = A(idx)
ans =
8 9 7
5 4 6
you will have to do this row by row using the index values that the sort function returns.
Something like this should do the trick and is expandable to any number of rows that your matrix A and B may have. This does also validate that A and B are the same size before it continues.
B= [22 32 12; 9 8 10]
A = [7 8 9; 4 5 6];
assert(all(size(A) == size(B)));
sortB = zeros(size(B));
finalA= zeros(size(A));
for i = 1:size(B,1)
[sorted,idx] = sort(B(i,:));
sortB(i,:) = sorted;
tempA = A(i,:);
tempA = tempA(idx);
finalA(i,:) = tempA;
end
There are many clever ways to do this, including this for loop. I hope the comments will explain the logic.
clear; %// input the sample data
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
sortB = [12 22 32; 8 9 10];
%// loop through every element in B
[R C]=size(B);
for i=1:C
for j=1:R
%// Where does A(j,i) need to go in Afinal?
%// It needs to go in the j-th row, and in
%// whatever column of B(j,:) equals sortB(j,i).
Afinal( j , find( B(j,:) ==sortB(j,i)) ) = A(j,i);
end
end
And the result:
>> Afinal
Afinal =
8 9 7
5 4 6
It would be appreciated if you could help me concatenate two cell arrays that have different sizes. For example, consider the cell arrays:
a={'p' 'e' 't' 'k'; 2 3 4 6; 3 5 9 8; 5 4 1 0; 8 9 6 5};
b={'a' 'v'; 1 2; 3 4; 0 5; 6 8};
Array b could have a different size, depending on the iteration result. I want to combine these cell arrays, so that I end up with
c={'p' 'e' 't' 'k';2 3 4 6; 3 5 9 8; 5 4 1 0; ...
8 9 6 5;'a' 'v' NaN NaN;1 2 NaN NaN; 3 4 NaN NaN;0 5 NaN NaN; 6 8 NaN NaN};
How can I do this, when the sizes of a and b are different each time I run my code?
You first need to "pad" the smaller cell array, then you can concatenate both cell arrays with standard methods. In the comment to your question you indicated that you want to pad the matrix with NaN. This is how you could do it, assuming that the width of array b is smaller or equal to the width of array a:
a={'p' 'e' 't' 'k';2 3 4 6; 3 5 9 8; 5 4 1 0;8 9 6 5};
b={'a' 'v' ;1 2; 3 4;0 5; 6 8};
sa = size(a);
sb = size(b);
columns_to_pad = sa(2) - sb(2);
padding = num2cell(NaN*ones(sb(1), columns_to_pad));
b_padded = [b, padding];
c = [a; b_padded];
Referring to Reshape row wise w/ different starting/ending elements number #Divakar came with a nice solution but, what if the number of columns is not always the same?
Sample run -
>> A'
ans =
4 9 8 9 6 1 8 9 7 7 7 4 6 2 7 1
>> out
out =
4 9 8 9 0 0
6 1 8 9 7 7
7 4 6 2 7 1
I took only the first 4 terms of A and put them in out, then fill the rest 2 empty cell with 0's. So the ncols = [4 6 6]. Unfortunately vet2mat doesn't allow vector as columns number.
Any suggestions?
You can employ bsxfun's masking capability here -
%// Random inputs
A = randi(9,1,15)
ncols = [4 6 5]
%// Initialize output arary of transposed size as compared to the desired
%// output arary size, as we need to insert values into it row-wise and MATLAB
%// follows column-major indexing
out = zeros(max(ncols),numel(ncols));
mask = bsxfun(#le,[1:max(ncols)]',ncols); %//'# valid positions mask for output
out(mask) = A; %// insert input array elements
out = out.' %//'# transpose output back to the desired output array size
Code run -
A =
5 3 7 2 7 2 4 6 8 1 9 7 5 4 5
ncols =
4 6 5
out =
5 3 7 2 0 0
7 2 4 6 8 1
9 7 5 4 5 0
You could use accumarray for that:
A = [4 9 8 9 6 1 8 9 7 7 7 4 6 2 7 1].'; %'// data
ncols = [4 6 6]; %// columns
n = max(ncols);
cs = cumsum(ncols);
ind = 1;
ind(cs+1) = 1;
ind = cumsum(ind(1:end-1)); %// `ind` tells the row for each element of A
result = accumarray(ind(:), A(:), [], #(x) {[x; zeros(n-numel(x),1)]}); %// split `A` as
%// dictated by `ind`, and fill with zeros. Each group is put into a cell.
result = [result{:}].'; %'// concatenate all cells
I have a list of strings or arrays with different length or size. I want to use the shortest string and compare with other strings by shifting the shortest string window one by one to do comparison.
Let's say I want to do addition, I have [2 1 3] as my shortest list and want to perform addition on [4 5 7 8 9]
1st addition: [2 1 3] + [4 5 7]
2nd addition: [2 1 3] + [5 7 8]
3rd addition: [2 1 3] + [7 8 9]
How can i do this using matlab?
Thanks
Say A is the longer vector and B the shorter one.
You can use hankel function to create a matrix where each row is a window of length 3 over A
>> hankel(A(1:3),A(3:end))
ans =
4 5 7
5 7 8
7 8 9
Now you just need to call bsxfun to do the desired action on each row:
L=numel(B);
bsxfun(#plus, B, hankel(A(1:L),A(L:end)))
results in
ans =
6 6 10
7 8 11
9 9 12
Where rows contain the desired output vectors.
Note that you can change #plus to #minus or any other user-defined function.
A simpler approach, if you don't care much about speed is using arrayfun and cell2mat. Note that this approach doesn't check which vector is which. a must be shorter than b.
a =
1 2 3
b =
1 3 5 2 4 6
c = cell2mat(arrayfun(#(n) a+b(n:n+numel(a)-1), 1:numel(b)-numel(a)+1,'UniformOutput',0).')
c =
2 5 8
4 7 5
6 4 7
3 6 9
You can create indices of a sliding window using hankel. Example:
a = [2 1 3];
b = [4 5 7 8 9];
idx = hankel(1:numel(a), numel(a):numel(b));
c = bsxfun(#plus, b(idx.'), a);
The result:
>> c
c =
6 6 10 % [2 1 3] + [4 5 7]
7 8 11 % [2 1 3] + [5 7 8]
9 9 12 % [2 1 3] + [7 8 9]
(Note: This assumes b is longer than a, swap them if otherwise).
I think you should do the following, assuming row arrays of doubles:
lenList(1) = length(list1);
lenList(2) = length(list2);
% find minumum length
[minLen, idx] = min(lenList);
% find length difference
lenDiff = abs(diff(lenList));
% initialize result
result = zeros(lenDiff + 1, minLen);
% Check which list is the longest
if idx == 1
shortList = list1;
longList = list2;
else
shortList = list2;
longList = list1;
end
% Perform math
for ii = 1:(lenDiff + 1)
result(ii, :) = shortList + longList(ii:(ii+minLen-1))
end
I have two datasets that have different number of columns:
ds1:
A B C
1 2 3
ds2:
A C D
2 3 4
Now I want to merge these two datasets:
result:
A B C D
1 2 3 0
2 3 0 4
As you can see, I just want to add 0, NaN or a blank if the variable names are not present in both datasets. I tried to use cat and join, but I can't figure out how to do it. Any hints?
Here is an ugly way to do it - and then a cleaner way below (added later). Problem is that as soon as you are working with cell arrays (since the data type is mixed - letters for the columns, then numbers) life gets hard. You can probably do better by creating a structure where column names and data are two separate arrays (see below)... but for now here is "a solution". I made life a little bit more interesting by having different numbers of rows in the two datasets as well as different numbers of columns - just to make sure that didn't break something.
ds1 = {'a','bb','c';1,2,3};
ds2 = {'aa','c','d', 'e';2,3,4,5; 5,6,7,8};
cols = unique({ds1{1,:} ds2{1,:}});
ds3 = cols;
n1 = size(ds1,1) - 1;
%%
for ii = 1:size(ds1,2)
ci = find(cellfun(#(x) isequal(x, ds1{1,ii}), cols));
if numel(ci) > 0
for jj = 1:n1
ds3{1+jj,ci} = ds1{1+jj, ii};
end
end
end
n2 = size(ds2, 1) - 1;
for ii = 1:size(ds2,2)
ci = find(cellfun(#(x) isequal(x, ds2{1,ii}), cols));
if numel(ci) > 0
for jj = 1:n2
ds3{1+n1+jj,ci} = ds2{1+jj, ii};
end
end
end
The resulting merged array:
'a' 'aa' 'bb' 'c' 'd' 'e'
[1] [] [ 2] [3] [] []
[] [ 2] [] [3] [4] [5]
[] [ 5] [] [6] [7] [8]
Not optimal, I'm sure - but it does what you asked... I hate doing this in loops but couldn't see a way around it. I hope one of the "real Matlab experts" will puke when he sees this and be spurred into giving you the clever one line answer.
EDIT I thought about this some more, and came up with a much more efficient algorithm:
% assuming column headers and data are in two separate arrays
ds1headers = {'a','bb','c'};
ds1data = [1 2 3; 2 3 4];
ds2headers = {'aa','c','d', 'e'};
ds2data = [2 3 4 5; 3 4 5 6; 4 5 6 7];
% as before, find unique column headers:
cols = unique({ds1headers{:} ds2headers{:}});
% convert to column numbers:
ds1conv = cellfun(#(x)find(ismember(cols, x)), ds1headers);
ds2conv = cellfun(#(x)find(ismember(cols, x)), ds2headers);
% now conversion is easy:
n1 = size(ds1data,1);
n2 = size(ds2data,1);
ds3data = zeros(n1+n2, numel(cols));
ds3data(1:n1, ds1conv) = ds1data;
ds3data(n1+(1:n2), ds2conv) = ds2data;
disp(cols)
disp(ds3data)
The result is
'a' 'aa' 'bb' 'c' 'd' 'e'
1 0 2 3 0 0
2 0 3 4 0 0
0 2 0 3 4 5
0 3 0 4 5 6
0 4 0 5 6 7
Looks like it would do the trick - and no ugly loops... I recognize now that this looks a little bit like #Magla's solution below (hadn't seen it when I posted my update, but it was clearly there before my latest edit) - except I still have a cell array for column names, and a few other improvements.
I would go for something like this. It fills the final matrix with zeros.
%examples (ABCD are replaced by indexes 1234)
A = [1 2 3; 11 12 13];
B = [1 3 5 8; 111 112 113 114];
%first mix the first rows of A and B
header = union(A(1,:), B(1,:))
%find the corresponding indexes in A and B
[Lia,LocbA] = ismember(A(1,:),header);
[Lia,LocbB] = ismember(B(1,:),header);
%concatenate the second rows of A and B
C = header
C(2,LocbA) = A(2,:);
C(3,LocbB) = B(2,:);
Results:
A =
1 2 3
11 12 13
B =
1 3 5 8
111 112 113 114
C =
1 2 3 5 8
11 12 13 0 0
111 0 112 113 114
EDIT: the code initially provided works with cells too (see below for an example). In this case, it fills the final cell array with empty cells. Contrary to #Floris solution, datasets to be merged are composed of both the column headers (first row) and the data (second row). I guess the data format you have will suit one of the two solutions.
%input modification (now with cells)
A = {'A' 'B' 'C'; 11 12 13};
B = {'A' 'C' 'E' 'H'; 111 112 113 114};
Results:
C =
'A' 'B' 'C' 'E' 'H'
[ 11] [12] [ 13] [] []
[111] [] [112] [113] [114]
First we create the example:
% Create test datasets:
A=1;
B=2;
C=3;
save db1
A=2;
clear B;
D=4;
save db2
clear;
Now the script would look more or less like:
% Your script starts here, replace your paths with the correct paths:
path_to_db1 = 'db1';
path_to_db2 = 'db2';
db1 = load(path_to_db1);
db2 = load(path_to_db2);
merge = db1;
for field = fieldnames(db1)'
field = field{1};
if isfield(db2,field)
merge.(field) = [merge.(field);db2.(field)];
else
merge.(field) = [merge.(field);0];
end
end
for field = fieldnames(db2)'
field = field{1};
if ~isfield(db1,field)
merge.(field) = [0;db2.(field)];
end
end
clear db1 db2;
The output:
>> merge.A
ans =
1
2
>> merge.B
ans =
2
0
>> merge.C
ans =
3
3
>> merge.D
ans =
0
4
But you may want them to be free variables on the workspace, not on the merge struct, so you may add the following code:
for field = fieldnames(merge)'
field=field{1};
eval(sprintf('%s = merge.%s;',field,field));
end