lookup table in matlab - matlab

I am trying to implement a kind of lookup table in MATLAB.
I have data generated from a script with three variables swept, let's say var_a, var_b, var_c. These are nested sweep, (var_a -> var_b -> var_c)
And there are 10 outputs, out_01, out02, ..., out10.
Now I have arranged the each output as out_01 = f(var_a,var_b,var_c), i.e., simply rearranging the data similar to nested loop.
My question is, how can I build a lookup table for such data?
I will give input like get out_01 # certain var_a(X), var_b(Y), var_c(Z).
I have tried the following.
idx1_var_a = max(find(data.var_a <= options.var_a));
idx2_var_a = min(find(data.var_a >= options.var_a));
idx1_var_b = max(find(data.var_b <= options.var_b));
idx2_var_b = min(find(data.var_b >= options.var_b));
idx1_var_c = max(find(data.var_c <= options.var_c));
idx2_var_c = min(find(data.var_c >= options.var_c));
Y1 = interpn(data.var_c,data.var_b,data.var_a,data.out_01,data.var_c(idx1_var_c),data.var_b(idx1_var_b),data.var_a(idx1_var_a))
Y2 = interpn(data.var_c,data.var_b,data.var_a,data.out_01,data.var_c(idx2_var_c),data.var_b(idx2_var_b),data.var_a(idx2_var_a))
if Y1 == Y2
Y = Y1
else
Here I am unable to figure how to interpolate between these two output values,Y1, and Y2!!
end
Any help is welcome.

I think you are looking for this:
Suppose you have:
var_a = 1:3;
var_b = 0:0.3:0.9;
var_c = 1:2;
[A, B, C] = ndgrid(var_a, var_b, var_c)
F = A.^3+B.^2+C;
Now you can directly acces the function at all existing points:
F(1,2,2)
Or alternatively
F(var_a==1,var_b==0.3,var_c==2)
Now if you are interested in values between the gridpoints, you can use interp3
Vq = interp3(F,1.5,2.5,1.5)
Note that this takes the desired location in the vector as input.

Related

How to write functions as matrix elements inside MATLAB matrices?

I am interested in writing some matrix elements as functions which can take value as per my convenience and then the required matrix operations can be applied on top of that. More precisely, I am trying to integrate over x, the trace of a matrix which has matrix elements as functions of x (which are unknown analytically as they come through products of matrices dependent on x) .
When I try to write the matrix elements as functions, then I obviously get the error- Conversion to double from function_handle is not possible. Is there an easy way to write the matrix elements as functions ?
Thanks. Please ask if my question is not clear.
For example, its something like this:-
N_k = 10;
M_sigma = cell(N_k,1);
M_rho = cell(N_k,1);
for ii = 1:N_k
M_sigma {ii}(1,2) = #(sigma) sigma; %this kind of thing is not allowed in matlab
M_sigma {ii}(2,1) = #(sigma) -conj(sigma);
M_sigma {ii}(1,1) = 0;
M_sigma {ii}(2,2) = 0;
end
for ii = 1:N_k
M_rho {ii}(1,2) = #(rho) rho;
M_rho {ii}(2,1) = #(rho) -conj(rho);
M_rho {ii}(1,1) = 0;
M_rho {ii}(2,2) = 0;
end
M_tau = cell(N_k,1);
for ii = 1:N_k
M_tau {ii} = exp(M_sigma{ii})*exp(M_rho{ii});
end
% the following statement is wrong but I want to do something like
%:-write M_tau as a function of sigma and sum(integrate) the trace of M_tau for all values of sigma
integral(#(sigma) M_tau{1}(sigma), {0,1})
I don't think that you would be able to accomplish that with function handles. I think the best way you could do it would be to define a function as follows :
function x = myFunction(rowindex,colindex)
x = x * rowindex + colindex;
end
You would replace this function with your algorithm and then you could iterate over it doing the following:
for a=1:10
for b=1:10
x(a,b)=myFunction(a,b);
end
end

how to vectorize array reformatting?

I have a .csv file with data on each line in the format (x,y,z,t,f), where f is the value of some function at location (x,y,z) at time t. So each new line in the .csv gives a new set of coordinates (x,y,z,t), with accompanying value f. The .csv is not sorted.
I want to use imagesc to create a video of this data in the xy-plane, as time progresses. The way I've done this is by reformatting M into something more easily usable by imagesc. I'm doing three nested loops, roughly like this
M = csvread('file.csv');
uniqueX = unique(M(:,1));
uniqueY = unique(M(:,2));
uniqueT = unique(M(:,4));
M_reformatted = zeros(length(uniqueX), length(uniqueY), length(uniqueT));
for i = 1:length(uniqueX)
for j = 1:length(uniqueY)
for k = 1:length(uniqueT)
M_reformatted(i,j,k) = M( ...
M(:,1)==uniqueX(i) & ...
M(:,2)==uniqueY(j) & ...
M(:,4)==uniqueT(k), ...
5 ...
);
end
end
end
once I have M_reformatted, I can loop through timesteps k and use imagesc on M_reformatted(:,:,k). But doing the above nested loops is very slow. Is it possible to vectorize the above? If so, an outline of the approach would be very helpful.
edit: as noted in answers/comments below, I made a mistake in that there are several possible z-values, which I haven't taken into account. If only a single z-value, the above would be ok.
This vectorized solution allows for negative values of x and y and is many times faster than the non-vectorized solution (close to 20x times for the test case at the bottom).
The idea is to sort the x, y, and t values in lexicographical order using sortrows and then using reshape to build the time slices of M_reformatted.
The code:
idx = find(M(:,3)==0); %// find rows where z==0
M2 = M(idx,:); %// M2 has only the rows where z==0
M2(:,3) = []; %// delete z coordinate in M2
M2(:,[1 2 3]) = M2(:,[3 1 2]); %// change from (x,y,t,f) to (t,x,y,f)
M2 = sortrows(M2); %// sort rows by t, then x, then y
numT = numel(unique(M2(:,1))); %// number of unique t values
numX = numel(unique(M2(:,2))); %// number of unique x values
numY = numel(unique(M2(:,3))); %// number of unique y values
%// fill the time slice matrix with data
M_reformatted = reshape(M2(:,4), numY, numX, numT);
Note: I am assuming y refers to the columns of the image and x refers to the rows. If you want these flipped, use M_reformatted = permute(M_reformatted,[2 1 3]) at the end of the code.
The test case I used for M (to compare the result to other solutions) has a NxNxN space with T times slices:
N = 10;
T = 10;
[x,y,z] = meshgrid(-N:N,-N:N,-N:N);
numPoints = numel(x);
x=x(:); y=y(:); z=z(:);
s = repmat([x,y,z],T,1);
t = repmat(1:T,numPoints,1);
M = [s, t(:), rand(numPoints*T,1)];
M = M( randperm(size(M,1)), : );
I don't think you need to vectorize. I think you change your algorithm.
You only need one loop to step through the lines of the CSV file. For every line, you have (x,y,z,t,f) so just store it in M_reformatted where it belongs. Something like this:
M_reformatted = zeros(max(M(:,1)), max(M(:,2)), max(M(:,4)));
for line = 1:size(M,2)
z = M(line, 3);
if z ~= 0, continue; end;
x = M(line, 1);
y = M(line, 2);
t = M(line, 4);
f = M(line, 5);
M_reformatted(x, y, t) = f;
end
Also note that pre-allocating M_reformatted is a very good idea, but your code may have been getting the size wrong (depending on the data). I think using max like I did will always do the right thing.

How to read a table in matlab, find the y values corresponding to x

I want to use the y value corresponding to the given x value from the table (my current table has 1000 values with 10-4 decimal points so I use :
load question_table.mat
eta_p = %assign a value
F12_p=find( (eta <eta_p+0.01) & (eta > eta_p-0.01), 1, 'first' )
what is missing ?
Here is how I have created the table, run this program.
i = 1;
etaspan = -500:0.001:500;
y = zeros(length(etaspan),1);
f = #(x,eta) (x.^(1/2))./(1+exp(x-eta));
for eta = etaspan
g = #(x) f(x,eta);
y(i) = integral(g,0,500);
i = i + 1;
end
f=y
eta=etaspan
save question_table.mat eta f
Just have MATLAB do the interpolation for you:
y_p = interp1(eta, y, eta_p);
interp1 uses linear interpolation by default, but can instead use higher order interpolation methods. Even with linear, your table seems much denser than necessary.

Matlab: How to assign values selectively to a matrix

I have 3 matrices x, y and z of order 3*3. I want to create a new matrix k with value = 1./(x.^2+y.^2+z.^2) if (x.^2+y.^2+z.^2 > 1) and value = 0 otherwise.
I am trying to use this :
k(x.^2+y.^2+z.^2>1)= 1./(x.^2+y.^2+z.^2)
but it gives error : In an assignment A(I) = B, the number of elements in B and I must be the same.
Can anyone provide a simple solution in a single line where I don't need to use for loops
I am not sure why you'd want to do this as opposed to splitting it up into two operations. This way, you save the cost of computing the sum of squares twice.
x = rand(3,3);
y = rand(3,3);
z = rand(3,3);
k = 1./(x.^2+y.^2+z.^2);
k(k>1)=0;
In any case, another way to do it would be using principles of Functional Programming:
x = rand(3,3);
y = rand(3,3);
z = rand(3,3);
myfun = #(x,y,z) 1/(x^2+y^2+z^2) * (x^2+y^2+z^2>1);
k = arrayfun(myfun, x, y, z);
Alternately, you can mix everything into one line as:
k = arrayfun(#(x,y,z) 1/(x^2+y^2+z^2) * (x^2+y^2+z^2>1), x, y, z);
What this code does is maps the function myfun to each of the data elements. The function myfun is quite simple. It computes the required quantity but multiplies it with the binding condition. However, you might want to beware.
EDIT: To address the comment.
If you don't want to compute the quantity at all, we can use conditional anonymous functions. For more details, you can refer to this guide.
iif = #(varargin) varargin{2 * find([varargin{1:2:end}], 1, 'first')}();
myfun = #(x,y,z) iif( x^2+y^2+z^2 <= 1, #() 0, x^2+y^2+z^2>1 ,#() 1/(x^2+y^2+z^2));
k = arrayfun(myfun, x, y, z);
How about
k = x.^2+y.^2+z.^2;
k(k < 1) = 0;
k(k~= 0) = 1 ./ k(k~=0);
If you are trying to save some processing time (i.e. do not compute at all the sum of squares for those cases when it is less than one) then pretty much the only solution is a table lookup
Otherwise the following code should work
k=1./(x.^2+y.^2+z.^2)
k(k<=1)=0
you can cut some time (assuming x, y and z could be greater than 1)
idx0=x<1 & y<1 & z<1
k=zeros(3)
k(idx0)=1./(x(idx0).^2+y(idx0).^2+z(idx0)^2)
k(k<=1)=0
Your original solution will work if you change it to use an indexer (I haven't profiled it, but I am pretty sure it will take longer, than mine :) )
idx0=x.^2+y.^2+z.^2>1
k=zeros(3)
k(idx0)=1./(x(idx0).^2+y(idx0).^2+z(idx0)^2)

Optimizing a kind-of-correlation computation

I have some code that currently reads:
data = repmat(1:10, 1, 2);
N = 6;
period = 10;
result = NaN * zeros(1, period);
for i=1:period
range_indices = i:i+N;
temp_data = data(range_indices);
result(i) = sum( temp_data .* fliplr(temp_data));
end
I'm trying to make this faster (for larger datasets, e.g. period = 2000 and N = 1600), but I'm unable to get this into a form where it's a matrix operation (e.g. by using conv or xcorr).
You should be able to completely linearise this. Firstly, consider the range_indices. These have the form:
1 -> N
2 -> N+1
...
P -> N+P
where P is the period. We can set up a matrix of these values like so:
range_indices = bsxfun(#plus,1:N,(1:period)'-1);
We can use these to grab the data directly, like so:
temp_data = data(range_indices);
It is then fairly simply to complete the function:
result = sum(temp_data.*fliplr(temp_data));
Finally, this isn't really related to the question, but just something I thought I'd point out - in future if you need to generate a matrix of NaN values, you should use nan(1,period) instead.