Creating mesh from 3-column table in matlab - arrays

I have a table with values extracted from a csv I want to make a contour plot from.
Let's use this table as an example
tdata.x = [1;2;1;2];
tdata.y = [3;3;4;4];
tdata.z = randn(4,1);
tdata=struct2table(tdata);
>> tdata
tdata =
4×3 table
x y z
_ _ _______
1 3 0.53767
2 3 1.8339
1 4 -2.2588
2 4 0.86217
I would like to pivot this such that I can use it for plotting a contour, so in principle I want a 2x2 z matrix where rows/columns are given by y and x respectively, something in this direction:
x 1 2
y
3 0.53767 1.8339
4 -2.2588 0.86217
where the first row are the x coordinates, the first columns is the y coordinates and in-between are the corresponding z-values. So that is to say the z-value corresponding to (x,y)=(1,4) is -2.2588.
Note, I am going to use this grid for other things down the road so solutions involving interpolation are not valid, as well the data is guaranteed to be given on a grid.

You can use unstack, i.e.
t = unstack( tdata, {'z'}, {'x'} );
Which will give you this:
Note that the column names are all prefixed with x because you can't have a column name beginning with a number. You should be able to extract the x values back again, especially if they're always integers it won't be too hard, for whatever operations you want from here...

Here's the approach I would use:
result = full(sparse(findgroups(tdata.y), findgroups(tdata.x), tdata.z));
Equivalently, you could use the third output of unique instead of findgroups, or accumarray instead of sparse:
[~, ~, ux] = unique(tdata.x);
[~, ~, uy] = unique(tdata.y);
result = accumarray([uy ux], tdata.z);

Related

Displaying a matrix obtained from a vectorised structure

I am trying to extract the coordinates located in the variable x within a .mat structure. I would like to print them as a three column matrix. Let's say:
-5543837.67700032 -2054567.16633347 2387852.25825667
4641938.565315761 393003.28157792 4133325.70392322
-3957408.7414133 3310229.46968631 3737494.72491701
1492206.38965564 -4458130.51073730 4296015.51539152
4075539.69798060 931735.497964395 4801629.46009471
3451207.69353006 3060375.44622100 4391915.05780934
I know that I can get them with
file=load('./filee_scan.mat')
stat = [file.scan.stat]';
x = [stat.x]';
But I get something like:
-5543837.67700032
-2054567.16633347
2387852.25825667
4641938.565315761
393003.28157792
4133325.70392322
% :: and so on
I would like to print them as I showed at the beginning (x as a vector of 3 coordinates and one line per station) but I don't know how to treat them. I have tried with loops but I really don't know how to express them.
How can I display my coordinates as an n -by- 3 matrix?
This is the scan file:
This is x:
[stat.x].' gives you a flattened vector, as you've seen. You can reshape() that vector to the desired format:
x = reshape(x,3,[]).';
This reshapes first to 3 rows and n columns (your number of stations), then transposed to have n rows of 3 columns.
For a short introduction on how reshape works, see this answer of mine.

Reshape a 3D array and remove missing values

I have an NxMxT array where each element of the array is a grid of Earth. If the grid is over the ocean, then the value is 999. If the grid is over land, it contains an observed value. N is longitude, M is latitude, and T is months.
In particular, I have an array called tmp60 for the ten years 1960 through 1969, so 120 months for each grid.
To test what the global mean in January 1960 was, I write:
tmpJan60=tmp60(:,:,1);
tmpJan60(tmpJan60(:,:)>200)=NaN;
nanmean(nanmean(tmpJan60))
which gives me 5.855.
I am confused about the reshape function. I thought the following code should yield the same average, namely 5.855, but it does not:
load tmp60
N1=size(tmp60,1)
N2=size(tmp60,2)
N3=size(tmp60,3)
reshtmp60 = reshape(tmp60, N1*N2,N3);
reshtmp60( reshtmp60(:,1)>200,: )=[];
mean(reshtmp60(:,1))
this gives me -1.6265, which is not correct.
I have checked the result in Excel (!) and 5.855 is correct, so I assume I make a mistake in the reshape function.
Ideally, I want a matrix that takes each grid, going first down the N-dimension, and make the 720 rows with 120 columns (each column is a month). These first 720 rows will represent one longitude band around Earth for the same latitude. Next, I want to increase the latitude by 1, thus another 720 rows with 120 columns. Ultimately I want to do this for all 360 latitudes.
If longitude and latitude were inputs, say column 1 and 2, then the matrix should look like this:
temp = [-179.75 -89.75 -1 2 ...
-179.25 -89.75 2 4 ...
...
179.75 -89.75 5 9 ...
-179.75 -89.25 2 5 ...
-179.25 -89.25 3 4 ...
...
-179.75 89.75 2 3 ...
...
179.75 89.75 6 9 ...]
So temp(:,3) should be all January 1960 observations.
One way to do this is:
grid1 = tmp60(1,1,:);
g1 = reshape(grid1, [1,120]);
grid2 = tmp60(2,1,:);
g2 = reshape(grid2,[1,120]);
g = [g1;g2];
But obviously very cumbersome.
I am not able to automate this procedure for the N*M elements, so comments are appreciated!
A link to the file tmp60.mat
The main problem in your code is treating the nans. Observe the following example:
a = randi(10,6);
a(a>7)=nan
m = [mean(a(:),'omitnan') mean(mean(a,'omitnan'),'omitnan')]
m =
3.8421 3.6806
Both elements in m are simply the mean on all elements in a. But they are different! The reason is the taking the mean of all values together, with mean(a(:),'omitnan') is like summing all not-nan values, and divide by the number of values we summed:
sum(a(:),'omitnan')/sum(~isnan(a(:)))==mean(a(:),'omitnan') % this is true
but taking the mean of the first dimension, we get 6 mean values:
sum(a,'omitnan')./sum(~isnan(a))==mean(a,'omitnan') % this is also true
and when we take the mean of them we divide by a larger number, because all nans were omitted already:
mean(sum(a,'omitnan')./sum(~isnan(a)))==mean(a(:),'omitnan') % this is false
Here is what I think you want in your code:
% this is exactly as your first test:
tmpJan60=tmn60(:,:,1);
tmpJan60(tmpJan60>200) = nan;
m1 = mean(mean(tmpJan60,'omitnan'),'omitnan')
% this creates the matrix as you want it:
result = reshape(permute(tmn60,[3 1 2]),120,[]).';
result(result>200) = nan;
r = reshape(result(:,1),720,360);
m2 = mean(mean(r,'omitnan'),'omitnan')
isequal(m1,m2)
To create the matrix you first permute the dimensions so the one you want to keep as is (time) will be the first. Then reshape the array to Tx(lon*lat), so you get 120 rows for all time steps and 259200 columns for all combinations of the coordinates. All that's left is to transpose it.
m1 is your first calculation, and m2 is what you try to do in the second one. They are equal here, but their value is not 5.855, even if I use your code.
However, I think the right solution will be to take the mean of all values together:
mean(result(:,1),'omitnan')

cell arrays manipulations in MATLAB --- creating a relation matrix

I have two cell arrays, named as countryname and export.
There is only one column in countryname, which is the code of the names of countries:
USA
CHN
ABW
There are two columns in export:
USA ABW
USA CHN
CHN USA
ABW USA
Each pair (X,Y) in a row of export means "country X has relation with country Y". The size of countryname has been simplified to 3. How can I achieve the following in MATLAB?
Create a square 3 by 3 (in general n by n, where n is the size of countryname) matrix M such that
M(i,j)=1 if country i has relation with country j
M(i,j)=0 otherwise.
The country names are relabeled as positive integers in countryname.
The first thing you need to do is establish a mapping from the country name to an integer value from 1 to 3. You can do that with a containers.Map where the input is a string and the output is an integer. Therefore, we will assign 'USA' to 1, 'CHN' to 2 and 'ABW' to 3. Assuming that you've initialized the cell arrays like you've mentioned above:
countryname = {'USA', 'CHN', 'ABW'};
export = {'USA', 'ABW'; 'USA', 'CHN'; 'CHN', 'USA'; 'ABW', 'USA'};
... you would create a containers.Map like so:
map = containers.Map(countryname, 1:numel(countryname));
Once you have this, you simply map the country names to integers and you can use the values function to help you do this. However, what will be returned is a cell array of individual elements. We need to unpack the cell array, so you can use cell2mat for that. As such, we can now create a 4 x 2 index matrix where each cell element is converted to a numerical value:
ind = cell2mat(values(map, export));
We thus get:
>> ind
ind =
1 3
1 2
2 1
3 1
Now that we have this, you can use sparse to create the final matrix for you where the first column serves as the row locations and the second column serves as the column locations. These locations will tell you where it will be non-zero in your final matrix. However, this will be a sparse matrix and so you'll need to convert the matrix to full to finally get a numerical matrix.
M = full(sparse(ind(:,1), ind(:,2), 1));
We get:
>> M
M =
0 1 1
1 0 0
1 0 0
As a more convenient representation, you can create a table to display the final matrix. Convert the matrix M to a table using array2table and we can add the row and column names to be the country names themselves:
>> T = array2table(M, 'RowNames', countryname, 'VariableNames', countryname)
T =
USA CHN ABW
___ ___ ___
USA 0 1 1
CHN 1 0 0
ABW 1 0 0
Take note that the above code to create the table only works for MATLAB R2013b and above. If that isn't what you require, just stick with the original numerical matrix M.
This is using basic MATLAB functionalities only. Solution posted above by #rayryeng is surely much more advanced and may be faster to code as well. However, this should also help you in understanding at fundamental level
clear
country={'USA','CHN','ABW'};
export={'USA' 'ABW'; 'USA' 'CHN'; 'CHN' 'USA' ; 'ABW' 'USA'};
M=zeros(length(country));
for i=1:length(country)
c=country(i);
ind_state=strfind(export(:,1),char(c)); % this gives state of every which is 1 or blank.
ind_match=find(not(cellfun('isempty', ind_state))); % extracting only indices which are 1.
exp_match=export(ind_match,2); % find corresponding export rel countries from second column
% useful only when your first ind_match matrix has more than 1 element.
% Like 'USA' appears twice in first column of export countries.
for j=1:length(exp_match)
c=exp_match(j);
ind_state=strfind(country,char(c));
ind_match=find(not(cellfun('isempty', ind_state)));
M(i,ind_match)=1; % Selective make elements of M 1 when there is match.
end
end
M

Grid calculation and export to CSV

I have two sets x [xmin,xmax] and y [ymin, ymax] and I would like to execute a function going stepwise from the min to the max values of x and y. So I want to apply a function to the Cartesian product of x and y. I would then like to save each combination as a row to a CSV file. I've been trying for some time with a do loop, but got a bit stuck on how to create the list in the end. For instance:
for x: 1 thru 2 step 1 do
for y: 1 thru 2 step 1 do
print([x,y,find_root (exp(a*x) = y, a, 0, 1)])
I'd get the values of x and y and the function of all combinations, but I struggle to save it and export it to a CSV, because I don't know how to create the list with [[1,1,function(1,1)],[1,2,function(1,2)],[2,1,function(2,1)],[2,2,function(2,2)]], that I could export with write_data.
Alternatively, I'd like to use:
xlist:makelist(x,x,1,2,1);
ylist:makelist(y,y,1,2,1);
create_list([x,y,x^y],x,xlist,y,ylist);
In this case I don't know how to include the function in create list or how to use map.
How do I do the above or is there a better way?
About speeding up the construction of the 1 million item list, how about solving the equation just once and then substituting values of x and y? E.g.:
solve (exp(a*x) = y, a);
my_solution : rhs (first (%));
create_list ([x, y, ev (my_solution)], x, xlist, y, ylist);
Here ev evaluates my_solution with the current values of the variables it contains (namely x and y).
About writing a CSV file, try this:
write_data (my_list, "my_output_file", 'comma);

Making coordinates out of arrays

So, I have two arrays:
X'
ans =
2.5770 2.5974 2.1031 2.7813 2.6083 2.9498 3.0053 3.3860
>> Y'
ans =
0.7132 0.5908 1.9988 1.0332 1.3301 1.1064 1.3522 1.3024
I would like to combine n-th members of two arrays together, and than plot those coordinates on graph.
So it should be:
{(2.5770,0.7132), (2.5974,0.5908)...}
Is this possible to do? If so, how?
Schorsch showed that it is simple to plot, but just to answer the question as asked in the title, you can combine the arrays into coordinates by just arranging the vectors like rectangles.
Your x and y are vertical, so you can put them side-by-side in a 2-column matrix:
combined = [x y]
or transform and have 2 rows: combined = [x' ; y']
(Because they're vertical, what you don't want is these, which would concatenate them out into one long column or row: [x ; y] or [x' y'])
Just to be clear, though, this is not needed for plotting.
Edit: A suggested edit asked what happens if you plot(combined). That depends if it's the horizontal or vertical version. In any case, plotting a 2x? matrix won't plot x vs. y. It plots all of the columns versus the simple indices 1,2,3,... So the first way I defined combined will make two lines, plotting x and y on the y-axis against their indices on the x-axis, and the second version of combined will make a strange plot with the all of the values of x plotted in a vertical column where x=1 and all of the points of y beside those at x=2.

Resources