How to re-organize cell arrays assigning more values to the same index

2 vues (au cours des 30 derniers jours)
Hi, I have the following code to assign a certain value to a certain timestep:
starts = [17;249;17]; % starting points for the timesteps
ends = [25;257;25]; % endingpoints for the timesteps
values = [1; 2; 3]; % values to assign in correspondent starts and ends
time_cell = cell (length(starts),1);
values_cell = cell (length(starts),1);
values_repeated = cell (1,length(starts));
for i= 1:length(starts)
time_cell{i,1}= (starts(i,1):1:ends(i,1))';
values_cell{i,1}= values(i);
values_repeated{:,i} = repmat(values_cell(i,1),length(time_cell {i,1}),1);
end
all_times_numeric = cell2mat(time_cell(:,1));
wrong_result = cat(1,values_repeated{:,:});
wrong_result =
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[1]
[2]
[2]
[2]
[2]
[2]
[2]
[2]
[2]
[2]
[3]
[3]
[3]
[3]
[3]
[3]
[3]
[3]
[3]
and would like to organise my array in such a way that for the same value of time, the correspondent cell contains more values in it. In other words, I would like to assign more values to the same timestep. To explain it, in the above example the matrix I would like to obtain is:
right_result =
[1 3]
[1 3]
[1 3]
[1 3]
[1 3]
[1 3]
[1 3]
[1 3]
[1 3]
[2]
[2]
[2]
[2]
[2]
[2]
[2]
[2]
[2]
Hope this is clear. Many thanks for your help!
  3 commentaires
Giovanni Rinaldi
Giovanni Rinaldi le 3 Août 2016
Modifié(e) : Giovanni Rinaldi le 3 Août 2016
Hi Azzi, thanks for replying and sorry if the question is not clear. I will try to explain. If you run the small piece of code I wrote, you will notice that there are two bound arrays: a column vector (all_times_numeric) with some values of timesteps (17 to 25, 249 to 257 and again 17 to 25, all in column) and a correspondent cell array (wrong_result) with 3 values (1,2,3) where 1 corresponds to the first series (17 to 25), 2 to the second (249 to 257) and 3 to the third (17 to 25, again). Now, since the values 1 and 3 are related to the same series (17 to 25), I was looking for a way of reorganizing the cell array (wrong_result) in such a way that the values 1 and 3 are in the same cell for the first 8 cells, corresponding to the series 17 to 25 in the vector array, as represented in the cell array right_result. I would like to do this using the for loop included in the code. Hope this can help you to help me :)
Giovanni Rinaldi
Giovanni Rinaldi le 3 Août 2016
Modifié(e) : Giovanni Rinaldi le 3 Août 2016
Further to my last edit, what I would like to implement is some kind of condition that checks if two interval in the first array are the same (same start and end, like for 17 and 25 in the example), and even if the value 1 correspond to the first series and the number 3 to the third, they are both included in the cells corresponding to the first series in a new cell array, as showed in right_result.
I found this condition as:
if i > 1
if ismember(time_cell{i,1},cell2mat(time_cell(1:i-1)))
Now, I am still trying to create the final cell vector (right_result).

Connectez-vous pour commenter.

Réponse acceptée

Giovanni Rinaldi
Giovanni Rinaldi le 4 Août 2016
If someone will wonder how to do something similar in the future, here is an option. Solution given in another forum.
starts = [17;249;17]; % starting points for the time periods
ends = [25;257;25]; % ending points for the time periods
values = [1; 2; 3]; % values to assign in correspondence of each interval
all_values = zeros(max(ends),max(values));
for k = 1:length(starts)
all_values(starts(k):ends(k),values(k)) = values(k);
end
all_values = all_values(sum(all_values,2)>0,:);
all_values = arrayfun(@(r) nonzeros(all_values(r,:)).',...
(1:length(all_values)),'UniformOutput',false).'
Which gives the desired result.

Plus de réponses (1)

Guillaume
Guillaume le 3 Août 2016
Modifié(e) : Guillaume le 3 Août 2016
Two notes first:
I find it much clearer to use linear (1D) indexing instead of subscript indexing when dealing with row or column vectors. In particular, I find that writing values_repeated{:,i} when the : means just one row is misleading and begs the question, is this a bug or was it intended. values_repeated{1, i} or better values_repeated{i} would achieve the same.
I recommend using numel instead of length. length is ambiguous when applied to matrices and will break your code.
Now, to answer your question. You would normally use unique to find repeated elements in an array. Unfortunately, unique does not work with cell arrays of numbers (only cell arrays of strings), so we have to be more clever. This is how I would do it:
starts = [17;249;17]; % starting points for the timesteps
ends = [25;257;25]; % endingpoints for the timesteps
values = [1; 2; 3]; % values to assign in correspondent starts and ends
time_cell = arrayfun(@(s, e) (s:e).', starts, ends, 'UniformOutput', false); %a cleaner way of generating time_cell
values_cell = num2cell(values); %a cleaner way of generating values_cell
[tcrow, tccol] = ndgrid(1:numel(time_cell)); %generating indices for cross-comparison of time_cells
issame = arrayfun(@(r, c) isequal(time_cell(r), time_cell(c)), tcrow, tccol); %compare all cells with each other. A 1 indicates identical. Obviously, all 1 on diagonal
merged_values = arrayfun(@(row) values(issame(row, :)).', 1:numel(values), 'UniformOutput', false)'
To generate your final output, one possible way of doing it:
merged_times = vertcat(time_cell{:});
cell_source = repelem(1:numel(time_cell), cellfun(@numel, time_cell))';
[~, origin] = unique(merged_times, 'stable');
right_result = merged_values(cell_source(origin))
Note that if all you wanted is to repeat my merged_values by the number of elements in each time_cell cell (which would make more sense to me than your right_result):
other_result = repelem(merged_values, cellfun(@numel, time_cell))
  2 commentaires
Giovanni Rinaldi
Giovanni Rinaldi le 3 Août 2016
Hi Guillaume, thanks for your reply and your comments. I agree about your comments on linear indexing and numel. However, this is just a part of a bigger code, therefore I have to stick to that way due to coherency issues.
Going back to my problem, unfortunately I could not test your solution until the end because the function repelem is not included in my version of MATLAB. This should not be a problem if I decide to use repmat or kron instead, but again, I would like to stick to the use of for loops and if conditions in order to find the general solution to my problem and for ease of integration with the rest of my code. I found the right grammar to check the condition (please look one of my edited previous comments) but I am still struggling in recreating the final cell array I am looking for. Many thanks for your help so far!
Guillaume
Guillaume le 3 Août 2016
It's always a good idea to mention it in the question if you're not using the latest version of matlab.
You could replicate the way I use repelem with:
function repdvector = my_repelem(vector, repetitions)
%note: does not replicate general repelem behaviour. Just the one in this case
repdvector = cell2mat(arrayfun(@(e, r) repmat(e, 1, r), vector(:), repetitions(:), 'UniformOutput', false)); %force both vectors to column with : to ensure they're the same shape
if iscolumn(vector)
repdvector = repdvector(:);
end
end

Connectez-vous pour commenter.

Catégories

En savoir plus sur Logical dans Help Center et File Exchange

Produits

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by