Convolution of two symbolic arrays on Matlab - matlab

I have two arrays:
p1=[sym(1) sym(2)]
p2=[sym(3) sym(4)]
I want to do the convolution of those two lists using conv function.
Matlab outputs the following:
Error using conv2
Invalid data type. First and second arguments must be numeric or logical.
Error in conv (line 43)
c = conv2(a(:),b(:),shape);
Can anyone help me how to deal with that?

Edit 1: i have not symbolic math toolbox so i demonstrated a matrix-wise operation on numeric values to calculate conv, i think this should do the trick on symbolic values either.
the conv function just accept numeric values.
there is a way to calculate conv matrix-wise
i demonstrate it with an example:
assume u and v are as follows :
u =
1 2 1 3
v =
2 7 1
>> conv(u,v)
ans =
2 11 17 15 22 3
instead we could first calculate u'*v, then do some rearranging and summing to calculate conv:
so first :
>> c=u'*v
c=
2 7 1
4 14 2
2 7 1
6 21 3
then we do some rearranging:
>> d=[c;zeros(3,3)]
d =
2 7 1
4 14 2
2 7 1
6 21 3
0 0 0
0 0 0
0 0 0
>>e= reshape(d(1:end-3),[6,3])
e=
2 0 0
4 7 0
2 14 1
6 7 2
0 21 1
0 0 3
and finally adding values together :
>> sum(e,2)
ans =
2
11
17
15
22
3
you can write your own code to use "v size" to do it(add (numel(v)*numel(v)) zeros to end of u'*v and so on.)

Related

Stem and leaf plot algorithm

I am trying to implement a stem and plot algorithm in MATLAB for educational purposes. Before I post my code, let me introduce what the steps of my approach are. Let us consider that we have two digit numbers:
A=[20 12 13 21 56 13 16 17 22 23 24];
Stems can be given by
stems=fix(A/10)
stems =
2 1 1 2 5 1 1 1 2 2 2
and leafs can be given by
leaf=fix(mod(A,10))
leaf =
0 2 3 1 6 3 6 7 2 3 4
What I have done, is to sort the stems and according to that sort leafs as well:
[stems, index]=sort(stems,'ascend')
leaf=leaf(index)
stems =
1 1 1 1 1 2 2 2 2 2 5
leaf =
2 3 3 6 7 0 1 2 3 4 6
This is the basic idea:
Count the frequency of occurrence of each number in stems
Take that many elements from leaf
Repeat this procedure for each stem, where at each step I am shortening the leaf array. So for instance for stems = 1, we have [5 1], so I will have
leaf(1:5)
ans =
2 3 3 6 7
leaf(1:5)=[]
leaf =
0 1 2 3 4 6
stems = 2 is again 5 times, so again:
leaf(1:5)
ans =
0 1 2 3 4
leaf(1:5)=[]
leaf =
6
Now for stems = 5, we have 1 leaf
leaf(1)
ans =
6
For this I used a map container and I have created the following code:
function stem_leaf_plot(v)
if ~isnumeric(v) % check that program will accept array as a integers
error( 'Input V must be numeric');
end
stems=fix(v/10);
leaf=fix(rem(v,10));
[stems, index]=sort(stems,'ascend');
leaf=leaf(index);
string_stems=num2str(stems);
%% count occurence of each stem
MAP=containers.Map();
n=length(stems); % total element of stems array
for ii=1:n
if isKey(MAP,string_stems(ii))
MAP(string_stems(ii))= MAP(string_stems(ii))+1;
else
MAP(string_stems(ii))=1;
end
end
MAP_count=length(MAP);
stem=num2str(cell2mat(keys(MAP)));
for jj=1:MAP_count
frequency=(MAP(string_stems(jj)));
fprintf('leafs of stem %d',stem(jj));
disp(leaf(1:frequency));
leaf(1:frequency)=[]; % delete elements step by step
end
end
However, the result of my code is
stem_leaf_plot(A)
leafs of stem 32 2 3 3 6
leafs of stem 49 7 0 1 2 3 4 6
What is wrong?
After suggestion of #Adriaan I used hist to count frequencies, instead of a container. Here is my updated code:
function stem_leaf_plot(v)
if ~isnumeric(v) % check that program will accept array as a integers
error( 'Input V must be numeric');
end
stems=fix(v/10);
leaf=fix(rem(v,10));
[stems, index]=sort(stems,'ascend');
leaf=leaf(index);
[a,b]=hist(stems,unique(stems));
n=length(a);
for ii=1:n
fprintf('leaf of stem %d is ',b(ii));
leaf(1:a(ii))
leaf(1:a(ii))=[];
end
>> A=[20 12 13 21 56 13 16 17 22 23 24];
>> stem_leaf_plot(A)
leaf of stem 1 is
ans =
2 3 3 6 7
leaf of stem 2 is
ans =
0 1 2 3 4
leaf of stem 5 is
ans =
6

Find if 2 vectors have 4 consecutive identical elements

I am trying to compare 2 vectors to discover if they share 4 consecutive values.
For example
w = [6 7 8 9 10 11 12 13 14]
v = [5 6 7 8 9]
Has 4 consecutive values 6 7 8 9
But
x = [6 7 8 9 10 11 12 13 14]
y = [6 7 1 2 3 4 5 6 13 14]
has four identical values (6 7 13 14) but they aren't consecutive.
The code I am currently using is:
if length(intersect(v, w)) >= 4
condition = true;
but this doesn't test for consecutive elements, so it would return true for both cases listed above whereas I want it to only return true for the first case.
Can somebody please help me find a way to test for identical consecutive elements rather than just identical elements.
Building on Marcos' answer:
Create all possible search vectors from your initial search (i.e. [5 6 7 8] [6 7 8 9]) - however we will make it a 3D matrix which will be m-by-1-by-n
n = 4;
m = numel(v)-n+1;
V = permute(v(bsxfun(#plus,(0:m-1)',1:n)),[1,3,2])
Check if any of these sub-vectors are a subset of the vector being searched
check = sum(any(bsxfun(#eq, V, w),3),2) >= n;
match = squeeze(V(check,:,:))' %'// The ' is debatable here, it depends on how many matches you get
you can compare
bsxfun(#eq, w,v')
Resulting with
ans =
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0
As you can see four consecutive matching elements form a diagonal of length 4.
To find the location of this diagonal you can conv2 with a 4 diagonal filter (eye(4)):
[rr cc] = find( conv2( single(bsxfun(#eq, [1 2 3 w],v')), eye(4), 'same' ) == 4 )
compensating for the center of the filter
loc_in_w = cc - 1
loc_in_v = rr - 1
yielding
loc_in_w =
1
loc_in_v =
2
which are the first index of the sequence in w and v respectively.
This method can work with more than one occurrence of a 4-substring of v in w...
I haven't meddled in matlab for ages, but my "general" approach to this in computing terms would be splitting the problem into a needle-and-haystack solution with two parts:
Create all possible search vectors from your initial search (i.e. [5 6 7 8] [6 7 8 9])
Check if any of these sub-vectors are a subset of the vector being searched.
Basically just two set-operations in a row.
You could convert your vectors to strings, and use strfind.
If x and y are your vectors:
x_str = mat2str(x);
y_str = mat2str(y);
n = strfind(x_str(2:end-1), y_str(2:end-1))
Note that you have to remove the first and last characters of the string version, as they correspond to the square brackets of the vectors.

Dividing a vector to form different matrices

I have a two long vector. Vector one contains values of 0,1,2,3,4's, 0 represent no action, 1 represent action 1 and 2 represent the second action and so on. Each action is 720 sample point which means that you could find 720 consecutive twos then 720 consecutive 4s for example. Vector two contains raw data corresponding to each action. I need to create a matrix for each action ( 1, 2, 3 and 4) which contains the corresponding data of the second vector. For example matrix 1 should has all the data (vector 2 data) which occurred at the same indices of action 1. Any Help??
Example on small amount of data:
Vector 1: 0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2
Vector 2: 6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0
Result:
Matrix 1:
5 6 4
0 5 6
Matrix 2:
9 8 7
5 8 0
Here is one approach. I used a cell array to store the output matrices, hard-coding names for such variables isn't a good plan.
V1=[0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2]
V2=[6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0]
%// Find length of sequences of 1's/2's
len=find(diff(V1(find(diff(V1)~=0,1)+1:end))~=0,1)
I=unique(V1(V1>0)); %// This just finds how many matrices to make, 1 and 2 in this case
C=bsxfun(#eq,V1,I.'); %// The i-th row of C contains 1's where there are i's in V1
%// Now pick out the elements of V2 based on C, and store them in cell arrays
Matrix=arrayfun(#(m) reshape(V2(C(m,:)),len,[]).',I,'uni',0);
%// Note, the reshape converts from a vector to a matrix
%// Display results
Matrix{1}
Matrix{2}
Since, there is a regular pattern in the lengths of groups within Vector 1, that could be exploited to vectorize many things while proposing a solution. Here's one such implementation -
%// Form new vectors out of input vectors for non-zero elements in vec1
vec1n = vec1(vec1~=0)
vec2n = vec2(vec1~=0)
%// Find positions of group shifts and length of groups
df1 = diff(vec1n)~=0
grp_change = [true df1]
grplen = find(df1,1)
%// Reshape vec2n, so that we end up with N x grplen sized array
vec2nr = reshape(vec2n,grplen,[]).' %//'
%// ID/tag each group change based on their unique vector 2 values
[R,C] = sort(vec1n(grp_change))
%// Re-arrange rows of reshaped vector2, s.t. same ID rows are grouped succesively
vec2nrs = vec2nr(C,:)
%// Find extents of each group & use those extents to have final cell array output
grp_extent = diff(find([1 diff(R) 1]))
out = mat2cell(vec2nrs,grp_extent,grplen)
Sample run for the given inputs -
>> vec1
vec1 =
0 0 1 1 1 0 0 2 2 2 ...
0 0 1 1 1 0 0 2 2 2
>> vec2
vec2 =
6 7 5 6 4 6 5 9 8 7 ...
9 7 0 5 6 4 1 5 8 0
>> celldisp(out)
out{1} =
5 6 4
0 5 6
out{2} =
9 8 7
5 8 0
Here is another solution:
v1 = [0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2];
v2 = [6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0];
m1 = reshape(v2(v1 == 1), 3, [])'
m2 = reshape(v2(v1 == 2), 3, [])'
EDIT: David's solution is more flexible and probably more efficient.

Matlab Unexpected Result/Error? Replacing zeros in array with Inf

I'm getting an unexpected result from Matlab and I have no idea why. My goal is to replace values less than or equal to zero with Inf.
Here is the correct result of what I expect should happen:
C = [0 0 0 0 0 1 1 1 1 1 1];
C(C<=0)=Inf
C = Inf Inf Inf Inf Inf 1 1 1 1 1 1
But when I begin the process in a different manner, matlab replaces values <=0 with 1 instead of Inf.
A = [0 2 4 6 8 10 12 14 16 18 20];
b = 7;
E=A-b>0
E = 0 0 0 0 1 1 1 1 1 1 1
E(E<=0)=Inf
E = 1 1 1 1 1 1 1 1 1 1 1
Any idea why this is occurring? I'm guessing it has something to do with logical E=A-b>0 step, but I don't know why it is different.
My overall goal is to find the index of the closest larger value of a vector to a scalar, and it did just occur to me that I could skip the step that I think is causing the problem and get the desired outcome, like so (with A & b already defined as above):
F=A-b
F = -7 -5 -3 -1 1 3 5 7 9 11 13
F(F<=0)=Inf
F = Inf Inf Inf Inf 1 3 5 7 9 11 13
[~,ind]=min(F)
ind = 5
BUT, I still don't understand why Matlab was not giving the result I expected above (and I already finished typing the question before I realized the simple solution). So does anyone know why Matlab gives the unexpected result?
Thanks
This is because E is a logical array. Possible values are 1 or 0. If you use E=double(A-b>0) you will get the expected results.

Extracting unique values

I have data in two columns that looks as follows:
A B
1,265848208 3
-0,608043611 0
-0,285735893 0
0,006895134 7
0 7
-0,004526196 7
0,176326617 10
-0,159688071 2
0,22439945 2
-0,991045044 1
0,178022324 1
-0,270967397 4
0,285849994 4
1,881705539 23
1,057184204 10
NaN 10
For all unique values in B I want to extract the corresponding value in column A and move it to a new matrix. I'm looking to then compute the mean of all the corresponding values in A and use as a dependent variable (weighted by no of observations per value in B) in a regression with the common value of B being the independent variable to reduce noise. Any help would on how to do this in Matlab (except running the regression) would be great!
Thanks
Oscar
Here is an efficient solution:
X = [
1.265848208 3
-0.608043611 0
-0.285735893 0
0.006895134 7
0 7
-0.004526196 7
0.176326617 10
-0.159688071 2
0.22439945 2
-0.991045044 1
0.178022324 1
-0.270967397 4
0.285849994 4
1.881705539 23
1.057184204 10
NaN 10
];
%# unique values in B, and their indices
[valB,~,subs] = unique(X(:,2));
%# values of A for each unique number in B (cellarray)
valA = accumarray(subs, X(:,1), [], #(x) {x});
%# mean of each group
meanValA = cellfun(#nanmean, valA)
%# perform regression here...
The result:
%# B values, mean of corresponding values in A, number of A values
>> [valB meanValA cellfun(#numel,valA)]
ans =
0 -0.44689 2
1 -0.40651 2
2 0.032356 2
3 1.2658 1
4 0.0074413 2
7 0.00078965 3
10 0.61676 3
23 1.8817 1