How to automatically create variables which are co

2020-04-12 09:02发布

问题:

I have an n*n matrix and I want to extract every 3 columns and keep the result in different variables.

I know that it is possible to do it in this way:

A1 = A(:,1:3); 
A2 = A(:,4:6); 
A3 = A(:,7:9);

But I would like to simplify and automate this for managing a large amount of data!

A =

[1     2     3     4     5     6     7     8     9
 2     4     6     8    10    12    14    16    18
 3     6     9    12    15    18    21    24    27
 4     8    12    16    20    24    28    32    36
 5    10    15    20    25    30    35    40    45
 6    12    18    24    30    36    42    48    54
 7    14    21    28    35    42    49    56    63
 8    16    24    32    40    48    56    64    72
 9    18    27    36    45    54    63    72    81]

Expected result:

A1 =               
[1  2   3          
 2  4   6          
 3  6   9          
 4  8   12
 5  10  15
 6  12  18
 7  14  21
 8  16  24
 9  18  27]

A2 =
[4  5   7 
 8  10  12
 12 15  18
 16 20  24 
 20 25  30
 24 30  36
 28 35  42 
 32 40  48
 36 45  54]

A3 =
[7  8   9 
 14 16  18
 21 24  27
 28 32  36 
 35 40  45
 42 48  54
 49 56  63  
 56 64  72 
 63 72  81]

回答1:

You really shouldn't split A this way. If you really want to adress A in 3 column block then use something like

A = (1:9).*((1:9).');
%% create anonymous function which can be called as Ac(1), Ac(2) and so on
Ac = @(n) A(:,(n-1)*3+1:n*3)

octave:2> Ac(1)
ans =

    1    2    3
    2    4    6
    3    6    9
    4    8   12
    5   10   15
    6   12   18
    7   14   21
    8   16   24
    9   18   27

octave:3> Ac(2)
ans =

    4    5    6
    8   10   12
   12   15   18
   16   20   24
   20   25   30
   24   30   36
   28   35   42
   32   40   48
   36   45   54


回答2:

You can use:

C = mat2cell(A,size(A,1),ones(size(A,2)/3,1)*3);

It will split your matrix into subcells.

You can access the information contained by those cells with:

C{1}
C{2}  %and so on


回答3:

Summary

You have a few options

  • Using eval. Don't do this, it's bad practise. However, it does directly answer your question in case you were curious how to do it. Because you shouldn't use this, I've included it last!
  • Using cell arrays or matrices. This is how I would do it, although if your data set is very large you may get memory issues. It does make indexing very simple, and allows you to over-write sections of the array.
  • Using an anonymous function as shorthand for your indexing. This is a read-only way to quickly access your data, and has no memory footprint.

Using cell arrays and 3D matrices

As suggested by Mathworks in the above linked blog, you could store the partitions in a cell array...

myCellArray = cell(size(A,2)/3,1)
for n = 1:3:size(A,2)
    myCellArray{1+(n-1)/3} = A(:,n:n+2)
end
% For accessing:
myCellArray{1} % = A1

or use a 3D matrix

% option 1
my3DArray = reshape(A,9,3,[]);
% option 2 (same structure as above cell example)
my3DArray = zeros(size(A,1), 3, size(A,2)/3);
for n = 1:3:size(A,2)
    my3DArray(:,:,1+(n-1)/3) = A(:,n:n+2);
end
% For accessing:
my3DArray(:,:,1); % = A1

The best option is not to duplicate A in memory and just index it as needed, either directly or using a helper function as shown above.


Using an anonymous function

You can create a function which just essentially lets you write in short hand if this is a common indexing operation. Inline functions written with the @() notation are called anonymous functions.

% Create A
A = repmat(1:9, 9, 1);
% Create helper function
An = @(n) A(:,(n-1)*3+1:n*3);
% For accessing:
An(1); % An(1) = [1 2 3; 1 2 3; ...]

However, you cannot assign using this.

An(1) = [11 12 13; 11 12 13; ...] % wont work

And you may be surprised if you change A then try and use it.

% Change A
A = repmat(11:19, 9, 1); % A = [11 12 13 14 15 16 17 18 19; 11 12 ...]
% indexing
An(1); % An(1) = [1 2 3; 1 2 3; ...] not the new values!!

To get around this second point, we could also pass A into the helper function:

An = @(M,n) M(:,(n-1)*3+1:n*3);
A = repmat(1:9, 9, 1);
An(A,1); % An(1) = [1 2 3; 1 2 3; ...];
A = repmat(11:19, 9, 1);
An(A,1); % An(1) = [11 12 13; 11 12 13; ...]; as desired!

Using eval

You can easily assign the variables like so

% Create some 9x9 matrix A
A = repmat(1:9, 9, 1);
% Loop through A, create A1, A2, ...
for n = 1:3:size(A,2)
    eval(['A', num2str(1+(n-1)/3), ' = A(:,n:n+2)']);
end
% Gives the result
% A1 = [1 2 3; 1 2 3; ...], A2 = [4 5 6; 4 5 6; ...], ...

This is very bad practise in MATLAB

Please read this post by Mathworks on avoiding eval. There is no reason why setting up variables in this way is better than any other method.

  • You won't know how many variables you're making
  • You won't know all the variable names you've generated
  • You've duplicated your "big amount of data" in memory
  • eval is harder to debug when things (inevitably) go wrong!


回答4:

Don't create dynamic variables. If really needed, reshape A into a 3D array as follows:

A = reshape(A,size(A,1),3,[]); 
%where 3 is the number of columns you want to extract at a time