Double interpolation in Matlab - matlab

I want to ask you somehting about interpolation in Matlab. I want to know if it's possible to do two different interpolations in the same line. I mean, for example, at the beginning do a linear interpolation and at the middle, approximately, do another kind of interpolation, like spline.
The main problem is that I've done a linear interpolation and it's perfect at the beginning but after some point, I think it would be better another type. If this is possible, how can I code where I want to change it? I've tried to check the documentation about Matlab and I couldn't find anything about modifying interpolations.
Thanks a lot in advance and greetings,

Allow me to elaborate on the comment I made on your post.
If you want to create an output array from an input array using 2 different functions with a split, you can use array index ranges like in the following code example
x = randn(20,1); %//your input data - 20 random numbers for demonstration
threshold = 5; %//index where you want the change of algorithm
y = zeros(size(x)); %//output array of zeros the same size as input
y(1:threshold) = fun1(x(1:threshold));
y(1+threshold:end) = fun2(x(1+threshold:end));
You could skip the preallocation of y if you like and just concatenate the additional data on to the end of the output. This is particularly useful if a function returns a different number of output elements relative to the number of input elements. The syntax for this is shown below.
y = fun1(x(1:threshold));
y = [y; fun2(x(1+threshold:end))];
EDIT:
In response to your post below, here is a full example . . .
clc; close all
x = -5:5; %//your x-range
y = [1 1 0 -1 -1 0 1 1 1 1 1]; %//the function to interpolate
t = -5:.01:5; %//sampling interval for output
xIdx = 5; %//the index on the x-axis where you want the split to occur
tIdx = floor(numel(t)/numel(x)*xIdx);%//need to calculate as it is at a different sample rate
out = pchip(x(1:xIdx),y(1:xIdx),t(1:tIdx));
out = [out spline(x((1+xIdx):end),y((1+xIdx):end),t((1+tIdx):end))];
%//PLOTTING
plot(x,y,'o',t,out,'-',[x(xIdx) x(xIdx)], [-1.5 1.5], '-')
legend('data','output','split',4);
ylim ([-1.5 1.5])
Which will give . . .

Related

Zero crossings around mean

I am working on developing a suite classifiers for EEG signals and I will be needing a zero-crossings around mean function, defined in the following manner:
Ideally if I have some vector with a range of values representing a sinusoid or any time varying signal, I will want to return a vector of Booleans of the same size as the vector saying if that particular value is a mean crossing. I have the following Matlab implementation:
ZX = #(x) sum(((x - mean(x)>0) & (x - mean(x)<0)) | ((x - mean(x)<0) & (x - mean(x)>0)));
Testing it on toy data:
[0 4 -6 9 -20 -5]
Yields:
0
EDIT:
Yet I believe it should return:
3
What am I missing here?
An expression like:
((x-m)>0) & ((x-m)<0)
is always going to return a vector of all zeros because no individual element of x is both greater and less than zero. You need to take into account the subscripts on the xs in the definition of ZX:
((x(1:end-1)-m)>0) & ((x(2:end)-m)<0)
You can use the findpeaks function on -abs(x), where x is your original data, to find the peak locations. This would give you the zero crossings in general for continuous signals which do not have zero as an actual maximum of the signal.
t = 0:0.01:10;
x = sin(pi*t);
plot(t,x)
grid
y = -abs(x);
[P,L] = findpeaks(y,t);
hold on
plot(L,P,'*')
A simple solution is to use movprod, and count the products which are negative, i.e.,
cnt = sum(sign(movprod(x-mean(x),2))<0);
With your toy example, you will get cnt = 3.

In matlab is it possible to vectorize in more than one dimension?

The matlab documentation explains vectorization with
i = 0;
for t = 0:.01:10
i = i + 1;
y(i) = sin(t);
end
This is a vectorized version of the same code:
t = 0:.01:10;
y = sin(t);
Can I apply vectorization to a function with non-scalar inputs and outputs? As a toy example, take the function foo
function out = foo(in)
out(1) = in(1);
out(2) = in(2);
end
It's just the identity. I'd like to do something like
inputs = [1,2; 3,4; 5,6];
t = 1:3;
outputs = foo(inputs(t,:))
and get
ans =
1 2
3 4
5 6
Instead I end up with
ans =
1 3
Clearly, I'm either doing it wrong or it's not possible. Help!
It is possible to vectorize in Matlab in N-dimensions. As it was mentioned by Andrew you can simply writte a function like:
function out = foo(in)
out(:,1:size(in,2)) = in(:,1:size(in,2));
end
Output will be as desired.
Using the (:) index operator on n-dimensional arrays allows you to perform vectorized operations on all elements. You can use e.g. reshape to put the result back into the same form as the input.
function out=foo(in)
out=reshape(in(:),size(in));
The unexpected output
The reason your output is [1 3] has to do with linear indexing of matrixes. In particular this means that you can access the full matrix with a vector, where you first count through the first column, then the second column and so on. For a 3x2 matrix this would be:
1 4
2 5
3 6
Therefore in(1) = 1 is the first element in the first column. in(2) = 3 is the second element in the first column. Find the full documentation for matrix indexing here.
The input
Secondly writing outputs = foo(inputs(t,:)) means that you take all rows specified in t with no condition to the columns. Therefore in your example it is equivalent to write outputs = foo(inputs) and outputs = foo(inputs(t,:)) as you put in all 3 rows. What you could do is let foo() have two arguments.
function out = foo(t,in)
out = in(t,:)
To have access to the rows inside the function you could write something like:
function out = foo(in);
[x,y] = size(in);
for t = 1:x
out(t,:) = in(t,:);
end
Normally vectorization is used to avoid looping through scalars. There are a lot of clever ways to simplify code and reduce it's computation time. Most techniques like .* ,.^, bsxfun() are already presented in the documentation about vectorization you already found. The tricky part is to find the right way to apply these methods. It takes a lot of experience and a sharp eye to fully utilize them, as in every case you need to adjust to the specific logic of your opperations.
Feel free to ask if you still struggle with your problem.

determine the frequency of a number if a simulation

I have the following function:
I have to generate 2000 random numbers from this function and then make a histogram.
then I have to determine how many of them is greater that 2 with P(X>2).
this is my function:
%function [ output_args ] = Weibullverdeling( X )
%UNTITLED Summary of this function goes here
% Detailed explanation goes here
for i=1:2000
% x= rand*1000;
%x=ceil(x);
x=i;
Y(i) = 3*(log(x))^(6/5);
X(i)=x;
end
plot(X,Y)
and it gives me the following image:
how can I possibly make it to tell me how many values Do i Have more than 2?
Very simple:
>> Y_greater_than_2 = Y(Y>2);
>> size(Y_greater_than_2)
ans =
1 1998
So that's 1998 values out of 2000 that are greater than 2.
EDIT
If you want to find the values between two other values, say between 1 and 4, you need to do something like:
>> Y_between = Y(Y>=1 & Y<=4);
>> size(Y_between)
ans =
1 2
This is what I think:
for i=1:2000
x=rand(1);
Y(i) = 3*(log(x))^(6/5);
X(i)=x;
end
plot(X,Y)
U is a uniform random variable from which you can get the X. So you need to use rand function in MATLAB.
After which you implement:
size(Y(Y>2),2);
You can implement the code directly (here k is your root, n is number of data points, y is the highest number of distribution, x is smallest number of distribution and lambda the lambda in your equation):
X=(log(x+rand(1,n).*(y-x)).*lambda).^(1/k);
result=numel(X(X>2));
Lets split it and explain it detailed:
You want the k-th root of a number:
number.^(1/k)
you want the natural logarithmic of a number:
log(number)
you want to multiply sth.:
numberA.*numberB
you want to get lets say 1000 random numbers between x and y:
(x+rand(1,1000).*(y-x))
you want to combine all of that:
x= lower_bound;
y= upper_bound;
n= No_Of_data;
lambda=wavelength; %my guess
k= No_of_the_root;
X=(log(x+rand(1,n).*(y-x)).*lambda).^(1/k);
So you just have to insert your x,y,n,lambda and k
and then check
bigger_2 = X(X>2);
which would return only the values bigger than 2 and if you want the number of elements bigger than 2
No_bigger_2=numel(bigger_2);
I'm going to go with the assumption that what you've presented is supposed to be a random variate generation algorithm based on inversion, and that you want real-valued (not complex) solutions so you've omitted a negative sign on the logarithm. If those assumptions are correct, there's no need to simulate to get your answer.
Under the stated assumptions, your formula is the inverse of the complementary cumulative distribution function (CCDF). It's complementary because smaller values of U give larger values of X, and vice-versa. Solve the (corrected) formula for U. Using the values from your Matlab implementation:
X = 3 * (-log(U))^(6/5)
X / 3 = (-log(U))^(6/5)
-log(U) = (X / 3)^(5/6)
U = exp(-((X / 3)^(5/6)))
Since this is the CCDF, plugging in a value for X gives the probability (or proportion) of outcomes greater than X. Solving for X=2 yields 0.49, i.e., 49% of your outcomes should be greater than 2.
Make suitable adjustments if lambda is inside the radical, but the algebra leading to solution is similar. Unless I messed up my arithmetic, the proportion would then be 55.22%.
If you still are required to simulate this, knowing the analytical answer should help you confirm the correctness of your simulation.

frequency of each vector value in another vector matlab

I need to calculate the frequency of each value in another vector in MATLAB.
I can use something like
for i=1:length(pdata)
gt(i)=length(find(pf_test(:,1)==pdata(i,1)));
end
But I prefer not to use loop because my dataset is quite large. Is there anything like histc (which is used to find the frequency of values in one vector) to find the frequency of one vector value in another vector?
If your values are only integers, you could do the following:
range = min(pf_test):max(pf_test);
count = histc(pf_test,range);
gt = count(ismember(range,a));
gt(~ismember(unique(a),b)) = 0;
If you can't guarantee that the values are integers, it's a bit more complicated. One possible method of it would be the following:
%restrict yourself to values that appear in the second vector
filter = ismember(pf_test,pdata);
% sort your first vector (ignore this if it is already sorted)
spf_test = sort(pf_test);
% Find the first and last occurrence of each element
[~,last] = unique(spf_test(filter));
[~,first] = unique(spf_test(filter),'first');
% Initialise gt
gt = zeros(length(pf_test));
% Fill gt
gt(filter) = (last-first)+1;
EDIT: Note that I may have got the vectors the wrong way around - if this doesn't work as expected, switch pf_test and pdata. It wasn't immediately clear to me which was which.
You mention histc. Why are you not using it (in its version with two input parameters)?
>> pdata = [1 1 3 2 3 1 4 4 5];
>> pf_test = 1:6;
>> histc(pdata,pf_test)
ans =
3 1 2 2 1 0

Matlab Fill previous value if missing value (or zero)

I have a vector containing a time series with different values and some missing values inbetween that are set to zero:
X=[0,0,2,0,5,0,0,0,4,0];
I want to create a new vector where the missing values (zeros) are populated by the previous value if one exist so that I get a new vector looking like:
Z=[0,0,2,2,5,5,5,5,4,4];
I have been browsing through the Matlab help and forums like this to find a neat and suitable function that would solve this for me with a one line solution or similar, but I have failed to do so. I can solve the problem through a few different steps according to below but I am guessing that there must be a better and easier solution available?
Current solution:
X=[0,0,2,0,5,0,0,0,4,0];
ix=logical(X);
Y = X(ix);
ixc=cumsum(ix);
Z=[zeros(1,sum(~logical(ixc))) Y(ixc(logical(ixc)))];
This does the trick, but it seems like an overly complicated solution to a simple problem, so can anyone help me with a better one? Thanks.
Here's a somewhat simpler version using cumsum:
X=[0,0,2,0,5,0,0,0,4,0];
%# find the entries where X is different from zero
id = find(X);
%# If we want to run cumsum on X directly, we'd
%# have the problem that the non-zero entry to the left
%# be added to subsequent non-zero entries. Thus,
%# subtract the non-zero entries from their neighbor
%# to the right
X(id(2:end)) = X(id(2:end)) - X(id(1:end-1));
%# run cumsum to fill in values from the left
Y = cumsum(X)
Y =
0 0 2 2 5 5 5 5 4 4
Here's a little something I wrote up. Does this do the trick?
% INPUT: the array you would like to populate
% OUTPUT: the populated array
function popArray = populate(array)
popArray = array;
% Loops through all the array elements and if it equals zero, replaces it
% with the previous element
%
% Since there is no element before the first to potentially populate it, this
% starts with the second element.
for ii = 2:length(popArray)
if array(ii) == 0;
popArray(ii)= popArray(ii-1);
end
end
disp(popArray);
Let me suggest another vectorized solution (though I like the one by #Jonas better):
X = [0 0 2 0 5 0 0 0 4 0]
id = find(X);
X(id(1):end) = cell2mat( arrayfun(#(a,b)a(ones(1,b)), ...
X(id), [diff(id) numel(X)-id(end)+1], 'UniformOutput',false) )