Creating a loop to manipulate a table - matlab

I have a a table with target temperature and actual temperature like this (table1):
Time Target actual diffrence
____ ______ ______ _________
1 40 40.2 0.2
2 40 41 1
3 40 40.3 0.3
I want to create a table that only contains the rows with a difference <= 0.5.
So the goal should look like this (table2):
Time Target actual
____ ______ ______
1 40 40.2
3 40 40.3
I don't know how to create a loop that solves my problem.
I tried to create an if-loop within a for loop:
for n = 1:3
if difference(n) <= 0.5
table2 = table(table1.Time(n), table1.Target(n), table1.actual(n))
end
end
But when i execute, my table3 consists of only the third row.
3 40 40.3
Can somebody please help me create the loop? (Maybe my loop always overwrites table3 and only saves the last iteration?)

Your analysis of the problem is correct. The statement in the loop just sets the value of table2 to be the latest row that meets the criterion.
You do not need to use loops for this at all. Create a boolean mask based on the difference column:
mask = table1{:, 4} > 0.5;
You can then select a subset of the entire table using the mask as an index:
table2 = table1{mask, 1:3};
You could even combine the two lines into one:
table2 = table1{table1{:, 4} > 0.5, 1:3};

Related

How can I delete table row values in pairs? For example, if either column is less than 0.01, how do I delete the row?

I have two sets of data from different instruments that have common X-variables (XThompsons) but various Y-variables (YCounts) due to various experimental conditions. The data resemble the example below:
[Table1]
XThompsons | YCounts (1) | YCounts (2) | YCounts (3) | .... | ....
------------------------------------------------------------------
[Table2]
XThompsons | YCounts (1) | YCounts (2) | YCounts (3) | .... | ....
------------------------------------------------------------------
When I have two sets of data that are like this, I have written a script to take a single Y-column information from Table1 and do some math to all Y-columns in Table2. However, when comparing two table columns if either column has a value of a specific threshold (0.10) I want to delete that value. In the example below I want to delete row 4 and row 6 because either column has a value containing 0.10 or less
XThompsons | Table1.YCounts(1) | Table2.YCounts(2)
--------------------------------------------------
1 1.00 0.50
2 0.22 0.12
3 0.29 0.14
4 0.29 0.09 (delete row)
5 0.11 0.49
6 0.02 0.83 (delete row)
How can I carry this out in Matlab? My current code is below; I convert each table row to an array first. How can I make it so that if Y < 0.10 delete the row?
datax = readtable('table1.xls'); % Instrument 1
datay = readtable('table2.xls'); % Instrument 2
SIDATA = [];
for idx=2:width(datay);
% Read the indexed column of datax (instrument 1) then normalize to 1
x = table2array(datax(:,idx));
x = x ./ max(x);
% Read indexed column of datay (instrument 2) and carry out loop
for idy=2:width(datay);
% Normalize y data to 1
y = table2array(datay(:,idy));
y = y ./ max(y);
% Calculate similarity index (SI) at using the datax index for all collision energies for datay
xynum = sum(sqrt(x) .* sqrt(y));
xyden = sqrt(sum(x) .* sum(y));
SIDATA(idy,idx) = (xynum/xyden);
end
end
Help would be appreciated.
Thanks!
Generally when looping through and pruning values you want to increment from the end of the matrix back to one; this way, if you delete any rows, you don't skip. (If you delete row 2, then advance to row 3, you skip the data formerly in row 3).
To me, the easiest way to do this is that if all your data is in one matrix A, with columns Y1 Y2,
APruned = A((A(:,1) > 0.1) & (A(:,2) > 0.1),:)
This takes the A matrix, finds the rows where Y1 > 0.1, finds the rows where Y2 > 0.1, finds the overlap, and then outputs only the rows in A where both of these are true.
You should read about logical indecies for more on this topic
EDIT: It looks like you could also clean up your earlier code using element-wise operations;
A = [datax./max(datax) datay./max(datay)];

Sorting (with conditions) using Matlab

Using Matlab, I will like to sort the following wireless sensor readings in descending order, using the received signal strength (RSS) values in Column 2. I will like to find the average of the coordinates corresponding to the three highest RSS values. Column 3 is the coordinate of each of the sensor, while Column 4 are the wireless sensors that are visible to the sensor in Column 1.
However, there is a condition that must be met. The three highest values to be selected must be visible to each other. For instance, if sensors A,D,F are selected, sensors D and F must be visible to A, sensors A and D must be visible to F, and sensors A and F must be visible D.
Column 1 Column 2 Column 3 Column 4
(Sensors that are visible to
the sensor in Column 1)
A -45 1,1 B,C,D,E,H
B -90 1,5 A,D,C,E,H
C -50 3,9 A,B,E,H,G
D -54 4,2 A,C,B,F,G
E -70 4,6 C,D,H,G
F -57 7,2 B,D,H,I
G -75 7,6 D,B,I,E
H -64 6,9 E,D,G,I
I -23 9,9 H,G,F,B
Looking forward to any form of assistance. Grateful
Here is what I got for the first part, sorting the data in descending order:
data = cell(9,4);
col1 = ['A','B','C','D','E','F','G','H','I'];
col2 = [-45,-90,-50,-54,-70,-57,-75,-64,-23];
col3 = [{'1,1'},{'1,5'},{'3,9'},{'4,2'},{'4,6'},{'7,2'},{'7,6'},{'6,9'},{'9,9'}];
col4 = [{'B,C,D,E,H'},{'A,D,C,E,H'},{'A,B,E,H,G'},{'A,C,B,F,G'},{'C,D,H,G'},{'B,D,H,I'},{'D,B,I,E'},{'E,D,G,I'},{'H,G,F,B'}];
for i = 1:length(data)
data{i,1} = col1(i);
data{i,2} = col2(i);
data{i,3} = col3(i);
data{i,4} = col4(i);
end
[trash idx] = sort([data{:,2}],'descend');
newdata = data(idx,:);
Then for the second part, this will work for finding the average of the coordinates corresponding to the three highest RSS values but without your condition that the three highest values to be selected must be visible to each other.
for i = 1:3
coord = str2num(cell2mat(newdata{i,3}));
coord_ave(i) = mean(coord);
end
I'll see if I can figure anything out on the condition stuff and post something if I do. Right now I think that using strfind will work to compare columns 1 and 4 to each other like below but some additional steps will be needed to find the 3 maximum matches in all of the data:
current_max_sens = newdata{i,1};
cell2mat(strfind(cellstr(newdata{3,4}),current_max_sens))
I hope this could at least give you a starting point.

Matlab transpose a table vector

Seems like an embarassingly simple question, but how can I transpose a Matlab table vector?
For a simple transposition of a column vector aTable to a row vector I tried standard syntaxes:
aTableT = aTable.';
aTableT = reshape(aTable, 1, height(aTable));
and
aTableT = rot90(aTable);
According to Mathworks the last one should work for table array, see here. However, I get this error code:
Error using table/permute (line 396)
Undefined function 'permute' for input arguments of type 'table'.
Error in rot90 (line 29)
B = permute(B,[2 1 3:ndims(A)]);
NB: fliplr isn't useful either. Pretty sure I've covered the obvious angles - any ideas?
thanks!
Try converting your table into an array, transposing that, then convert back to a table. In other words, try doing this:
aTableArray = table2array(aTable);
aTableT = array2table(aTableArray.');
I read the documentation for rot90 too, and it says that rot90 should definitely work for tables, and I get the same error as you. As such, since transposing obviously works for arrays / matrices, let's do a quick workaround by converting to a matrix, transposing that, then converting back to a table. This worked for me!
I extend the table() class with a few additional methods and fix some existing problems in https://github.com/okomarov/tableutils.
Specific to this question, I define a transpose of a matrix-like table, i.e. if the table has one value per cell, and all variables (columns) are of the same class, then you can transpose the table with my package.
An example:
t = array2table(magic(4))
t =
Var1 Var2 Var3 Var4
____ ____ ____ ____
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> t'
ans =
Row1 Row2 Row3 Row4
____ ____ ____ ____
Var1 16 5 9 4
Var2 2 11 7 14
Var3 3 10 6 15
Var4 13 8 12 1

Matlab: Count till sum equals 360 > insert event1, next 360 >insert event 2 etc

I have been trying to solve this problem for a while now and I would appreciate a push in the right direction.
I have a matrix called Turn. This matrix contains 1 column of data, somewhere between 10000 and 15000 rows (is variable). What I like to do is as follows:
start at row 1 and add values of row 2, row 3 etc till sum==360. When sum==360 insert in column 2 at that specific row 'event 1'.
Start counting at the next row (after 'event 1') till sum==360. When sum==360 insert in column 2 at that specific row 'event 2'. etc
So I basically want to group my data in partitions of sum==360
these will be called events.
The row number at which sum==360 is important to me as well (every row is a time point so it will tells me the duration of an event). I want to put those row numbers in a new matrix in which on row 1: rownr event 1 happened, row 2: rownr event 2 happened etc.
You can find the row indices where events occur using the following code. Basically you're going to use the modulo operator to find where the sum of the first column of Turn is a multiple of 360.
mod360 = mod(cumsum(Turn(:,1)),360);
eventInds = find(mod360 == 0);
You could then loop over eventInds to place whatever values you'd like in the appropriate rows in the second column of Turn.
I don't think you'll be able to place the string 'event 1' in the column though as a string array is acts like a vector and will result in a dimension mismatch. You could just store the numerical value 1 for the first event and 2 for the second event and so on.
Ryan's answer looks like the way to go. But if your condition is such that you need to find row numbers where the cumulative sum is not exactly 360, then you would be required to do a little more work. For that case, you may use this -
Try this vectorized (and no loops) code to get the row IDs where the 360 grouping occurs -
threshold = 360;
cumsum_val = cumsum(Turn);
ind1 = find(cumsum_val>=threshold,1)
num_events = floor(cumsum_val(end)/threshold);
[x1,y1] = find(bsxfun(#gt,cumsum_val,threshold.*(1:num_events)));
[~,b,~] = unique(y1,'first');
row_nums = x1(b)
After that you can get the event data, like this -
event1 = Turn(1:row_nums(1));
event2 = Turn(row_nums(1)+1:row_nums(2));
event3 = Turn(row_nums(2)+1:row_nums(3));
...
event21 = Turn(row_nums(20)+1:row_nums(21));
...
eventN = Turn(row_nums(N-1)+1:row_nums(N));
Edit 1
Sample case:
We create a small data of 20 random integer numbers instead of 15000 as used for the original problem. Also, we are using a threshold of 30 instead of 360 to account for the small datasize.
Code
Turn = randi(10,[20 1]);
threshold = 30;
cumsum_val = cumsum(Turn);
ind1 = find(cumsum_val>=threshold,1)
num_events = floor(cumsum_val(end)/threshold);
[x1,y1] = find(bsxfun(#gt,cumsum_val,threshold.*(1:num_events)));
[~,b,~] = unique(y1,'first');
row_nums = x1(b);
Run
Turn =
7
6
3
4
5
3
9
2
3
2
3
5
4
10
5
2
10
10
5
2
threshold =
30
row_nums =
7
14
18
The run results shows the row_nums as 7, 14, 18, which mean that the second grouping starts with the 7th index in Turn, third grouping starts at 14th index and so on. Of course, you can append 1 at the beginning of row_nums to indicate that the first grouping starts at the 1st index.
Given a column vector x, say,
x = randi(100,10,1)
the following would give you the index of the first row where the cumulative sum off all the items above that row adds up to 360:
i = max( find( cumsum(x) <= 360) )
Then, you would have to use that index to find the next set of cumulative sums that add up to 360, something like
offset = max( find( cumsum(x(i+1:end)) <= 360 ) )
i_new = i + offset
You might need to add +1/-1 to the offset and the index.
>> x = randi(100,10,1)'
x =
90 47 47 44 8 79 45 9 91 6
>> cumsum(x)
ans =
90 137 184 228 236 315 360 369 460 466
>> i = max(find(cumsum(x)<=360))
i =
7

Merge tables Without sorting on Keys

I have two tables (or actually I have several) that should be merged using innerjoin. It works as expected; except that it sort on the "Keys", which destroys my data since it actually must be arranged in the original row order.
From the help section:
C is sorted by the values in the key variables
t1 = Case Val
3 1
1 1
2 1
t2 = Val2 Case Subset
2 2 2
1 2 2
2 3 1
tnew = innerjoin(t1,t2)
tnew = Case Val Val2 Subset
2 ...
% will start with "2" since its a lower value than "3", but in t1 "3" was in lower row than "2", it is rearranged
How should I avoid the sorting? Hopeless to use innerjoin?
In addition to the resulting table, innerjoin returns two extra outputs: The row indices of the first table and the row indices of the second table that correspond to the rows in the output.
You can simply use the second output to determine the rows in t1 that were used and you could sort these. Then use the sort order to change the ordering of the rows in the result of the join.
%// Setup the data
t1 = table([3;1;2], [1;1;1], 'VariableNames', {'Case', 'Val'});
t2 = table([2;1;2],[2;2;3],[2;2;1], 'VariableNames', {'Val2', 'Case', 'Subset'});
%// Perform the inner join and keep track of where the rows were in t1
[tnew, rows_in_t1] = innerjoin(t1, t2);
%// Sort them in order to maintain the order in t1
[~, sortinds] = sort(rows_in_t1);
%// Apply this sort order to the new table
tnew = tnew(sortinds,:);
%// Case Val Val2 Subset
%// ____ ___ ____ ______
%// 3 1 2 1
%// 2 1 2 2
%// 2 1 1 2