assigning rows and columns - swift

I am trying to plot data to a grid that is made up of hexagons. Because of this, the row lengths alternate between two different values.
ie: a grid would look like this with row lengths 4 and 5
0 0 0 0 0
0 0 0 0
0 0 0 0 0
0 0 0 0
Does any one know a clever way to approach this? I thought about using flags to tell you which row you are in, but feel like there can be a more elegant solution

If all you're trying to do is figure out the row length, it's quite simple: Just use modulus of 2.
In your example, assume the top row has an index of 0, and the index increases as you go down.
rowLength = rowIndex % 2 == 0 ? 5 : 4;
0 % 2 == 0 --> 5
1 % 2 == 1 --> 4
2 % 2 == 0 --> 5
3 % 2 == 1 --> 4
Alternatively, you can have the rows always have a length of five, and on every other row store a value (such as null) that indicates the value should be skipped:
0 0 0 0 0
0 0 0 0 (null)
0 0 0 0 0
0 0 0 0 (null)

One solution is to write a "generic" pattern generator:
Takes a pattern, in your case "0 ". This specific pattern uses 2 characters.
Given number of rows, say nR, and number of characters per row, say nC (in your case, nC=5*2-1=9): repeat the pattern into one string that contains this pattern, however, without proper line breaks.
Using number of characters per row nC: insert a line break exactly after nC characters continually throughout your string.
This can be done in a somewhat "swifty functional" manner:
func plotGrid(numRows: Int, numCharsPerRow: Int, myPattern: String) {
let totalNumChars = numRows*numCharsPerRow
let numCharsInPattern = myPattern.characters.count
let gridTemplate = [String](count:totalNumChars/numCharsInPattern + totalNumChars%numCharsInPattern, repeatedValue: myPattern).reduce("", combine: +)
let grid = 0.stride(to: totalNumChars, by: numCharsPerRow)
.map {
i -> String in
let a = gridTemplate.startIndex.advancedBy(i)
let b = a.advancedBy(numCharsPerRow, limit: gridTemplate.endIndex)
return gridTemplate[a..<b] + "\n"
}.reduce("", combine: +)
print(grid)
}
With the results (for your example and another 4-character pattern example, respectively)
plotGrid(5, numCharsPerRow: 9, myPattern: "0 ")
//0 0 0 0 0
// 0 0 0 0
//0 0 0 0 0
// 0 0 0 0
//0 0 0 0 0
plotGrid(4, numCharsPerRow: 10, myPattern: "X O ")
//X O X O X
//O X O X O
//X O X O X
//O X O X O
Note, however, that in the let grid assignment, you create an array that is immediately reduced, so there will be an unnecessary overhead here, especially for large grids. The imperative approach below should be faster, but the above, perhaps, neater.
One possible (among many) classic imperative approach:
let numRows = 5
let numCharsPerRow = 9
let myPattern = "0 "
let totalNumChars = numRows*numCharsPerRow
var grid = "0"
var addZero = false
for i in 2...totalNumChars {
grid += addZero ? "0" : " "
addZero = !addZero
if i%numCharsPerRow == 0 {
grid += "\n"
}
}
print(grid)
//0 0 0 0 0
// 0 0 0 0
//0 0 0 0 0
// 0 0 0 0
//0 0 0 0 0

Related

Performing an averaging operation over every n elements in a vector

I have a logical vector in which I would like to iterate over every n-elements. If in any given window at least 50% are 1's, then I change every element to 1, else I keep as is and move to the next window. For example.
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
output = func(input,4);
output = [0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1];
This function is trivial to implement but is it possible to apply a vectorized implementation using logical indexing?. I am trying to build up the intuition of applying this technique.
here's a one liner (that works for your input):
func = #(input,n) input | kron(sum(reshape(input ,n,[]))>=n/2,ones(1,n));
of course, there are cases to solve that this doesnt answer, what if the size of the input is not commensurate in n? etc...
i'm not sure if that's what you meant by vectorization, and I didnt benchmark it vs a for loop...
Here is one way of doing it. Once understood you can compact it in less lines but I'll details the intermediate steps for the sake of clarity.
%% The inputs
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
1) Split your input into blocks of size n (note that your final function will have to check that the number of elements in input is a integer multiple of n)
c = reshape(input,n,[]) ;
Gives you a matrix with your blocks organized in columns:
c =
0 0 0 0 0
0 1 0 1 0
0 1 0 0 0
1 0 1 1 1
2) Perform your test condition on each of the block. For this we'll take advantage that Matlab is working column wise for the sum function:
>> cr = sum(c) >= (n/2)
cr =
0 1 0 1 0
Now you have a logical vector cr containing as many elements as initial blocks. Each value is the result of the test condition over the block. The 0 blocks will be left unchanged, the 1 blocks will be forced to value 1.
3) Force 1 columns/block to value 1:
>> c(:,cr) = 1
c =
0 1 0 1 0
0 1 0 1 0
0 1 0 1 0
1 1 1 1 1
4) Now all is left is to unfold your matrix. You can do it several ways:
res = c(:) ; %% will give you a column vector
OR
>> res = reshape(c,1,[]) %% will give you a line vector
res =
0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1

How to create an error function comparing two matrices?

I have two matrices in MATLAB. Each one is filled with 1 and 0 at different positions. I want to compare each element:
If there is a 1 match, I want it to record as True Positive.
If there is a 0 match, I want it to record as True Negative.
If one says 1 and the other says 0, I want to record as False Positive.
If one says 0 and the other says 1, I want to record as False Negative.
I tried just comparing the two matrices:
idx = A == B
But, that gives me a simple match, not telling me when there is a True Positive or Negative, etc.
Is there any specific function I could use, or any alternative?
You could just add the matrices in a prescribed way....
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = a + 2*b;
% For pairs [a,b] we expect
% [0,0]: C = 0, true negative
% [1,0]: C = 1, false positive
% [0,1]: C = 2, false negative
% [1,1]: C = 3, true positive
% C =
% [ 3 0 1 0
% 1 1 0 2
% 0 0 3 1 ]
If you have the Statistics and Machine Learning toolbox and you only want a summary, you might just need the function confusionmat.
From the docs:
C = confusionmat(group,grouphat) returns the confusion matrix C determined by the known and predicted groups in group and grouphat. [...]. C is a square matrix with size equal to the total number of distinct elements in group and grouphat. C(i,j) is a count of observations known to be in group i but predicted to be in group j.
For example:
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = confusionmat( a(:), b(:) );
% C =
% [ 5 1
% 4 2]
% So for each pair [a,b], we have 5*[0,0], 2*[1,1], 4*[1,0], 1*[0,1]
A similar function for those with the Neural Network Toolbox instead would be confusion.
You could just use bitwise operators to produce the four different values:
bitor(bitshift(uint8(b),1),uint8(a))
Produces an array with
0 : True Negative
1 : False Negative (a is true but b is false)
2 : False Positive (a is false but b is true)
3 : True Positive
One naive approach would be four comparisons, case by case:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
% Determine true positives, false positives, etc.
tp = ground_truth & compare
fp = ~ground_truth & compare
tn = ~ground_truth & ~compare
fn = ground_truth & ~compare
Output:
ground_truth =
1 0 1 0 0
0 1 1 0 1
1 1 0 1 0
0 1 0 1 1
0 0 0 1 0
compare =
0 1 1 0 1
0 1 1 1 0
1 1 0 0 1
1 1 1 0 0
1 1 1 1 1
tp =
0 0 1 0 0
0 1 1 0 0
1 1 0 0 0
0 1 0 0 0
0 0 0 1 0
fp =
0 1 0 0 1
0 0 0 1 0
0 0 0 0 1
1 0 1 0 0
1 1 1 0 1
tn =
0 0 0 1 0
1 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
fn =
1 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 0 1 1
0 0 0 0 0
That works, because 0 and 1 (or any positive value) are alternative representations for true and false.
To keep your main code clean, set up a separate function, say my_stats.m
function [tp, fp, tn, fn] = my_stats(ground_truth, compare)
% Determine true positives, false positives, etc.
tp = ground_truth & compare;
fp = ~ground_truth & compare;
tn = ~ground_truth & ~compare;
fn = ground_truth & ~compare;
end
and call it in your main code:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
[tp, fp, tn, fn] = my_stats(ground_truth, compare)
Hope that helps!
I found that I can use the find method and set two conditions, then just find the numbers of the element in each variable
TruePositive = length(find(A==B & A==1))
TrueNegative = length(find(A==B & A==0))
FalsePositive = length(find(A~=B & A==1))
FalseNegative = length(find(A~=B & A==0))
The confusionmatrix() method suggested by #Wolfie is also really neat, especially if you use the confusionchart() which provides a nice visualisation.

Ismember, multiple times

I have an array of letters
mystring = 'abcdefghijklmnopqrstuvwxyz';
and I have the word Elephant. I want to know how many times the letters appear in Elephant. I have tried ismember and it gives me if they appear but not how many times. How can I get the number of times a letter occurs in a word?
You could use histcounts:
mystring = 'bcdfgijkmoqrsuvwxyzelphant';
myword = 'elephant';
[sortstring, idx] = sort(mystring); % Bin edges for histcounts need to be increasing
N = histcounts(double(myword), [double(sortstring) 257]); % Add 257 to the array so we capture the last character in a bin
N(idx) = N; % Undo the sort
Which returns:
N =
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 1 1 1
Note that due to the conversion to ASCII this method is case sensitive. You can adjust for this using lower or upper, if necessary.
mystring = char(['A':'Z','a':'z']);
Alphabet = zeros(numel(mystring),1);
for ii = 1:numel(mystring)
Alphabet(ii,1) = sum(ismember('Elephant',mystring(ii)));
end
ismember checks whether the current letter of the alphabet as dictated by the loop exists in the word. If it does, it sums all occurrences to obtain the total occurrence times of each letter, stored in Alphabet, where each entry corresponds to the letter at that position in the alphabet.
I used the method of creating the alphabet as per #Daniel's comment; capitals do now work.
Example, test for William Shakespeare:
Alphabet.'
ans =
Columns 1 through 15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Columns 16 through 30
0 0 0 1 0 0 0 1 0 0 0 3 0 0 0
Columns 31 through 45
3 0 0 1 2 0 1 2 1 0 0 1 0 1 1
Columns 46 through 52
0 0 0 0 0 0 0

Finding consecutive 00's and 11's in a vector

I have a vector a = 1111000011100001110000100100 and I have to compute two values based on it: p00 and p11.
p00 is the number of times 00 is occurring in the vector, divided by the total number of zeros. For example, in the above code then number of times 00 is occurring is 8/16 (total number of zeros).
Similarly, p11 is the number of occurrences of 11 divided by the total number of ones.
How can this be implemented in Matlab?
The safest and most generic way to do it is using regular expressions, because of the way they match runs.
a = [1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0]
s = char(a + '0');
p00 = numel(regexp(s, '00')) / sum(a == 0)
p11 = numel(regexp(s, '11')) / sum(a == 1)
NOTE:
I was tempted to do something along the following lines:
a = [1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 1 0 0]
n = numel(a);
p00 = sum(a(1:n-1) == 0 & a(2:n) == 0) / sum(a == 0)
p11 = sum(a(1:n-1) == 1 & a(2:n) == 1) / sum(a == 1)
But this won't give the correct result, because it counts the sequence 0 0 0 0 as 3, rather than 2.
I would add the vector to itself shifted with one element to the right. The number of two-s will be the number of 11-s. The number of 0-s will be the number of 00-s. I think this is a natural solution in MATLAB.
Alternatively you could implement finite state machines to parse your vector.
a2 = a(2:end)+a(1:end-1);
p11 = length(find(a2 == 2))/length(find(a));
p00 = length(find(a2 == 0))/length(find(a==0));
The proposed solution was wrong!!!
Here is one that should work, but is not very efficient (but faster than the regexp solution):
d0=0; i=1;
while i<length(a)
if (a(i) == 0 & a(i)==a(i+1)) d0 = d0+1; i = i+1; end;
i=i+1;
end
p00 = d0/sum(a == 0)

howto find out if a vector contains only one 1 and other are 0? or how to check if every entry is the same?

howto find out if a vector contains only one 1 and other are 0? or how to check if every entry is the same?
e.g. i need to check if a vector contains zeros except only one 1 like:
(0 0 0 0 1 0 0 0 0) -> true
(0 0 0 0 0 0 0 0 1) -> true
(0 0 0 0 2 0 0 0 0) -> false
(0 0 1 0 1 0 0 0 0) -> false
You can use logical indexing, assuming your vector is v: numel(v(v==1)) returns the number of elements equal to 1 in your vector.
In the same way, if you want to check if every value is the same you can use: numel(unique(v)) which returns the number of unique entries of v.
A slightly different solution:
v = [0 0 0 0 1 0 0 0 0];
TF = sum(v==1)==1 %# returns TRUE
This is especially useful if you want to apply it to all rows of a matrix:
M = [
0 0 0 0 1 0 0 0 0 ;
0 0 0 0 0 0 0 0 1 ;
0 0 0 0 2 0 0 0 0 ;
0 0 1 0 1 0 0 0 0
];
TF = sum(M==1,2)==1
The result:
>> TF
TF =
1
1
0
0
The check for only zeroes could be achieved by extracting all unique elements from your variable:
u = unique (v)
You can then compare the result to zero and voila.
To check for a non-zero element, use the find function. If it finds only one index and that entry is one, your desired result is true. Otherwise it's false.
function bool = oneone(vector)
num = find(vector);
bool = isscalar(num) && vector(num)==1;
end
For all same entries, the diff function calculates the difference of subsequent elements. If any of the results are non-zero, your desired result is false.
function bool = allsame(vector)
d = diff(vector);
bool = ~any(d);
end