Count based on column and row - arrays

I can't seem to find something quite like this problem...
I have an array table where each row contains a random assortment of numbers 1-N
On another sheet, I have a table with column and row headers numbered 1-N
I want to count how many rows in the array contain both the column and row headers for a given cell in the table. Since countifs only reference the current cell in the specified array, they don't seem to be working in this scenario.
Example array:
A B C D
1 3 5 7
1 2 3 4
2 3 4 5
2 4 6 8
...
Table results (symmetrical about the diagonal):
A B C D E F
. 1 2 3 4 5 ...
1 - 1 2 1 1
2 1 - 2 2 1
3 2 2 - 2 2
4 1 2 2 - 1
5 1 1 2 1 -
Would using nested countifs work?

I don't agree with your results corresponding to 4/2, which surely should be 3, not 2, but this formula, based on the array table being in Sheet1 A1:D4 and the results table being in Sheet2 A1:F6, placed in cell B2 of the latter, should work:
=IF($A2=B$1,"-",SUMPRODUCT(N(MMULT(N(COUNTIF(OFFSET(Sheet1!$A$1:$D$1,ROW(Sheet1!$A$1:$D$4)-MIN(ROW(Sheet1!$A$1:$D$4)),),CHOOSE({1,2},B$1,$A2))>0),{1;1})=2)))
Copy across and down as required.
Note: If your actual table is in fact much larger than that given, it will probably be worth adding a simple clause into the above to the effect that the results for approximately half of the cells are obtained from their symmetrical counterparts, rather than via calculation of this construction, thus saving resource.
Regards

Related

Replace the values in a vector with the keys from a map in MATLAB?

I have a vector of nondecreasing data. Here is a sample:
1
1
1
2
2
2
2
2
2
2
2
3
3
4
4
6
Clearly there are duplicates and missing numbers. I can remove the duplicates using unique, so my unique values are:
uniqueVals = unique(sortedData);
So far, so good. Now, I want to change the data so that the values in sortedData are replaced with their index number in uniqueVals. For instance, uniqueVals first 5 elements would be 1,2,3,4,6, with indices 1,2,3,4,5. I want to change sortedData so that 1 maps to 1, 2 maps to 2, 3 to 3, 4 to 4, 6 to 5 and so on.
I know I can create a "map" object, but that seems to just be used to map uniqueVals to its index. How do I apply that mapping so that the entries in sortedData are changed?
I have no need for this to be a particularly fast operation. sortedData contains only a few hundred thousand rows and it only needs to be done once.
You can use the third output from unique
[uniqueVals,~,yourOutput] = unique(sortedData);
yourOutput =
1
1
1
2
2
2
2
2
2
2
2
3
3
4
4
5
You can also use g = findgroups(sortedData);, which will give you the group index, where there is one group per unique value. The 2nd output of this tells you the value itself
[g, gValue] = findgroups( sortedData );

How do I generate a 2D Array with values, each with neighbours of different, non-repeating values?

I have a square 2D Array, which I wish to fill with values between 1 to 4.
For this to be correct, the neighbours of any of the values inside the array would need to look like this, as an example:
x 2 x
3 1 1
x 4 x
The x values are irrelevant to the middle value 1. As we can see, the neighbours of middle value 1 do not appear more than once, aside from itself
An incorrect value's neighbours would look like this:
x 2 x
3 1 2
x 4 x
One of the neighbours of middle value 1 appear more than once (the value of 2 appears twice), and we don't want this.
I have made a LUA solution for this problem, but it is very slow, as all it does is add 2 rules to the generation and runs through all combinations until it finds a valid one.
The rules being:
Corner neighbours (in our case the xs) cannot have the same value as the middle value
The next value OVER the relevant neighbours cannot have the same value as the middle value.
Explanation for rule 2:
y y y y
y 1 y x
y y y y
The x in this instance cannot have the value of 1, this applies for every vertical and horizontal direction (up-down-left-right)
EDIT: I now know I can just repeat a tileable pattern, but this is not what I wanted, as I do not want a clear repetition to be observable
You can simply repeat a pattern to get the desired array.
Row 1: 1 2 3 4 1 2 3... repeated for the required number of columns
Row 2: 3 4 1 2 3 4 1... repeated similarly
Row 3: Same as row 2
Row 4: Same as row 1
Row 5: Start repeating pattern from row 1
...continued for the required number of rows.
Here's an example for a 6x6 grid:
1 2 3 4 1 2
3 4 1 2 3 4
3 4 1 2 3 4
1 2 3 4 1 2
1 2 3 4 1 2
3 4 1 2 3 4
This is guaranteed to follow both rules, since:
The diagonal for a value of x here will always be 5 - x, and x != (5 - x) for x in [1,2,3,4].
The next over value for 1 will always be 3 and vice versa (in any direction), and same for 2 and 4.
Edit: In your comment, you've mentioned that you needed the array to be more "varied". Any form of randomness would mean that we can't use any patterns. In that case, your current solution can not be improved, since you can't use any pattern for optimisation.
You can use a Block that if repeated any number of times in any direction does not Break the neigbour-constraint. One such Block (I'm sure there are others, but this is the most trivial I think) would be:
1 1 2 2
3 3 4 4
2 2 1 1
4 4 3 3
You can repeat this Block any number of times in any direction and every cell will always have unique neighbors.
For example, if you need a 6x8-array just repeat the Block once to the right and down, and then slice according to the size you want:
1 1 2 2 1 1
3 3 4 4 3 3
2 2 1 1 2 2
4 4 3 3 4 4
1 1 2 2 1 1
3 3 4 4 3 3
2 2 1 1 2 2
4 4 3 3 4 4
Fun Fact: On further inspection one might recognize, that Abhinav Mathur and I came up with a somewhat similar solution. If you rotate my Block and swap the 2 with the 3, the pattern is the same. But his formulation with repeating rows instead of the whole block is probably easier to implement in most cases.
I write this as a second answer as there is a new requirement: Don't use any repeating pattern!
TLDR
Just fill the array row for row, left to right. With following constraint:
You want to fill the value x, then you only have to consider the values in the colored cells.
If the green cell is filled, then there is only one possibility for x (the one value that is not in either the green or red cells)
If the green cell is empty (you are at the left border). Then there are two options (one of the values that is not in the red cells). But the value that you choose has to be one of the values in the blue cells too (this is always possible) or otherwise it will be impossible to fill the value y later! If the blue cells aren't filled (right border) you can just ignore this secondary constraint.
Explanation
You can separate this into two independent problems. Think of your array like a chessboard and solve the numbers in the light and dark fields separately as they don't care about each other (light and dark don't share any neighbors because their neighbors are always of the opposite color respectively).
So you split your chessboard in light and dark (just to better understand, you don't have to actually separate the arrays).
Then you solve them separately with some simple constraints. The value (green) is not allowed to be equal to one of the values two cells apart (red):
Just fill the values row by row from left to right. You can only actually choose a value at the left border as the rest will be constrained by the other three values above. At this left border you have to be careful. The chosen value in the green cell has to be the same as one of the values in the blue cells. Otherwise the next cell (red) will be impossible!
Just do this for the light and dark cells and then put them back together:
This should be relatively easy to implement and really fast, as it is not brute-forcing anything but directly constructing a valid solution. When saying choose a number or fill values I mean to just get the possible values and choose one at random.
You can try yourself in excel too, it's like a really boring sudoku as there is actually not much to choose, it is really constrained :).

Google Sheets - Increment cell value with ARRAYFORMULA and based on value from another field

I have a spreadsheet I'm creating and I have an ARRAYFORMULA for incrementing the number of a field based on another field. My formula looks like this (NOTE: my rows start on row 4 that is why there is a ROW(A4:A)-3):
=ARRAYFORMULA(IF(ROW(A4:A) = 4, 1, IF(B4:B = 1, ROW(A4:A)-3, (ROW(A4:A)-3)-(B4:B - 1))))
What I'm doing is creating groups (A) and then have a sequence counter (B) which is the number of rows within the group. I want the result to look like this where the A just picks up from where it left off (Note: B is manually entered):
A
B
1
1
2
1
3
1
3
2
3
3
4
1
5
1
However, my result is looking this this:
A
B
1
1
2
1
3
1
3
2
3
3
6
1
7
1
I know ROW gets me the row number but when I try and use INDEX formula:
=ARRAYFORMULA(IF(ROW(G4:G) = 4, 1, IF(H4:H = 1, INDEX(G4:G, ROW(G4:G)-1, 1) + 1, INDEX(G4:G, ROW(G4:G)-1, 1)))
to get the actual value of the prior cell I get a constant flashing like it's stuck in an infinite loop of some sort. I know I can probably just accomplish this without ARRAYFORMULA however, this spreadsheet will be shared and contains many other formulas that I just don't want people to have to cut and copy from the row above and get formulas all messed up. I'm dealing with non-technical people that need something to just work very simple.
Sample:
https://docs.google.com/spreadsheets/d/1FqndR4oTm_uaO7aUxYSgb3p0bZ6yBz-KnjUAD8kctd4/edit?usp=drivesdk
use:
=ARRAYFORMULA(COUNTIFS(A4:A, A4:A, ROW(A4:A), "<="&ROW(A4:A)))
reverse:
=ARRAYFORMULA(MMULT(TRANSPOSE((SEQUENCE(COUNTA(B4:B))<=
SEQUENCE(1, COUNTA(B4:B)))*IF(INDIRECT("B4:B"&COUNTA(B4:B)+ROW(B4)-1)=1, 1)),
SEQUENCE(COUNTA(B4:B))^0))

Intersection with two columns in each matrix

I would like to find the intersection of two columns in two matrix (see example below). So to find the position where A and B intersect -- in this case in position 3 and 5.
My solution so far, was to combine the two columns to one column and use intersect function on one column afterwards with a string. Is there a more elegant solution?
A = [1,1;1,3;1,4;2,1;2,5;3,1]
A =
1 1
1 3
1 4
2 1
2 5
3 1
B = [2,5;1,4]
B =
2 5
1 4
You can avoid combining the columns. When using intersect you can use the rows option.
A = [1,1;1,3;1,4;2,1;2,5;3,1]
B = [2,5;1,4]
[C,ia,ib] = intersect(B,A,'rows');
>>ib
3
5
Additionally, if you do not want the intersection result to be ordered you can use the stable option.
[C,ia,ib] = intersect(B,A,'rows','stable');
>>ib
5
3

Calling multiple values from data frame by row and column in R

I'm working in R and I'd like to call a selection of values from a data frame by their column and row indices. However doing this yields a matrix rather than an array. I shall demonstrate:
Given the data.frame:
a = data.frame( a = array(c(1,2,3,4,5,6,7,8,9), c(3,3)) )
(for those of you who don't want to plug it in, it looks like this)
a.1 a.2 a.3
1 1 4 7
2 2 5 8
3 3 6 9
And lets say I have two arrays pointing to the values I'd like to grab
grab_row = c(3,1,2)
grab_col = c(1,2,1)
Now I'd expect this to be the code I want...
a[ grab_row, grab_col ]
To get these results...
[1] 3 4 2
But that comes out as a 3x3 matrix, which makes enough sense in and of itself
a.1 a.2 a.1.1
3 3 6 3
1 1 4 1
2 2 5 2
Alright, I also see my answer is in the diagonal of the 3x3 matrix... but I'd really rather stick to an array as the output.
Any thoughts? Danka.
Passing the row and column indices in as a two-column matrix (here constructed using cbind()) will get you the elements you were expecting:
a[cbind(grab_row, grab_col)]
[1] 3 4 2
This form of indexing is documented in ?"[":
Matrices and array:
[...snip...]
A third form of indexing is via a numeric matrix with the one
column for each dimension: each row of the index matrix then
selects a single element of the array, and the result is a vector.
Try this:
> mapply(function(i,j)a[i,j], grab_row, grab_col)
[1] 3 4 2
Works for both dataframes and matrices.

Resources