Changing Matlab Nested Structures to Matrices Iteratively for histogram plotting - matlab

Pretty new to Matlab, so please forgive the poor coding. I have some data for different categories (9 categories) with a different number of data points in each category. I created a structure that holds the data points for the different categories. I believe the categories are themselves structures within the larger structure.
I want to plot a histogram for each category. The first thing I tried was just creating a for-loop and plotting a histogram for each category in the structure, but this failed because the histogram doesn't take in structures. The next thing I tried to do was create another for loop which would change the structure holding each category into a cell array, but this also failed with the error:
if isnumeric(c{1}) || ischar(c{1}) || islogical(c{1}) || isstruct(c{1})
I am able to individually change each category to a cell array and then to a matrix, which allowed me to create one histogram. Is there a way to do this using a loop? My code is below. Thanks.
data = readtable('');
data = table2array(data);
Trial = data(:,1);
dist = data(:,2);
Time = data(:,3);
intstim = data(:,4);
color = data(:,5);
UniqueDist = unique(dist);
for ii = 1.0:length(UniqueDist)
idx = find(dist == UniqueDist(ii));
distTime(ii).data = Time(idx);
distTime(ii).data = distTime(ii).data(distTime(ii).data ~= 0);
end
for jj =1.0:length(distTime)
distTime(jj).data = struct2cell(distTime(jj));
distTime(jj).data = cell2mat(distTime(jj));
end

Related

Storing image data as a row vector

I have multiple images in a folder, and for each image, I want to store the data(pixel values) as a row vector. After I store them in a row vector I can combine these row vectors as one multi dimensional array. e.g. the data for the first image will be stored in row 1, the data for the second image will be stored in row 2 and so on. And any time I want to access a particular image data, let us say I want the third image, I can do something like this race(3,:).
I am currently getting the error:
Dimensions of matrices being concatenated are not consistent.
The error occurs here race = [race; imagevec] I am lost in how to correct this, unless imagevec = I(:)' is not converting the matrix to a row vector .
race = []; % to store all row vector
imagevec = []; % to store row vector
path = 'C:\Users\User_\somedir\'; % directory
pathfile = dir('C:\Users\User_\somedir\*.jpg'); % image file extension in directory
for i = 1 : length(path)
filename = strcat(path,pathfile(i).name); % get the file
I = imread(filename); % read file
imagevec = I(:)'; % convert image data to row vector
race = [race; imagevec]; % store row vector in matrix
end
Using a cell array instead of a matrix will allow you to index in this way even if your images are of different sizes.
You don't even have to turn them into a row vector to store them all in the same structure. You can do something like this:
path = 'C:\Users\User_\somedir\'; % directory
pathfile = dir([path,*.jpg']); % image file extension in directory
race = cell(length(pathfile),1);
for i = 1 : length(pathfile)
filename = strcat(path,pathfile(i).name); % get the file
I = imread(filename); % read file
race{i} = I; % store in cell array
end
Then when you want to perform some operation, you can simply index into the cell array. You could even turn it into a row vector, if you wanted to, as follows.
thisImage = race{3}(:)';
If you are using a matrix to store the results, all rows of a matrix must be the same length.
Cell arrays are similar to arrays except the elements need not be the same type / size.
You can accomplish what you are looking for using a cell array. First, initialize race to:
race = {};
Then try:
race = {race{:}, imagevec};

Sorting dicom images in Matlab

I am working with lung data sets in matlab, but I need to sort the slices correctly and show them.
I knew that can be done using the "instance number" parameter in Dicom header, but I did not manage to run the correct code.
How can I do that?
Here is my piece of code:
Dicom_directory = uigetdir();
sdir = strcat(Dicom_directory,'\*.dcm');
files = dir(sdir);
I = strcat(Dicom_directory, '\',files(i).name);
x = repmat(double(0), [512 512 1 ]);
x(:,:,1) = double(dicomread(I));
axes(handles.axes1);
imshow(x,[]);
First of all, to get the DICOM header, you need to use dicominfo which will return a struct containing each of the fields. If you want to use the InstanceNumber field to sort by, then you can do this in such a way.
%// Get all of the files
directory = uigetdir();
files = dir(fullfile(directory, '*.dcm'));
filenames = cellfun(#(x)fullfile(directory, x), {files.name}, 'uni', 0);
%// Ensure that they are actually DICOM files and remove the ones that aren't
notdicom = ~cellfun(#isdicom, filenames);
files(notdicom) = [];
%// Now load all the DICOM headers into an array of structs
infos = cellfun(#dicominfo, filenames);
%// Now sort these by the instance number
[~, inds] = sort([infos.InstanceNumber]);
infos = infos(inds);
%// Now you can loop through and display them
dcm = dicomread(infos(1));
him = imshow(dcm, []);
for k = 1:numel(infos)
set(him, 'CData', dicomread(infos(k)));
pause(0.1)
end
That being said, you have to be careful sorting DICOMs using the InstanceNumber. This is not a robust way of doing it because the "InstanceNumber" can refer to the same image acquired over time or different slices throughout a 3D volume. If you want one or the other, I would choose something more specific.
If you want to sort physical slices, I would recommend sorting by the SliceLocation field (if available). If sorting by time, you could use TriggerTime (if available).
Also you will need to consider that there could also potentially be multiple series in your folder so maybe consider using the SeriesNumber to differentiate these.

Canonical Way to Aggregate Structures into a Vector

I feel dumb even having to ask this, it really should be dead simple, but being new to MatLab I'd like to know how a more experienced person would do it.
Simple problem; I need to find some regions in multiple images, correlate them by position, save those regions of interest, and use them later. One way to do that would be to store the regions in a vector.
%% pseudo code
regions = [];
for i = some_vector_of_images
% segment, get mask
% find regions
cc = bwconncomp(mask);
stats = regionprops(cc, 'all');
% correlate against known x/y
% save for later
regions[index++] = stats;
end
% use 'regions'
However, the array declaration is problematic. Its default type is double, so that won't work (can't assign a struct to an element). I've tried struct.empty, but the array is not resizable. I've tried a cell array, but I get a similar error (Conversion to cell from struct is not possible.)
Really, I just need a way to have some collection declared prior to the loop that will hold instances of these structures. Again, pretty noobish question and slightly embarrassed here... please take pity.
See if using struct2cell helps you with this. Give this pseudo-code a try -
regions = cell(num_of_images,1) %// This will be before the loop starts
...
regions[index++] = {struct2cell(stats)} %// Inside the loop
Please not that this a pseudo-code, so square brackets and ++ won't work.
Thus, the complete version of pseudo-code would be -
%%// --- pseudo code
%// Without this pre-allocation you would get the error -
%%// "Conversion to cell from struct is not possible"
regions = cell(numel(some_vector_of_images),1)
for i = some_vector_of_images
% segment, get mask
% find regions
cc = bwconncomp(mask);
stats = regionprops(cc, 'all');
% correlate against known x/y
% save for later
regions(i) = {struct2cell(stats)}
end
You can cast your empty array to a structure array by appending a structure. Replace regions[index++] = stats; with
regions = [regions, stats];
This line will continue to build the array within the loop. This idiom is generally frowned on in MATLAB because a new array needs to be created each loop.
Another method is to preallocate the array with a template structure, using repmat.
stats = some_operations_on(some_vector_of_images(1));
regions = repmat(stats, numel(some_vector_of_images), 1);
and within the loop, assign with
regions(i) = stats;
In this scenario, typically I just don't preallocate at all, or use a cell-cat pattern.
Not initializing
This one doesn't initialize the struct array, but works fine. Make sure i is an index of each element in this case.
for i = 1:numel(some_vector_of_images)
% mask = outcome of some_vector_of_images(i)
cc = bwconncomp(mask);
regions(i) = regionprops(cc, 'all');
end
cell-cat pattern
This one catches results in a cell array, and concatenates all elements at the end.
regions = cell(numel(some_vector_of_images), 1);
index = 1;
for i = some_vector_of_images
% mask = outcome of i
cc = bwconncomp(mask);
regions{index} = regionprops(cc, 'all');
index = index + 1;
end
regions = cat(1, regions{:});

MATLAB: vectors of different length

I want to create a MATLAB function to import data from files in another directory and fit them to a given model, but because the data need to be filtered (there's "thrash" data in different places in the files, eg. measurements of nothing before the analyzed motion starts).
So the vectors that contain the data used to fit end up having different lengths and so I can't return them in a matrix (eg. x in my function below). How can I solve this?
I have a lot of datafiles so I don't want to use a "manual" method. My function is below. All and suggestions are welcome.
datafit.m
function [p, x, y_c, y_func] = datafit(pattern, xcol, ycol, xfilter, calib, p_calib, func, p_0, nhl)
datafiles = dir(pattern);
path = fileparts(pattern);
p = NaN(length(datafiles));
y_func = [];
for i = 1:length(datafiles)
exist(strcat(path, '/', datafiles(i).name));
filename = datafiles(i).name;
data = importdata(strcat(path, '/', datafiles(i).name), '\t', nhl);
filedata = data.data/1e3;
xdata = filedata(:,xcol);
ydata = filedata(:,ycol);
filter = filedata(:,xcol) > xfilter(i);
x(i,:) = xdata(filter);
y(i,:) = ydata(filter);
y_c(i,:) = calib(y(i,:), p_calib);
error = #(par) sum(power(y_c(i,:) - func(x(i,:), par),2));
p(i,:) = fminsearch(error, p_0);
y_func = [y_func; func(x(i,:), p(i,:))];
end
end
sample data: http://hastebin.com/mokocixeda.md
There are two strategies I can think of:
I would return the data in a vector of cells instead, where the individual cells store vectors of different lengths. You can access data the same way as arrays, but use curly braces: Say c{1}=[1 2 3], c{2}=[1 2 10 8 5] c{3} = [ ].
You can also filter the trash data upon reading a line, if that makes your vectors have the same length.
If memory is not an major issue, try filling up the vectors with distinct values, such as NaN or Inf - anything, that is not found in your measurements based on their physical context. You might need to identify the longest data-set before you allocate memory for your matrices (*). This way, you can use equally sized matrices and easily ignore the "empty data" later on.
(*) Idea ... allocate memory based on the size of the largest file first. Fill it up with e.g. NaN's
matrix = zeros(length(datafiles), longest_file_line_number) .* NaN;
Then run your function. Determine the length of the longest consecutive set of data.
new_max = length(xdata(filter));
if new_max > old_max
old_max = new_max;
end
matrix(i, length(xdata(filter))) = xdata(filter);
Crop your matrix accordingly, before the function returns it ...
matrix = matrix(:, 1:old_max);

How can I create/process variables in a loop in MATLAB?

I need to calculate the mean, standard deviation, and other values for a number of variables and I was wondering how to use a loop to my advantage. I have 5 electrodes of data. So to calculate the mean of each I do this:
mean_ch1 = mean(ch1);
mean_ch2 = mean(ch2);
mean_ch3 = mean(ch3);
mean_ch4 = mean(ch4);
mean_ch5 = mean(ch5);
What I want is to be able to condense that code into a line or so. The code I tried does not work:
for i = 1:5
mean_ch(i) = mean(ch(i));
end
I know this code is wrong but it conveys the idea of what I'm trying to accomplish. I want to end up with 5 separate variables that are named by the loop or a cell array with all 5 variables within it allowing for easy recall. I know there must be a way to write this code I'm just not sure how to accomplish it.
You have a few options for how you can do this:
You can put all your channel data into one large matrix first, then compute the mean of the rows or columns using the function MEAN. For example, if each chX variable is an N-by-1 array, you can do the following:
chArray = [ch1 ch2 ch3 ch4 ch5]; %# Make an N-by-5 matrix
meanArray = mean(chArray); %# Take the mean of each column
You can put all your channel data into a cell array first, then compute the mean of each cell using the function CELLFUN:
meanArray = cellfun(#mean,{ch1,ch2,ch3,ch4,ch5});
This would work even if each chX array is a different length from one another.
You can use EVAL to generate the separate variables for each channel mean:
for iChannel = 1:5
varName = ['ch' int2str(iChannel)]; %# Create the name string
eval(['mean_' varName ' = mean(' varName ');']);
end
If it's always exactly 5 channels, you can do
ch = {ch1, ch2, ch3, ch4, ch5}
for j = 1:5
mean_ch(j) = mean(ch{j});
end
A more complicated way would be
for j = 1:nchannels
mean_ch(j) = eval(['mean(ch' num2str(j) ')']);
end
Apart from gnovice's answer. You could use structures and dynamic field names to accomplish your task. First I assume that your channel data variables are all in the format ch* and are the only variables in your MATLAB workspace. The you could do something like the following
%# Move the channel data into a structure with fields ch1, ch2, ....
%# This could be done by saving and reloading the workspace
save('channelData.mat','ch*');
chanData = load('channelData.mat');
%# Next you can then loop through the structure calculating the mean for each channel
flds = fieldnames(chanData); %# get the fieldnames stored in the structure
for i=1:length(flds)
mean_ch(i) = mean(chanData.(flds{i});
end