MATLAB - How to plot a function using an array with negative indices? - matlab

I'm trying to figure out this bootleg programming language but keep getting stumped on things like this.
My code is as follows:
clc;
clear;
for i = -3:6;
x(i) = i;
y(i) = (i^4)-(4*(i^3))-(6*(i^2))+15; %being my given function
end
plot(x,y)
It works if I start from 1 because it's a positive integer. It can't access zero nor negative values. How do I go around this?
edit: thanks for the swift response you guys, I like your methods and definitely wanted to approach it different ways but one of the requirements in my text is to use the for loop, sadly

Since you can't access array elements with negative indices, you'll need to use a different variable than i to keep track of each element in x and y; this new variable should start at 1 and increment with every loop iteration.
But you don't even need to worry about managing that; you can simply assign -3:6 to x and compute your function using x as an array:
clc;
clear;
x = -3:6;
y = (x.^4)-(4*(x.^3))-(6*(x.^2))+15;
plot(x,y)
However, this will produce a graph that looks a bit jagged. If you want x to contain more points, you can use linspace() instead:
clc;
clear;
x = linspace(-3, 6); % (similar to -3:0.09:6)
y = (x.^4)-(4*(x.^3))-(6*(x.^2))+15;
plot(x,y)

You can do this even without a for loop.
x = -3:6;
y = (x.^4)-(4*(x.^3))-(6*(x.^2))+15;
Matlab is way more effective if it's used without loops. For your case with this small range it will have no effect but if you go for way more elements you increase the speed of your code using this approach.
To answer you original question. The problem is that you are using the index based vector access. And the first element in Matlab vector is defined with index 1.
For your edit and the requirement of using the for loop you can use this approach
x = -3:6;
y = zeros(1, length(x));
% initialization prevents the vector size being changed in every iteration
for i = 1:length(x)
y = (x(i)^4)-(4*(x(i)^3))-(6*(x(i)^2))+15;
end

Related

Rewrite medfilt1 MATLAB function to support codegen

I am writing a MATLAB script that uses the medfilt1 function. Here is an example using an order of 100:
median_filter_results = medfilt1(my_data, 100);
When trying to export the MATLAB code via codegen, an error message states that medfilt1 is not supported. Looking on the MATLAB documentation website, I can tell that it is not there, while medfilt2 is. This makes me think that the function is probably rather easy to reproduce.
When reading this post, the authors make this comment:
You can use the median() function. Then you just have to put that inside a for loop, which is extremely trivial.
However, I am not entirely sure I know what that means since the median function returns back one number vs a vector of the medfilt1 function. Wikipedia goes a bit further, where they show a sliding window, through which one could use the median function. However, I am not entirelly too certain that this is what MATLAB is doing.
How can I rewrite the medfilt1 function (vector of data and order of 100) in a codegen safe way?
Here is an implementation using sliding window of median in a for loop:
Implementing a sliding window is simple.
There is a small complication regarding the margins.
The implementation pads the margins with zeros (default padding of medfilt1).
Here is the implementation and a test:
n = 100;
%Test using an array of random elements.
A = rand(1, 1000);
B = my_medfilt1(A, n);
%Reference for testing
refB = medfilt1(A, n);
%Display 1 if result of my_medfilt1 is the same as medfilt1
is_equal = all(B == refB)
function y = my_medfilt1(x, n)
%Perform one dimensional median filter in a loop.
%Assume x is one dimensional row vector.
if size(x, 1) > 1
error('x must be a row vector')
end
y = zeros(1, length(x)); %Initialize space for storing resut
%Add n/2 zeros from each side of x (this is the default padding of medfilt1.
x = padarray(x, [0, floor(n/2)], 0, 'both');
%Sliding window
for i = 1:length(y)
y(i) = median(x(i:i+n-1));
end
end
If the 2d filter is supported, you could repurpose it.
x=rand(100,1);
y1=medfilt1(x,11);
y2=medfilt2(x,[11,1]);
all(y1==y2)
Otherwise, read up what a median filter does. It replaces the element with the median of it and it's surrounding neighbors. Size of the neighborhood is your parameter n.

Finding roots in data

I have data that look like this:
These are curves of the same process but with different parameters.
I need to find the index (or x value) for certain y values (say, 10).
For the blue curve, this is easy: I'm using min to find the index:
[~, idx] = min(abs(y - target));
where y denotes the data and target the wanted value.
This approach works fine since I know that there is an intersection, and only one.
Now what to do with the red curve? I don't know beforehand, if there will be two intersections, so my idea of finding the first one and then stripping some of the data is not feasible.
How can I solve this?
Please note the the curves can shift in the x direction, so that checking the found solution for its xrange is not really an option (it could work for the data I have, but since there are more to come, this solution is probably not the best).
Shameless steal from here:
function x0 = data_zeros(x,y)
% Indices of Approximate Zero-Crossings
% (you can also use your own 'find' method here, although it has
% this pesky difference of 1-missing-element because of diff...)
dy = find(y(:).*circshift(y(:), [-1 0]) <= 0);
% Do linear interpolation of near-zero-crossings
x0 = NaN(size(dy,1)-1,1);
for k1 = 1:size(dy,1)-1
b = [[1;1] [x(dy(k1)); x(dy(k1)+1)]] \ ...
[y(dy(k1)); y(dy(k1)+1)];
x0(k1) = -b(1)/b(2);
end
end
Usage:
% Some data
x = linspace(0, 2*pi, 1e2);
y = sin(x);
% Find zeros
xz = data_zeros1(x,y);
% Plot original data and zeros found
figure(1), hold on
plot(x, y);
plot(xz, zeros(size(xz)), '+r');
axis([0,2*pi -1,+1]);
The gist: multiply all data points with their consecutive data points. Any of these products that is negative therefore has opposite sign, and gives you an approximate location of the zero. Then use linear interpolation between the same two points to get a more precise answer, and store that.
NOTE: for zeros exactly at the endpoints, this approach will not work. Therefore, it may be necessary to check those manually.
Subtract the desired number from your curve, i.e. if you want the values at 10 do data-10, then use and equality-within-tolerance, something like
TOL = 1e-4;
IDX = 1:numel(data(:,1)); % Assuming you have column data
IDX = IDX(abs(data-10)<=TOL);
where logical indexing has been used.
I figured out a way: The answer by b3 in this question did the trick.
idx = find(diff(y > target));
Easy as can be :) The exact xvalue can then be found by interpolation. For me, this is fine since i don't need exact values.

Using sum function instead of for loop to work with vectors in Matlab

I'm writing a Matlab function that multiplies the ith element of a vector square by the ith value. I can get the function to work but I would like to not use a for loop. I'd like to use the sum matlab function without a for loop.
%x is the vector
x = [3; 3; 3; 1; 1];
%value = sum(.x^2); I tried this but this wouldn't work as I can't figure out how to get the ith value.
sumvalue = 0;
for i=1:length(x)
fprintf('The j is %d, the value is %d.\n',i, x(i));
sumvalue = sumvalue + (i * x(i)^2);
fprintf('The sumvalue is %d.\n',sumvalue);
end
I've tried a few other things but I can't seem to find or figure out how to get the ith value without using a for loop. I was thinking about using the dot notation on the vector (.x) but I'm not real sure how to use that and then I'm back to the issue of not having the ith value. I'm not new to programming but I'm new to using Matlab. Any help is greatly appreciated.
I've tried the code below.
value = sum((1:numel(x)).*x.^2);
but I get the following error, "Error using .* Matrix dimensions must agree.". I've added values to x to show the simple values I'm using. Thanks again for the help.
To compute the total sum:
sum((1:numel(x)).'.*x(:).^2)
Note that the vector 1:numel(x) takes the place of your i but in vectorized form.
If you want all the partial sums:
cumsum((1:numel(x)).'.*x(:).^2)

evaluating a self made function for a cector and then plotting in matlab

i have created a function that represents a triangle sign.
this function does not work on vectors. i want to evaluate a vector x:
x=[-2:0.01:2]
and save the answer in vector y, for this purpose i came up with the following code:
for i=1:400, y(i) = triangle(x(i))
after i got the ans i plotted is using plot. in this case it worked ok but i am interested on observing the influence of time shifting and shrinking so when i try to use lets say:
for i=1:200, y(i) = triangle(x(2*i))
i get a vector y not the same length as vector x and i cant even plot them... is there any easy way to achieve it? and how should i plot the answer?
here is my function:
function [ out1 ] = triangle( input1 )
if abs(input1) < 1,
out1 = 1 - abs(input1);
else
out1 = 0;
end
end
y is a different length in each for loop because each loop iterated a different number of times. In the example below, I use the same for loops and plot y2 with the corresponding values of x. i is already defined in matlab so I've changed it to t in the example below.
clear all
x=[-2:0.01:2];
for t=1:400
y(t) = triangle(x(t));
end
for t=1:200
y2(t) = triangle(x(2*t));
end
Or, if you want to see y2 plotted over the same range you can increase the size of x:
clear all
x=[-2:0.01:8];
for t=1:400
y(t) = triangle(x(t));
end
for t=1:400
y2(t) = triangle(x(2*t));
end
plot(x(1:length(y)),y,'r')
hold on
plot(x(1:length(y2)),y2,'b')

Removing Similar Elements in Matrix

I'm trying to figure out how to remove an element of a matrix in MATLAB if it differs from any of the other elements by 0.01. I'm supposed to be using all of the unique elements of the matrix as thresholding values for a ROC curve that I'm creating but I need a way to remove values when they are within 0.01 of each other (since we are assuming they are basically equal if this is true).
And help would be greatly appreciated!
Thanks!
If you are simply trying to remove adjacent values within that tolerance from a vector, I would start with something like this:
roc = ...
tolerance = 0.1;
idx = [logical(1) diff(roc)>tolerance)];
rocReduced = roc(idx);
'rocReduced' is now a vector with all values that didn't have an adjacent values within a tolerance in the original vector.
This approach has two distinct limitations:
The original 'roc' vector must be monotonic.
No more than two items in a row may be within the tolerance, otherwise the entire swath will be removed.
I suspect the above would not be sufficient. That said, I can't think of any simple operations that overcome those (and other) limitations while still using vectorized matrix operations.
If performance is not a huge issue, you maybe the following iterative algorithm would suit your application:
roc = ...
tolerance = 0.1;
mask = true(size(roc)); % Start with all points
last = 1; % Always taking first point
for i=2:length(roc) % for all remaining points,
if(abs(roc(i)-roc(last))<tolerance) % If this point is within the tolerance of the last accepted point, remove it from the mask;
mask(i) = false;
else % Otherwise, keep it and mark the last kept
last = i;
end
end
rocReduced = roc(mask);
This handles multiple consecutive sub-tolerance intervals without necessarily throwing all away. It also handles non-monotonic sequences.
MATLAB users sometimes shy away from iterative solutions (vs. vectorized matrix operations), but sometimes it's not worth the trouble of finding a more elegant solution when brute force performance meets your needs.
Let all the elements in your matrix form a graph G = (V,E) such that an there is an edge between two vertices (u,v) if the difference between them is less than 0.01. Now, construct an adjacency matrix for this graph and find the element with the largest degree. Remove it and add it to a list and remove it's neighbors from your graph and repeat until there aren't any elements left.
CODE:
%% Toy dataset
M = [1 1.005 2 ;2.005 2.009 3; 3.01 3.001 3.005];
M = M(:);
A = false(numel(M),numel(M));
for i=1:numel(M)
ind = abs(M-M(i))<=0.01;
A(i,ind) = 1;
end
C = [];
while any(A(:))
[val ind] = max(sum(A));
C(end+1) = M(ind);
A(A(ind,:),:) = 0;
end
This has a runtime of O(n^2) where your matrix has n elements. Yeah it's slow.
From your description, it's not very clear how you want to handle a chain of values (as pointed out in the comments already), e.g. 0.0 0.05 0.1 0.15 ... and what you actually mean by removing the elements from the matrix: set them to zero, remove the entire column, remove the entire line?
For a vector, it could look like (similar to Adams solution)
roc = ...
tolerance = 0.1;
% sort it first to get the similar values in a row
[rocSorted, sortIdx] = sort(roc);
% find the differing values and get their indices
idx = [logical(1); diff(rocSorted)>tolerance)];
sortIdxReduced = sortIdx(idx);
% select only the relevant parts from the original vector (revert sorting)
rocReduced = roc(sort(sortIdxReduced));
The code is untested, but should work hopefully.
Before you use a threshold or tolerance to keep values that all close enough, you can use matlab inbuilt unique() to reduce the run. Usually, matlab tries to accelerate their inbuilts, so try to use as many inbuilts as possible.