How would I go about counting the amount of each alphanumerical in an array? (APL) - classification

I can't figure out how to take a matrix and count the amount of the alphanumerical values for each row. I will only be taking in matrices with the values I'm counting.
For example, if I got:
ABA455
7L9O36G
DZLFPEI
I would get something like A:2 B:1 4:1 5:2 for the first row and each row would be counted independently.
I would most like to understand the operators used if you could please explain them too.
Thank you.

The following should work in any mainstream APL implementation.
Let's start with a simple vector of characters:
m ← 3 7⍴'ABA455 7L9O36GDZLFPEI'
v ← m[1;]
v
ABA455
We can find the unique characters by filtering to keep only elements that have the same index as the first occurrence of themselves:
v ⍳ v
1 2 1 4 5 5 7
⍳ ⍴ v
1 2 3 4 5 6 7
( v ⍳ v ) = ⍳ ⍴ v
1 1 0 1 1 0 1
⎕ ← unique ← ( (v ⍳ v) = ⍳ ⍴ v ) / v
AB45
Now we compare the unique elements to every element:
unique ∘.= v
1 0 1 0 0 0 0
0 1 0 0 0 0 0
0 0 0 1 0 0 0
0 0 0 0 1 1 0
0 0 0 0 0 0 1
Summing this table horizontally gives us the occurrence count for each unique element:
+/ unique ∘.= v
2 1 1 2 1
Now we just need to pair up the unique elements with their respective counts:
unique ,[1.5] +/ unique ∘.= v
A 2
B 1
4 1
5 2
1
Let's put that into a utility function:
∇ c ← Counts v; u
u ← ( (v ⍳ v) = ⍳ ⍴ v ) / v
c ← u ,[1.5] +/ u ∘.= v
∇
Counts v
A 2
B 1
4 1
5 2
1
Now we need to apply this function on each row of the matrix. We start by splitting the matrix into a vector of vectors:
⊂[2] m
┌───────┬───────┬───────┐
│ABA455 │7L9O36G│DZLFPEI│
└───────┴───────┴───────┘
Then we apply the utility function to each vector:
Counts¨ ⊂[2] m
┌───┬───┬───┐
│A 2│7 1│D 1│
│B 1│L 1│Z 1│
│4 1│9 1│L 1│
│5 2│O 1│F 1│
│ 1│3 1│P 1│
│ │6 1│E 1│
│ │G 1│I 1│
└───┴───┴───┘
Try it online!
If you're using Dyalog APL, then the Key operator (⌸) is very much what you need:
{⍺ ⍵}⌸ 'ABA455'
┌─┬───┐
│A│1 3│
├─┼───┤
│B│2 │
├─┼───┤
│4│4 │
├─┼───┤
│5│5 6│
└─┴───┘
It takes a single operand and calls it once per unique value, with the specific value as left argument and the list of occurrence indices as right argument. However, we're not interested in the actual occurrences, only in their counts:
{⍺ (≢⍵)}⌸ 'ABA455'
A 2
B 1
4 1
5 2
Now we simply have to apply this function on each row. We can do this by splitting the matrix and applying the function with Each:
{⍺ (≢⍵)}⌸¨ ↓ m
┌───┬───┬───┐
│A 2│7 1│D 1│
│B 1│L 1│Z 1│
│4 1│9 1│L 1│
│5 2│O 1│F 1│
│ 1│3 1│P 1│
│ │6 1│E 1│
│ │G 1│I 1│
└───┴───┴───┘
Try it online!

Related

how to create a vector based on two auxiliary vectors in Matlab

I am creating a Matlab program to handle an array of data that contains finite integers (not necessary to be consecutive). Let says my data array is A, I need to find the unique values of A first, that forms a vector B, and I have to count the number of occurrence of each unique value in A, that gives another vector C. Finally, I need a vector E which has the same length of A but with the value of the ith element of E computed as E(i) = C(k) with k = A(i)
I have the code to create B and C as follows:
A=[5 5 1 1 3 1 3 11 9 6 -2];
[B, ia, ic]=unique(A);
C = accumarray(ic,1);
A could be a large vector and the elements of A could be very different and in a wide range, I wonder if there is any vectorize approach to generate the array E instead of the following loop method
E = zeros(size(A));
for n=1:length(E)
k=find(B==A(n));
E(n) = C(k);
end
As you say, to calculate the values for E, you need to calculate the indices in B which correspond to each element in A, informally where A == B. This obviously won't work because A and B are different sizes. But we can get what we want by thinking about things as a 2D grid. Transpose one of the vectors and equate them.
A == B.'
% or for older versions of MATLAB (won't implicitly expand the different sized variables)
bsxfun(#eq, A, B.')
ans =
7×11 logical array
0 0 0 0 0 0 0 0 0 0 1
0 0 1 1 0 1 0 0 0 0 0
0 0 0 0 1 0 1 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1 0 0 0
% to help visualise
% A = [5 5 1 1 3 1 3 11 9 6 -2];
% B = [-2 1 3 5 6 9 11];
You can see that, for each column, the position of the 1 tells us the where we can find that element in A in vector B. We can use find to get the row indices of each 1, and use that to calculate E.
[indices, ~] = find(A == B.');
E = C(indices);
Final result
If you want to calculate this with one line of code, you can use do the following
A=[5 5 1 1 3 1 3 11 9 6 -2];
[B, ia, ic]=unique(A);
C = accumarray(ic,1);
E = C(mod(find(B.' == A)-1, length(B))+1).';
% or for older versions of MATLAB
E = C(mod(find(bsxfun(#eq, A, B.'))-1, length(B))+1).';

Count the first x integers of a group of values

I want to get the count starting from 1, of the number of occurrences of Y until its subsequent value is N. A simple example table can be found below, I've added an additional column called expected output to highlight what I'm trying to achieve.
tab:([]x:`N`N`Y`N`N`Y`Y`Y`N`N`Y`Y`Y;expected_output:0 0 1 0 0 1 2 3 0 0 1 2 3)
I have been playing around with the idea of trying to use cut (granted that I can find the correct indexes) I could split the table up, get the count of each list, then piece it all back together somehow e.g.
0 2 3 5 8 10 cut tab
Approach without scan, not as neat as terry's but should be faster on larger tab.
q)update o:{a+r-maxs differ[x]*r:sums a:`Y=x}x from tab
x expected_output o
-------------------
N 0 0
N 0 0
Y 1 1
N 0 0
N 0 0
Y 1 1
Y 2 2
Y 3 3
N 0 0
N 0 0
Y 1 1
Y 2 2
Y 3 3
One approach using scan
q)update c:0{y*x+y}\x=`Y from tab
x expected_output c
-------------------
N 0 0
N 0 0
Y 1 1
N 0 0
N 0 0
Y 1 1
Y 2 2
Y 3 3
N 0 0
N 0 0
Y 1 1
Y 2 2
Y 3 3
Essentially a modified version of sums which resets the counter back to zero (using zero/false multiplication) whenever the next boolean is zero

Matlab how to find boundary submatrix

let a and b are sequence of k consecutive natural numbers a = (a_i) and b = (b_i). If A an n x n matrix, then A[a,b] (a nontrivial submatrix of A which is obtained from eliminating elements that doesnt belong in rows a and columns b) is called boundary submatrix if
i) a_1 = 1 or a_1 > 1 and A[(a_1 - 1),b] = 0
or
ii) b_1 = 1 or b_1 > 1 and A[a,(b_1 - 1)] = 0
(yes the matrix usually got some zero entries)
what is the easiest code to understand for this case?
I tried to make and array for the sequence using columnk but it seems difficult.
Example for matrix
B =
1 1 1 0
1 2 1 1
0 1 2 2
0 0 3 2
if we pick a = 2,3 and b = 3,4 we get
B(a,b) =
1 1
2 2
and the example of its boundary submatrices of B would be its every principal submatrix, and B([2,3],[2,3]) since B([2,3],1)=0

Generate all possible column vectors in matlab

I am essentially trying to figure out how to generate code for basis vectors of different configurations of M objects into N different states (for example, if I had 2 snacks between 2 kids, I could have (2,0) (0,2) or (1,1), terrible example, but thats the idea)
I am struggling to figure out how to do this without going into many different loops (I want this to be automatic). The idea would be to create a Matrix where each row is a vector of length M. I would start with vec(1) = N then an if loop where if sum(vec) == N, Matrix(1,:)=vec; Then I could take vec(1)=N-i and do the same.
My only issue is I do not see how to use the if and forget it so that if I had maybe 2 objects in 5 locations, how would I do this to get (1 0 0 0 1).
I am not seeing how to do this.
You could use a recursive function:
function out = combos(M,N)
if N == 1
out = M;
else
out = [];
for i = 0:M
subout = combos(M-i,N-1);
subout(:,end+1) = i;
out = [out;subout];
end
end
I think this does what you want.
The key idea is to generate not the number of elements in each group, but the split points between groups. This can be done via combinations with repetition. Matlab's nchoosek generates combinations without repetition, but these are easily converted into what we need.
M = 5; % number of objects
N = 3; % number of groups
t = nchoosek(1:M+N-1, N-1); % combinations without repetition...
t = bsxfun(#minus, t, 1:N-1); % ...convert into combinations with repetition
t = diff([zeros(size(t,1), 1) t repmat(M, size(t,1), 1) ], [], 2); % the size of each
% group is the distance between split points
In this example, the result is
t =
0 0 5
0 1 4
0 2 3
0 3 2
0 4 1
0 5 0
1 0 4
1 1 3
1 2 2
1 3 1
1 4 0
2 0 3
2 1 2
2 2 1
2 3 0
3 0 2
3 1 1
3 2 0
4 0 1
4 1 0
5 0 0
This is a similar approach to Luis' without bsxfun. Because we don't like fun.
n = 5;
k = 3;
c = nchoosek(n+k-1, k-1);
result = diff([zeros(c, 1) nchoosek(1:(n+k-1), k-1) ones(c, 1)*(n+k)], [], 2) - 1;
This creates the partitions of the integer n with length k. Given an array of length n + (k-1), we find all combinations of (k-1) places to place partitions between the (unary) integers. For 5 items and 3 locations, we have 7 choices of where to put the partitions:
[ 0 0 0 0 0 0 0 ]
If our chosen combination is [2 4], we replace positions 2 and 4 with partitions to look like this:
[ 0 | 0 | 0 0 0 ]
The O's give the value in unary, so this combination is 1 1 3. To recover the values easily, we just augment the combinations with imaginary partitions at the next values to the left and right of the array (0 and n+k) and take the difference and subtract 1 (because the partitions themselves don't contribute to the value):
diff([0 2 4 8]) - 1
ans =
1 1 3
By sliding the partitions in to each possible combination of positions, we get all of the partitions of n.
Output:
result =
0 0 5
0 1 4
0 2 3
0 3 2
0 4 1
0 5 0
1 0 4
1 1 3
1 2 2
1 3 1
1 4 0
2 0 3
2 1 2
2 2 1
2 3 0
3 0 2
3 1 1
3 2 0
4 0 1
4 1 0
5 0 0

Matlab - using values from two matrices to alter a third

I have a matrix A which contains values 0-100. Matrix B (same number of rows, but only two columns) has values 0 or 1 in its second column. Matrix C has the same number of entries as Matrix A, but just 0 or 1.
I'll assume C starts full of zeros. If there is a 1 in the second column of B, then I want to have a 1 in the same row in C, for every position where there is a value in the corresponding position in A. However, I can't work out how to get this. I've tried using the for loop, but it doesn't seem to be doing what I want.
i.e.
A =
10 10 10
10 10 10
0 20 10
B =
1 0
2 0
3 1
I want to have C:
C =
0 0 0
0 0 0
0 1 1
Very grateful for any help.
Find desired rows using B:
>> B(:, 2) == 1
ans =
0
0
1
Find possible values from A:
>> A ~= 0
ans =
1 1 1
1 1 1
0 1 1
Put it all together by anding using bsxfun:
>> bsxfun(#and, B(:, 2) == 1, A ~= 0)
ans =
0 0 0
0 0 0
0 1 1