Performing operations on items within an array - VBA - arrays

Sorry for this question, but I haven’t found the answer in any of the texts or sites I’ve been researching. I am trying to do something that seems like it should be easy, but I don’t understand enough about arrays to pull it off. I am trying to create an array that is some number of rows; let’s say 10 rows, by 3 columns, or Myarr(1 to 10 , 1 to 3) – and then populate it as follows in memory before pasting it back into an excel sheet. Here’s an example using very simple constants and functions, not the ones I really need to run.
The reason is that I've found that running my particular construct as set of Excel formulas and VBA custom functions is very slow and results in a recalculation problem that I have written about in this forum that is not yet solved, so I am trying a work-around that performs all operations in an array, and then just pastes the result back to Excel.
Column 1 is just the list of numbers 1 to 10
Column 2 is the value of the previous row of Column 2 plus a constant; “Constant”; this is the part I really am puzzled by
Column 3 is just a function of the value of this row of Column 2
For example:
Constant = 2
Function of Column 2 value is simply Column 2 value x 4
So the output should be
Value col 1, previous value col 2 + Constant, column 2 x 4 as follows:
1,2,8
2,4,16
3,6,24
4,8,32
5,10,40
6,12,48
7,14,56
8,16,64
9,18,72
10,20,80
I just cant find any instructions about how to refer backwards to previous row values in an array and use them to produce a new value for that same column,
The simplest example would be a 1 dimensional array making a list of numbers where you started with a number and each successive row was the previous value + 1.
I realize this is probably basic stuff, but I must be searching on the wrong term to find an answer so I turn to you. Thank you very much for your help.

Did you try something like
Myarr(i,2)=Myarr(i-1,2)+const

Related

Get Row and Column Totals from 2d Array in Excel

I wanted to know how to get row and column totals from a 2D array in Excel. This is a fairly common thing to do but I couldn't find an answer to it by searching on row and column totals so I thought it would be worth posting it as a question.
Supposing I wanted to find the lowest column total and highest row total in the following array which is in cells A1:D3:-
1 2 3 4
5 6 7 8
9 10 11 12
my initial thoughts were along the lines of
=min(A1:D3*(column(A1:D3)={1,2,3,4}))
but this kind of simple approach doesn't work. I remembered reading that you had to use mmult in some of these situations and have seen advanced formulae using them but couldn't quite remember how. I shall try and answer my own question but other suggestions are more than welcome.
You can do it with MMULT as you mentioned. The following should work with your setup:
Smallest column
=MIN(MMULT({1,1,1},A1:D3))
Largest row:
=MAX(MMULT(A1:D3,{1;1;1;1}))
Note how many 1s in the array - for the rows calc you need a 1 for each column (i.e. 3) and vica versa for columns. Also note the order of the arrays - it won't work the other way around
Yes you have to mmult to deliver either a column array or row array containing the required totals, then use can use MAX, MIN or any other aggregate function to get the value you require.
Column totals
=MIN(MMULT(TRANSPOSE(ROW(A1:D3))^0,A1:D3))
Row Totals
=MAX(MMULT(A1:D3,TRANSPOSE(COLUMN(A1:D3))^0))
So the idea is that you create a single-row array {1,1,1} and multiply it by the 2D array to end up with an array {15,18,21,24} and take the minimum value from it.
Or create a single-column array {1;1;1;1} and multiply the original array by it to end up with an array {10;26;42} from which you get the maximum value.
Remember that mmult works like the matrix multiplication you might have learned at college where for each cell it works across the cells in the corresponding row of the first array and down the cells of the corresponding column in the second array multiplying each pair and adding them to the total. So the number of columns in the first array must always equal the number of rows in the second array.
These are, as #Scott Craner reminds me, array formulae that have to be entered with
Ctrl Shift Enter

speed up array formula

I have the following formula which I will explain below:
{=SUM(IF(($G$1:$L$1=$O$1)*($G$2:$L$2=$O$2)*($G$3:$L$3=$O$3)*($G$4:$L$4=$O$4)*($G$5:$L$5=$O$5)*($G$6:$L$6=$O$6)*($G$7:$L$7=$O$7);G21:L21))}
Here is what the worksheet looks like:
Under columns G - L we have a 'database' of all data. These columns will be added cumulatively each quarter (approx 30 columns a quarter). So after a few years we have ended up with a bunch of database columns (1000 + columns of raw data). For the sake of this demo, I have only included those 6 columns.
As you can see, each column contains specific parameters, between rows 1 - 7, which allows to identify specific CountryCode + Project Code + Category + Fiscal Year, + ... (etc.). This allows us to track down a unique specific project and retrieve its data.
What we have afterwards on the column O is a specific project we are trying to retrieve values for (you can see that the rows 1 - 7 are the same as under column G (we are trying to retrieve values for this particular project).
Here comes our formula. I have attached above. Here is what it looks like when I press F2. As you can see the IF statement is first simply checking whether the particular columns match the pre-defined criteria under column O and it sums all the columns that match all the criteria between rows 1-7.
Now here is the problem. We have a worksheet, which contains 20 projects (such as column O) and we are using this array formula there to retrieve values. The problem is that retrieving data using this way takes A LOT OF TIME. We have also adopted a principle via VBA that we iterate through all the cells, then we insert a formula, calculate array cell, and then we copy & Paste resulting value inside (so that we won't end up with full sheet of array formulas). However it still takes LONG to calculate (1 minute or so).
I was wondering, if there is a better solution how to retrieve the data in the already mentioned format (that means we have a specific criteria we are trying to find)? Maybe SUMIFS could be better? Or sumproduct? Or even compeltely different solution?
I am open to any proposal which would fasten the process.
i met similar problem about 2 weeks ago. At first i use a helper column/row. The helper column is to concatenate the 7 string in each column. then only use the IF function to check if the joined text match. Such as, assuming the helper row is row 8 per your sample, cell G8 formula would be
=CONCATENATE(G1,"|",G2,"|",G3,"|",G4,"|",G5,"|",G6,"|",G7)
and do the same for the rest including column O
=CONCATENATE(O1,"|",O2,"|",O3,"|",O4,"|",O5,"|",O6,"|",O7)
Then do a HLOOKUP
=HLOOKUP(O8,G8:L21,14,0)
In my case, the calculation time reduce from 10 min to a few seconds!
Alternatively I also found a way to do without helper column, using array again, but the idea is pretty much the same,
the formula in O21 as per your sample would be
=SUM(IF(CONCATENATE(G1:L1,G2:L2,G3:L3,G4:L4,G5:L5,G6:L6,G7:L7)=CONCATENATE(O1,O2,O3,O4,O5,O6,O7),G21:L21))
(i didn't add in the "|" delimiter for this formula, but it is better to do so)
But in the end I prefer the helper column method.
For your reference
HTH
To improve performance avoid reapeating same calculations multiple times.
This allows us to track down a unique specific project and retrieve its data.
If a combination of 7 values is unique, calculate the position of chosen project only once in helper cell (for example O15) with array formula (confirmed with Ctrl+Shift+Enter:
=MATCH(1;(G1:L1=O1)*(G2:L2=O2)*(G3:L3=O3)*(G4:L4=O4)*(G5:L5=O5)*(G6:L6=O6)*(G7:L7=O7);0)
Use the following formula in O21 and drag down:
=INDEX(G21:L21;1;$O$15)

Unexpected COUNTIF behavior when mixing number and text (EXCEL)

This question is about how Excel's COUNTIF function treats different data types when used as an array formula.
There are lots of good posts out there detailing how to use COUNTIF for tasks such as extracting unique values from a list, for example this post. I've managed to use examples from this and other posts to solve specific problems, but I'm trying to get a deeper understanding of array formulas in order to adapt my formulas to new needs.
I came across a peculiar behavior of COUNTIF. In general, Excel seems to treat strings as "larger than" numbers, so that the following examples are valid:
Cell Formula Returns
=1<2 TRUE
="a"<"b" TRUE
="a">"b" FALSE
=1<"b" TRUE
Now, suppose range A1:A6 contains the following data set:
1
2
3
A
B
C
For each cell in this set, I want to check how many of all the cells in the set that are smaller than or equal to that cell (a useful technique in more complex formulas). I enter the following array formula in range B1:B6:
{=COUNTIF($A$1:$A$6,"<="&$A$1:$A$6)} (CTRL + SHIFT + ENTER)
Based on the examples above comparing numbers and strings (also illustrated in Column D below), I would expect the output shown below to look like Column C. However, the array formula returns the result shown in Column B, which suggests that strings and number elements are counted separately by arraywise COUNTIF.
Column A Column B Column C Column D
1 1 1 A1<"C" = TRUE
2 2 2 A2<"C" = TRUE
3 3 3 A3<"C" = TRUE
A 1 4 A4<"C" = TRUE
B 2 5 A5<"C" = TRUE
C 3 6 A6<"C" = FALSE
So the question is how to produce the output in Column C? (EDIT: Just to clarify, I'm specifically looking for solutions that make use of COUNTIF's array properties.)
Any insight into why arraywise COUNTIF apparently behaves differently than the single-cell examples would also be much appreciated.
NOTE: I've translated the examples from a non-English version of Excel, so I apologize in advance for any typos.
PS. For a background, I ran into this problem when I tried to build a formula that would both extract unique values from a list with possible duplicates, and sort the unique values in numerical/alphabetical order. My current solution is to do this in two steps. One solution for how to do it in one step is proposed here.
First of all, excellently laid-out question, and on an interesting topic to boot.
I also raised an eyebrow when I first came across this behaviour of the COUNTIF(S)/SUMIF(S) functions. In their defence, I suppose we could construct situations in which we actually want strings and numerics to be considered separately.
In order to construct your required in-formula array, you will need something like:
MMULT(0+(TRANSPOSE($A$1:$A$6)<=$A$1:$A$6),ROW($A$1:$A$6)^0)
though note that the necessary transposition will mean that any set-up which includes this construction will require committing with CSE.
Regards
The different behavior can easily shown if you compare
=COUNTIF($A$1:$A$6,"<=A")
with
{=COUNT(IF($A$1:$A$6<="A",1))}
The first will only get text values from $A$1:$A$6 because it is clearly text to compare and it is faster ignoring other values then. =COUNTIF($A$1:$A$6,"<=3") will only get numeric values from $A$1:$A$6 because of the same reasons. Even if the criterion would be a concatination with a cell reference, then the concatination would be the first process and would lead either to "<=A" or "<=3". So it is ever clear what to compare, text or numbers.
The second first needs an array of the comparisons, then performs the IF, gets so an array of 1 or FALSE and counts then. But the "A" could also be a cell reference. So it is not clear what to compare at the beginning and the first array has to compare all values in $A$1:$A$6.
So COUNTIF(S) and SUMIF(S) cant be used comparing mixed text and numeric data.
The solution is shown already by XOR LX.
Btw.: with your PS. For a background you should consider the following solution from an German Excel site: http://www.excelformeln.de/formeln.html?welcher=236.
In your linked example:
Formula in B2 downwards
{=INDEX($A$2:$A$99,MATCH(LARGE(COUNTIF(A$2:A$99,">="&A$2:A$99)+99*ISNUMBER(A$2:A$99),ROWS($1:1)),COUNTIF(A$2:A$99,">="&A$2:A$99)+99*ISNUMBER(A$2:A$99),0))&""}
In this solution the COUNTIF compares with >= so the biggest text or number will count lowest and so get the lowest position. All number positions are added with 99. So they are ever greater than all possible text positions. So we have a descended sorted array. Then, using LARGE, the list is created from the highest to the lowest position.
I doubt that countif is the right function for what you want to achieve here.
try this (ctrl+shift+enter):
={SUM(IF(A1>=$A$1:$A$6,1,0))}
You will get
1
2
3
4
5
6
PS: CountIf is an basically an array function internally. Using it in another array function results into multiple array functions and their behaviour becomes complex. Array functions are best used with clear logical path.
As tested in Excel 2013, you will only get 1 in all results instead of what was proposed in Column B.
Currently, in the function provided by you, countif cannot figure out which cell to compare to which cell. Array functions expand ranges and then perform the provided action. Therefore, it is comparing each cell to same cell and resulting into 1.
Try this FormulaArray in B1 then copy till B6:
=SUM(($A$1:$A$6<=$A1)*1)

Countif function only evaluating on first value in array

My apologies if the title is not very clear (couldn't think of a better way to put my problem)...
I'm using a countif function to find all values that match two criteria, part type & code. The issue I'm having is that the criteria on part type is a range and when I try to evaluate this as an array formula, excel is only looking at the first value in the range. This is what the data looks like:
Sheet 1:
PART CODE
1 x
2 y
3 z
4 x
3 z
4 z
3 y
Sheet2:
1 Type A
2 Type A
3 Type B
4 Type B
I'm using a countif formula (as an array) to calculate the number of type B parts that have a code of "z". Here's the formula I used in Excel (PART & CODE are named ranges):
COUNTIFS(PART, 'Sheet2'!$B$3:$B$4, CODE,z)
This formula returns 2 instead of 3. When I evaluated this, Excel shows that it only finds the first value (y) and evaluates from there. I'm not sure why it is not seeing the second value (z) and evaluating that too. Any help on this would be great.
I did some more research and finally decided to try other functions (despite my initial thoughts that they wouldn't serve my purpose)...here's the final function:
SUMPRODUCT(IF(PART=TRANSPOSE(TypeA),1,0)*IF(CODES="z",1,0)
I had never used this function before and was thus confused when I initially read about it. After several hours of pulling my hair, I figured I had nothing to lose and forced myself to try and understand what it was accomplishing. I was so stuck on "counting" the number of occurrences that I failed to realize that by using this function with multiple IF statements, I could obtain the necessary answers (and save myself the hassle).
The TRANSPOSE function was used so that I could get the column array to be read horizontally like {1,2} instead of vertically {1;2}. I'm still not sure why my original function could not handle the vertical (unless I'm missing a fundamental aspect of arrays).

Find a MAX and SUM a variable number of columns in Excel without Macros

I have a table in Excel:
A B C D
Day1 Day2 Day3
1 Ron 3 2 2
2 Don 4 2 1
3 Ton 1 5 2
On a different worksheet, I need to produce a table of the type (without using macros):
A B C D
Ron Don Ton
1 Ron - - -
2 Don 8 - -
3 Ton 10 11 -
Where the value is the MAX of each pairs value for each days, summed across all days. So if it was just for 3 names and days, I would just use the formula below with a VLOOKUP to see what the value for each name is on a particular day and copy/paste it for each day. (Side note, actually my project is not that big, so by the time I posted this question, I could have been done with it, but I really want to learn how to do this in a more intelligent way).
=SUM(MAX( VLOOKUP(Table2!$A2,Table1!$A:$D,2,FALSE), VLOOKUP(Table2!B$1,Table1!$A:$D,2,FALSE)), and so on Day2, Day3...
I tried the following:
{=SUM(MAX(VLOOKUP(Table2!$A2,Table1!$A:$D,{2,3,4},FALSE),VLOOKUP(Table2!D$1,Table1!$A:$D,{2,3,4},FALSE)))}
However, apparently the VLOOKUP can't return an array (and INDEX MATCH can't either).
Any help would be much appreciated.
L42 is right - VLOOKUP has trouble returning an array when the lookup value is a range or array but with an array in place of column index number it returns an array no problem
To see that just put a single VLOOKUP in a cell, e.g.
=VLOOKUP($A2,Table1!$A:$D,{2,3,4},0)
then select the cell, press F2 to select the formula and F9 to see the result and you get a result like ={3,2,2}
The problem with your formulas is that MAX just takes the largest value of the six values returned by the two VLOOKUPs, it doesn't compare the two arrays
To do that in a single formula you can use a formula of the following form:
=SUM(IF(array1>array2,array1,array2))
that will compare the 2 arrays and sum the largest value in each position, for your setup that would be a formula like this in Table2 B2
=IF($A2=B$1,"-",SUM(IF(VLOOKUP($A2,Table1!$A:$D,{2,3,4},0)>VLOOKUP(B$1,Table1!$A:$D,{2,3,4},0),VLOOKUP($A2,Table1!$A:$D,{2,3,4},0),VLOOKUP(B$1,Table1!$A:$D,{2,3,4},0))))
confirm with CTRL+SHIFT+ENTER and copy across and down
If you have a larger number of columns then to avoid using {2,3,4,5,6,7,8,9,10......etc.} you can use INDEX/MATCH instead of VLOOKUP like this
=INDEX(Table1!$B:$D,MATCH($A2,Table1!$A:$A,0),0)
Note: INDEX here is returning a range (not an array). The MATCH function determines the row number and a zero as the column argument means you get the whole row returned

Resources