I am currently organizing heterogeneous data in structure arrays in MatLab, e.g.,
patient.name = 'John Doe';
patient.billing = 127;
patient.test = [79 75 73 180 178 177.5; 220 210 205 79 75 73; 180 178 177.5 20 210 205;];
patient(2).name = 'Ann Lane';
patient(2).billing = 28.50;
patient(2).test = [68 70 68; 118 118 119; 172 170 169; 220 210 205];
Let's assume I want to do some more advanced indexing, I want to look at the size of the test field of each patient. These fields all have heterogeneous sizes, which is why I want to use a different struct for each patient.
I want to do something along the lines of this:
%This does not work
disp(patient([size(patient.test,1)]>3))
For example, check whether the array of patient.test has more than 3 rows and use the resulting boolean array to index the entire structure array. I assume my syntax is simply wrong, but I have not found examples on how to do it properly. Help would be appreciated!
patient.test will give a comma-separated list of the field's contents. You can collect that list into a cell array and use cellfun to apply the size function to the contents of each cell:
>> sz = cellfun(#size, {patient.test}, 'UniformOutput', false);
>> celldisp(sz)
sz{1} =
3 6
sz{2} =
4 3
If you only want to display the sizes, you can use cellfun to apply an anonymous function that does that:
>> cellfun(#(c) disp(size(c)), {patient.test})
3 6
4 3
To obtain an index based on the size of the field:
>> ind = cellfun(#(c) size(c,1)>3, {patient.test})
ind =
1×2 logical array
0 1
and then
patient_selected = patient(ind);
or, if you prefer it in a single line,
patient_selected = patient(cellfun(#(c) size(c,1)>3, {patient.test}));
Related
I have data contained in 4195X1 double called z1. I would like to extract data in 120 chunks and label them z1_1_120, z1_120_240, z1_240_360, etc using matlab. I would like to both extract them and also label them in this manner in a loop. Here's what I've done so far, but am unsure as to how to proceed:
load(z1)
for i = 1:4195
q=z1(i);
q1(i,:)=q;
q2=q1(1:120:end);
end
z1=q2(1:end);
As Daniel commented, don't do it!
In case you want the kind of labeling you described, you may either use a Table or a Struct data types.
Read the following post:
The solution is easy: Don't do this. It is a shot in your knee
The following solution creates a Table and a Struct.
The Table needs some padding, because all columns must be the same length (and 4195 is not a multiple of 120).
The code is a bit complicated, I tried to solve it without using for loops (for making the solution more efficient [and more interesting]).
I hope you mange to follow the code, and learn from it...
Here is a code sample (explanations are in the comments):
% Fill z1 with sequential numbers (just for testing)
z1 = (1:4195)';
z1_len = length(z1);
% Compute length remainder from 120
len_mod120 = mod(z1_len, 120);
pad_len = mod(120 - len_mod120, 120);
% If length of z1 is not a multiple of 120, add zeros padding at the end.
pad_z1 = padarray(z1, pad_len, 0, 'post'); % After padding, length of z1 is 4120
% Reshape pad_z1 into a matrix where each row is 120 elements
% Z1(:, 1) gets z1(1:120), Z1(:, 2) gets z1(121:240)...
Z1 = reshape(pad_z1, [], length(pad_z1)/120);
% Build naming indices
name_idx = zeros(1, 2*length(pad_z1)/120);
name_idx(1:2:end) = 1:120:length(pad_z1); %First naming index: 1 121 241 361 ...
name_idx(2:2:end) = name_idx(1:2:end) + 120-1; %Second naming index: 120 240 360 480
% String of elements names separated by space
str_names = sprintf('z1_%d_%d ', name_idx); % 'z1_1_120 z1_121_240 z1_241_360 z1_361_480 ...
% Build cell array of names
var_names = split(str_names(1:end-1)); %{'z1_1_120'}, {'z1_121_240'}, {'z1_121_240'}
% Build table, where each column is 120 elements, and column names are 'z1_1_120' 'z1_121_240' 'z1_121_240'
% A table is useful, if you don't care about the zero padding we added at the beginning
% https://www.mathworks.com/matlabcentral/answers/376985-how-to-convert-string-to-variable-name
T = array2table(Z1, 'VariableNames', var_names);
% Convert table to struct:
% S.z1_1_120 holds first 120 elements, S.z1_121_240 holds next 120 elements...
S = table2struct(T, 'ToScalar', true);
% Fix the last field in the stract - should contain only 115 elements (not 120)
S = rmfield(S, var_names{end}); % Remove the last field from the struct
% last_field_name = 'z1_4081_4195'
last_field_name = sprintf('z1_%d_%d', name_idx(end-1), z1_len);
% Add the last field (only 195 elemtns)
S.(last_field_name) = z1(end-len_mod120+1:end);
Table T:
T =
120×35 table
z1_1_120 z1_121_240 z1_241_360 z1_361_480 ...
________ __________ __________ __________
1 121 241 361
2 122 242 362
3 123 243 363
4 124 244 364
5 125 245 365
6 126 246 366
... ... ... ...
Example for accessing the first element: T.z1_1_120(1)
Struct S:
S =
struct with fields:
z1_1_120: [120×1 double]
z1_121_240: [120×1 double]
z1_241_360: [120×1 double]
z1_361_480: [120×1 double]
z1_481_600: [120×1 double]
z1_601_720: [120×1 double]
z1_721_840: [120×1 double]
...
z1_4081_4195: [115×1 double]
Example for accessing the first element: S.z1_1_120(1)
I use this code to change my 1 x 71 cell array to split into a 71 x 12 cell array. I do what I want with split cell and I want to back in 1 x 71 cell array, But I don't know how?
Here is the code that I use for change 1 x 71 to 71 x 12 please help me reverse it:
Cmo = cell(numel(C),12);
for i = 1:numel(C)
Cmo(i,:) = arrayfun(#(m){C{i}(month(C{i}.date) == m, :)},1:12);
end
You can use reshape for both (but it maintains the number of elements, so it must be divisible without remainder):
C = cell(1,72);
% 1x72 => 12x6
C = reshape(C,12,length(C)/12);
% 12x6 => 1x72
C = reshape(C,size(C,2)*12,1);
This does not work for your case since you only have 71 entries. So you will have to do looping or extend your matrix fist and then use reshape
Num = ceil(length(C)/12)*12-length(C);
% assuming that C is an array (1x??)
C = [C,cell(1,Num)]
I am not able to save the value of BB in Bv.
MATLAB returns this error:
Subscripted assignment dimension mismatch.
Please help me to do it.
X=[1 6 9 5; 6 36 54 30; 9 54 81 40; 5 30 40 25]
[N1,dim1]=size(X) ;
for i=1:N1
bb=X(i:end,1)*X(i,i:end);
BB=bb(triu(true(size(bb))))
Bv(i,:)=BB(:);
end
As #Rashid suggests, use cell arrays instead of numeric arrays. The beauty of cell arrays is that it can store matrices of different type and size in 1 storage unit. It is much like a structure, but with indices to easily call entries.
X=[1 6 9 5; 6 36 54 30; 9 54 81 40; 5 30 40 25];
for ii=1:size(x,1)
bb=X(ii:end,1)*X(ii,ii:end);
BB=bb(triu(true(size(bb))))
Bv{ii,:}=BB(:);
end
Note that I also changed your loop index to use ii as opposed to i, see here. i is the imaginary unit and to prevent errors it's better to not overwrite build-in functions.
Just an example of how a cell array stores different data types and sizes:
A = magic(2); % 2x2 double
B = uint8(magic(3)); % 3x3 uint8
C = 'hello world'; % string
YourCell{1} = A;
YourCell{2} = B;
YourCell{3} = C;
YourCell =
[2x2 double] [3x3 uint8] 'hello world'
The same but now as a structure:
YourStruct.magic2double = A;
YourStruct.magic3uint8 = B;
YourStruct.MyString = C;
YourStruct =
magic2double: [2x2 double]
magic3uint8: [3x3 uint8]
MyString: 'hello world'
The cell and structure contain the same information, but for information in the cell you call YourCell{ii}, whilst in the structure you must call YourStruct.variablename. The cell can be accessed by indexing, the structure cannot. For the structure however you can use easy names to remember what you stored in each element, whilst that's impossible for the cell.
I have 163x2 matrix called A, and a 15x1 vector called delindex.
Now I want to delete every row from A, that has any of the numbers stored in delindex as a value in the first row.
short example:
A =
1 29292
2 44652
3 56569
4 68909
5 81053
6 93343
101 105585
102 118870
103 132163
7 144257
104 156616
8 205344
9 216865
105 228979
106 229307
107 240849
108 253306
And my delindex =
4
101
7
105
(And for possible future implications: how do I do the same for other rows, in this example the seconde one?)
I have looked up removerows and the any() function, but can't get them to work for me. I'm totally new to matlab and programming, so a place to look in documentation on this might help me as well!
Thanks in advance!
Use ismember (#Prashant's suggestion):
ia = ismember(A(:,1), delindex);
Or intersect:
[~,ia,~] = intersect(A(:,1), delindex);
To find which rows should be removed.
Then remove the rows:
A(ia,:) = []
Note
ismember returns logical indices, while in [C,ia,ib] =intersect(A,B), ia and ib are indices.
Both could be used for indexing. For more info read Matrix Indexing in MATLAB.
This question already has answers here:
Vectorizing the Notion of Colon (:) - values between two vectors in MATLAB
(4 answers)
Closed 9 years ago.
I have a series of datasets each one about 1032 x 4. With the programme I have now I can find the time at which a certain event is happening (I did that though structures). And the output I get is :
startTime: [1 84 111 251 450 482 613 660 787 951 956]
endTime: [5 90 112 252 451 485 619 661 788 952 961]
This output tells me from which row to which row this event that I am interested in is happening. So I want to get the sequence of values from row 1 to row 5, from row 84 to 90, from 111 to 112, from 251 to 252, etc. I can do that manually by typing time(1), time(5), time(84), time(90) so I can calculate the duration of the events. But is there any way to do that automatically?
Help please !! It sounds like an easy thing to do but it is driving me crazy.
Thanks in advance,
The code I have so far is:
function DetectEvent = DetectEvent(inputData, ColumnNumbers)
%ColumnNumbers = 1 contains Time
%ColumnNumbers = 2 contains Position
%ColumnNumbers =3 contains velocity
%ColumnNumbers=4 contains accelereation
eventNow = false;
event.startTime = []; % initialise
event.endTime = []; % initialise
for i = 1: length(inputData)
if abs(inputData(i,ColumnNumbers.velocity)) == 0
if ~eventNow
eventNow = true;
thisevent.startTime = i;
end
else
if eventNow
eventNow = false;
thisevent.endTime = i - 1;
event.startTime = [event.startTime, thisevent.startTime];
event.endTime = [event.endTime, thisevent.endTime];
end
end
end
You can use startTime and endTime as indices combined with the colon operator (:).
For example, to get the events recorded at i (e.g. started at startTime(i) and ended at endTime(i) you can use (assuming your 1032x4 matrix is called data):
events=data(startTime(i):endTime(i),:);
This will place into events all rows from startTime(i) to endTime(i) in the data matrix.