Suppose I have a 2-D array such that the first column is composed of only two integers 1 and 2:
1 5 1 7 0.5
2 4 5 6 0.1
1 9 3 4 0.6
2 8 7 2 0.2
I want to separate two matrices out of this, such that the first column of each contains the same integer (so the first column of first matrix contains only integer 1, same goes for 2 in the second matrix).
So it would become:
1 5 1 7 0.5
1 9 3 4 0.6
and
2 4 5 6 0.1
2 8 7 2 0.2
I don't know exactly how to start. I was thinking of using the count at the beginning (well, because I have a way larger matrix with 10 different integers in the first column), then according to the counted number of each integer I construct the dimension of each [sub]matrix. After that, the only thing I could think of is the count(mask), and if the value is true it's then added to the matrix by if statement.
You can't have mixed types (integer and real) in the same array in Fortran, so I will suppose all data are real in the 2-dim array:
program split
implicit none
real, allocatable :: a(:, :), b(:, :)
integer :: i, ids = 10
integer, allocatable :: id(:), seq(:)
a = reshape([real :: 1, 5, 1, 7, 0.5, &
& 2, 4, 5, 6, 0.1, &
& 1, 9, 3, 4, 0.6, &
& 2, 8, 7, 2, 0.2], [5, 4])
seq = [(i, i = 1, size(a, 2))]
do i = 1, ids
print*, "i = ", i
! here we are creating a vector with all the line indices that start with i
! e.g. for i = 1 we get id = [1, 3], for i = 2 we get [2, 4], for i = 3 we get [], ...
id = pack(seq, a(1,:) == i)
! here we use a Fortran feature named vector-subscript
b = a(:, id)
print*, b
end do
end
If you want the first column(or any column) to be integer, you can declare it as a separated array, and use the same vector subscripts to gather the desired lines.
Related
In C you can easily initialize an array using the curly braces syntax, if I remember correctly:
int* a = new int[] { 1, 2, 3, 4 };
How can you do the same in Fortran for two-dimensional arrays when you wish to initialize a matrix with specific test values for mathematical purposes? (Without having to doubly index every element on separate statements)
The array is either defined by
real, dimension(3, 3) :: a
or
real, dimension(:), allocatable :: a
You can do that using reshape and shape intrinsics. Something like:
INTEGER, DIMENSION(3, 3) :: array
array = reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array))
But remember the column-major order. The array will be
1 4 7
2 5 8
3 6 9
after reshaping.
So to get:
1 2 3
4 5 6
7 8 9
you also need transpose intrinsic:
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array)))
For more general example (allocatable 2D array with different dimensions), one needs size intrinsic:
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(:, :), ALLOCATABLE :: array
ALLOCATE (array(2, 3))
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6 /), &
(/ size(array, 2), size(array, 1) /)))
DEALLOCATE (array)
END PROGRAM main
For multidimensional (rank>1) arrays, the Fortran way for initialization differs from the C solution because in C multidimensional arrays are just arrays of arrays of etc.
In Fortran, each rank corresponds to a different attribute of the modified data type. But there is only one array constructor, for rank-1 arrays. From these two reasons, initialization through array constructor requires the RESHAPE intrisic function.
In addition to what has already been answered, there is a more direct way of entering the value of a matrix by row instead as by column: reshape has an optional argument ORDER which can be used to modify the order of filling the element of the multidimensional array with the entries of the array constructor.
For instance, in the case of the example in the first answer, one could write:
INTEGER, DIMENSION(3, 3) :: array=reshape( (/ 1, 2, 3, &
4, 5, 6, &
7, 8, 9 /), &
shape(array), order=(/2,1/) )
obtaining the filling of the matrix array exactly in the order shown by the lines of code.
The array (/2, 1/) forces the column index (2) to have precedence on the row index (1), giving the desired effect.
Array initialization can be done in the array declaration statement itself, as shown below:
program test
real:: x(3) = (/1,2,3/)
real:: y(3,3) = reshape((/1,2,3,4,5,6,7,8,9/), (/3,3/))
integer:: i(3,2,2) = reshape((/1,2,3,4,5,6,7,8,9,10,11,12/), (/3,2,2/))
end program test
It surprises me that
real:: y(3,3) = (/(/1,2,3/),(/4,5,6/),(/7,8,9/)/)
is not accepted by the compiler (tried g95, gfortran). It turns out that the shape of
(/(/1,2,3/),(/4,5,6/),(/7,8,9/)/) is 9 and not 3 3!
The command diff calculates differences between two consecutive elements. Is there any way to calculates differences between two nonconsecutive elements?
For example, with
x = [1,2,3,4,5,6]
is there any command to find
[x(3)-x(1),x(4)-x(2),x(5)-x(3),x(6)-x(4)] = [2,2,2,2]
or
[x(4)-x(1),x(5)-x(2),x(6)-x(3)] = [3,3,3]
And in general, for the case of a matrix? I can write some code for this; I just wonder if there any existing command in Matlab for this?
An example of the matrix case
x = [1,2,3,4;1,3,5,7;2,4,6,8]
and we want to find
[x(1,3)-x(1,1),x(1,4)-x(1,2);x(2,3)-x(2,1),x(2,4)-x(2,2);x(3,3)-x(3,1),x(3,4)-x(3,2)] = [2,2;4,4;4,4]
For vectors
I would use convolution with kernel [1 0 ยทยทยท 0 -1], where the number of zeros depends on the desired step. This can be done with function conv:
x = [1,2,3,4,5,6]; % data
s = 2; % step
result = conv(x, [1 zeros(1,s-1) -1], 'valid');
gives
result =
2 2 2 2
For matrices or N-dimensional arrays
The above can be generalized using convn, with a kernel defined as before but oriented along the desired dimension:
x = [1,2,3,4; 1,3,5,7; 2,4,6,8]; % data
s = 2; % step
d = 2; % dimension
result = convn(x, reshape(([1 zeros(1,s-1) -1]), [ones(1,d-1) s+1 1]), 'valid');
gives
result =
2 2
4 4
4 4
I'm not aware of such a function, but you can simply set up a very simple anonymous function
stepDiff = #(x, s) x(:, s:end)-x(:, 1:end-s+1);
Will give outputs like:
x = [1, 2, 3, 4, 5, 6];
>> stepDiff(x, 2)
ans =
1 1 1 1 1
>> stepDiff(x, 4)
ans =
3 3 3
x = [1, 2, 3, 4; 1, 3, 5, 7; 2, 4, 6, 8];
>> stepDiff(x, 3)
ans =
2 2
4 4
4 4
I have the following sample sheet:
1/A B C D E F G H I J
2
3 Points 8 4 2 1
4
5 Values 1 2 3 4 4 3 1 2
I'm trying to sum the 'Points' based upon the array index from the 'Values'.
My expected result from this is: 30
Here is my formula:
{=SUM(INDEX($C$3:$F$3,1,C5:J5))}
For some reason though, this only returns the first value of the array, rather than the entire sum.
To clarify, the C# version would be something like:
var points = new int[] { 8, 4, 2, 1 };
var values = new int[] { 2, 4, 3, 1, 2, 4, 2 };
var result = (from v in values
select points[v - 1]).Sum(); // -1 as '4' will crash, but in Excel '4' is fine
Edit: Adding further clarifying example
Another example to clarify:
Points is the array. The 'values' represents the index of the array to sum.
The example above is the same as:
=SUM(8, 4, 2, 1, 1, 2, 8, 4)
INDEX will never take its row or column parameters from arrays and then perform multiple times within one array formula contained in one cell. For this OFFSET will be needed.
Either
{=SUM(N(OFFSET($C$3,,C5:J5-1)))}
as an array formula.
Or
=SUMPRODUCT(N(OFFSET($C$3,,C5:J5-1)))
as an implicit array formula without the need for [Ctrl]+[Shift]+[Enter].
I have a vector [x, y, ...] in Octave and I would like to take the pth powers of the elements to get the new vector [x^p, y^p, ...]. Anybody has an idea how to do this?
v = [1, 2, 3, 4, 5];
p = 2;
w = v.^p;
Output (ideone):
1 2 3 4 5
1 4 9 16 25
If you want to apply an operation element wise to a vector/matrix, prepend the operator with a dot:
b=[1,2,3,4,5,6];
b2=b.^2;
In C you can easily initialize an array using the curly braces syntax, if I remember correctly:
int* a = new int[] { 1, 2, 3, 4 };
How can you do the same in Fortran for two-dimensional arrays when you wish to initialize a matrix with specific test values for mathematical purposes? (Without having to doubly index every element on separate statements)
The array is either defined by
real, dimension(3, 3) :: a
or
real, dimension(:), allocatable :: a
You can do that using reshape and shape intrinsics. Something like:
INTEGER, DIMENSION(3, 3) :: array
array = reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array))
But remember the column-major order. The array will be
1 4 7
2 5 8
3 6 9
after reshaping.
So to get:
1 2 3
4 5 6
7 8 9
you also need transpose intrinsic:
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array)))
For more general example (allocatable 2D array with different dimensions), one needs size intrinsic:
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(:, :), ALLOCATABLE :: array
ALLOCATE (array(2, 3))
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6 /), &
(/ size(array, 2), size(array, 1) /)))
DEALLOCATE (array)
END PROGRAM main
For multidimensional (rank>1) arrays, the Fortran way for initialization differs from the C solution because in C multidimensional arrays are just arrays of arrays of etc.
In Fortran, each rank corresponds to a different attribute of the modified data type. But there is only one array constructor, for rank-1 arrays. From these two reasons, initialization through array constructor requires the RESHAPE intrisic function.
In addition to what has already been answered, there is a more direct way of entering the value of a matrix by row instead as by column: reshape has an optional argument ORDER which can be used to modify the order of filling the element of the multidimensional array with the entries of the array constructor.
For instance, in the case of the example in the first answer, one could write:
INTEGER, DIMENSION(3, 3) :: array=reshape( (/ 1, 2, 3, &
4, 5, 6, &
7, 8, 9 /), &
shape(array), order=(/2,1/) )
obtaining the filling of the matrix array exactly in the order shown by the lines of code.
The array (/2, 1/) forces the column index (2) to have precedence on the row index (1), giving the desired effect.
Array initialization can be done in the array declaration statement itself, as shown below:
program test
real:: x(3) = (/1,2,3/)
real:: y(3,3) = reshape((/1,2,3,4,5,6,7,8,9/), (/3,3/))
integer:: i(3,2,2) = reshape((/1,2,3,4,5,6,7,8,9,10,11,12/), (/3,2,2/))
end program test
It surprises me that
real:: y(3,3) = (/(/1,2,3/),(/4,5,6/),(/7,8,9/)/)
is not accepted by the compiler (tried g95, gfortran). It turns out that the shape of
(/(/1,2,3/),(/4,5,6/),(/7,8,9/)/) is 9 and not 3 3!