I'll admit that I'm not an Excel guru so maybe someone here can help me.
On my worksheet I have several blocks of data.
I calculate the sum of all items within column D of that block.
Within each block I am checking the value of the cell in column C and if it contains the letter "y" and the value in column D of that row is equal to zero I must exclude the total sum of column D.
Currently I am doing this by multiplying the sum value by either 1 or 0 which is produced by running a test over the cell contents.
Below is an example of what I am using to test rows 23 to row 25 inclusively for data in Column D. I am also performing the same on Column E and G, but the "y" character is always in column C, hence the absolut column reference.
=IF(AND($C23="y",D23=0),0,1)*IF(AND($C24="y",D24=0),0,1)*IF(AND($C25="y",D25=0),0,1)
There must be a more efficient way to do this.
Ideally I would like to write a function that I can paste into a cell and then select the rows or cells over which I run the test.
Can anyone point me in the right direction?
I'm not sure if I understand the question 100%, but here's an answer anyway:
{=NOT(SUM((C23:C25="y")*(D23:D25=0)))*SUM(D23:D25)}
Don't enter the curly braces, rather enter the formula using Control+Shift+Enter to make it an array formula. Here's what it does:
First, it counts all the rows where C is 'y' and D is zero. That will return (in this example) 0, 1, 2, or 3. The NOT will change the zero to a 1 and change anything else to a zero. Then that one or zero will be multiplied by the sum of column D.
So if zero rows have both 'y' and 0, multiply the SUM by 1. If more than zero rows have both 'y' and 0, multiply the SUM by 0.
This Function should work:
Option Explicit
Public Function getMySum(src As Range, sumColumn As Integer)
Dim iRow As Range
Dim yColumn As Integer
yColumn = 1
getMySum = 0
For Each iRow In src.Rows
If (Strings.StrConv(iRow.Cells(1, yColumn), vbLowerCase) <> "y") Or (iRow.Cells(1, sumColumn) <> 0) Then
getMySum = getMySum + iRow.Cells(1, sumColumn)
Else
getMySum = 0
Exit For
End If
Next iRow
End Function
You need to add this Code to a VBA Module
The function call for your Example would be: =getMySum("C:23:D25", 2)
The only other option I see would be to combine the value in c and d like =C23&";"&D23 and sumif VLOOKUP that searches for "y;0" returns an error.
Related
Hi everyone I have the following excel table:
my task (performed in the green cell) is that for each year I compute the sum of the rows that contain a negative number in at least one of the previous years.
For instance the -1.29 in year two is the sum of the values in year to corresponding to rows 1,5,6,10,12 which are the rows containing negative numbers in the previous year. For year 3 same logic except that now I can sum all the values for that year from rows containing negative values either in Year 1 or Year 2.
for now in each of the green cells I am using the following formulas:
Year1 : 0
Year2 : =SUM(IF(B3:B13="-";0;B3:B13)($A$3:$A$13<0))
Year3 : =SUM(IF(C3:C13="-";0;C3:C13)((($B$3:$B$13<0)+($A$3:$A$13<0))>0))
Year4 : =SUM(IF(D3:D13="-";0;D3:D13)*((($C$3:$C$13<0)+($B$3:$B$13<0)+($A$3:$A$13<0))>0))
but I would like to make recursive, in the sense that I can just drag it to the next year without having to add one more of this ($C$3:$C$13<0) terms
would that be possible by just using Excel Formulas (hence no VBA)? An excel function computing the product of two vectors element by element would be enough but I cannot find anything like that unfortunately.
Hope to find some help!
Best,
Federico
Use MMULT:
=SUM(B2:B13*(MMULT(--($A$2:A13<0);TRANSPOSE(COLUMN($A$1:A1))^0)>0))
add formula to B14 and copy to the right. This is array formula.
If I understand you correctly, the following does what you want.
{=SUM(IF(A$1:A$13<0,B$1:B$13))}
Notes:
o The formula as shown is as it would be entered at B14
o Note the {}: This is an array formal so use Ctrl, Shift and Enter to enter the first case
o You don't need to eliminate text values (SUM does that automatically)
o I prefer to include headings (if they exist and are text) as it makes growing the table easier
Not sure why, but your example formulas started at row 3 (excluding row 2).
If that is truly what's wanted amend the above to:
{=SUM(IF(A$3:A$13<0,B$3:B$13))}
There is a quite simple solution in VBA:
Function getSum(rng As Range) As Variant
Dim r As Long, c As Long
For r = 1 To rng.Rows.Count
For c = 1 To rng.Columns.Count - 1
If rng.Cells(r, c) < 0 Then
getSum = getSum + rng.Cells(r, rng.Columns.Count)
Exit For
End If
Next
Next
End Function
Where the range passed to the function is written with a combination static/dynamic definition so the range expands. The upper left cell of the range will be the first cell of your data, static: say $A$3, and the lower right corner will be the bottom cell of the column with the values you are wanting to sum and dynamic, say: B14. In that case the formula =getSum($A$3:B14) would go in B15 and could be dragged over. Here's a picture:
I originally answered a different question than was asked. Currently editing to address the asked question using formulas only.
I have a named range : mycosts comprised of these cells:
$D$20,$D$31,$D$42,$D$50
The cells contain numbers. I need the min and max numbers in the range. I know how to do that with the min and max functions. What I need to know is the cell reference, or just the row, of the cell that had the min value in it and the max value. I need this so that I can reference antoher cell in ColA from that row number.
Any help is greatly appreciated.
Because of the disjoint nature of the range, I would use a VBA UDF:
Public Function WhereIsMin(rng As Range) As Long
Dim vlu As Double, r As Range
vlu = Application.WorksheetFunction.Min(rng)
For Each r In rng
If r.Value = vlu Then
WhereIsMin = r.Row
Exit Function
End If
Next r
End Function
Given that all your cost figures are sitting in Column D, you can use MATCH to return the row number using the following formula:
=MATCH(MIN(mycosts),D1:D50,0)
You may use absolute reference for the range in Column D such as $D$1:$D$50 given that you may use this formula within another formula.
If you have other numerical values in that range which could be the same as the minimum cost (hence returning an incorrect row number), you may use a helper column to "exclude" other numerical values. As shown in the following example, the second formula returned 19 as the row number for the minimum cost but the correct answer should be 20 as returned by the first formula.
{=MATCH(MIN(mycosts),IF(LEFT($C$1:$C$50,4)="Cost",$D$1:$D$50,""),0)}
Please note it is an array formula which needs to be confirmed by pressing Ctrl+Shift+Enter in the formula bar.
In the above example I used the word Cost in Column C to identify the relevant figures in Column D, and then do the MATCH within the filtered range. You may use other methods to filter the range in Column D depending on your actual data.
Let me know if you have any question. Cheers :)
I have a data set. There is a signal triggered at one point: it change from 1 -> 0 ( I know the column number), the column looks like this
00000111111000022222233333 (transpose this line please)
I want to write a command that do this (not necessarily a macro)
if row(x) = = 1 && row (x+1) = = 0
return x
the problem is I don't know how to use IF(AND... without the row number...
Thank you for your help in advance
Supposing the column for the signal is B starting in row 1 then in another column (say C starting in C2 ) enter
=if(AND($B1=1,$B2=0),"Trigger","")
and copy down, then filter on Trigger
The following formula will return the row number of the 1 that precedes the 0
=LOOKUP(2,1/((myRng=1)*(OFFSET(myRng,1,0)=0)),ROW(myRng))
You did not write what you want to happen if there are multiple triggers. The above will return the row number of the last trigger.
myRng cannot refer to the entire column, and could be replaced by, for example, A1:A100. If you do that, you might as well replace the OFFSET(myRng,1,0) with A2:A101 to make the formula non-volatile
Explanation:
(myRng=1)*(OFFSET(myRng,1,0)=0)
This multiplies each cell in myRng by the next cell. If the first = 1, and the second = 0, then the factors resolve to TRUE * TRUE and returns a 1. So the above will return an array looking like:
{0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0}
Dividing that array into 1 returns an array of DIV/0 errors or a 1.
The LOOKUP formula will then return the position of the last 1 and, using an array of row numbers for result_vector, will match it appropriately.
I am not that fluent in Excel, so my question is potentially very simple but nevertheless gives me a headache.
I have a speadsheet with two types of values. One absolute value, and one that is calculated as a percentage from this absolute value.
A
1 10
2 0.1
3 20
4 0.2
5 30
6 0.3
7 40
8 0.4
9 50
10 0.5
In this example, the second value is 1% from the first value (e.g. 0.1 from 10). In my actual table these values differ and the numbers are random. The % fraction from the second value depends on some key etc. So this is a simplified representation for the sake of a minimal example.
I want to determine the sum of the largest 4 (out of 5) numbers, but only from those 1% (e.g. 0.1, not 10) values. The numbers are all below each other. Basically, i want to ignore the absolute numbers (e.g. 10) and only apply the relative (e.g. 0.1) numbers.
The LARGE function determines the largest n numbers and has the following format:
=SUM(LARGE(array, k))
The array represents a continuous range in the table. However, I need to throw in a selected set of fields. Is there a way to do this with set of cells?
In other words, if i use the array I have
=SUM(LARGE(A1:A10, {1,2,3,4}))
the algorithms will always pick up 20,30,40 and 50.
Ideally, I want something like this:
=SUM(LARGE(array(A2,A4,A6,A8,A10), {1,2,3,4}))
Help?
Using your provided sample data, something like this regular formula (does not require array entry) should work for you because the percentages will always be less than or equal to 1:
=SUMPRODUCT(LARGE((A1:A10<=1)*A1:A10,{1,2,3,4}))
If you want a more flexible way of grabbing the top N numbers, you can substitute the {1,2,3,4} with the ROW() function, like so:
=SUMPRODUCT(LARGE((A1:A10<=1)*A1:A10,ROW(1:4)))
EDIT: If the only way to get relative values is if they are every other row, starting in row 2, you can use this formula instead:
=SUMPRODUCT(LARGE(INDEX((MOD(ROW(A1:A10),2)=0)*A1:A10,),ROW(1:4)))
For your simplified example, suppose that B1:B4 contains the values 1,2,3,4. Then in C1:C4 enter the array formula:
{=LARGE(IF(MOD(ROW(A1:A10),2)=0,A1:A10,-1),B1:B4)}
Similarly, the formula
{=SUM(LARGE(IF(MOD(ROW(A1:A10),2)=0,A1:A10,-1),B1:B4))}
(using Ctrl + Shift + Enter to accept as an array formula)
will give you the sum of the top 4.
This assumes that the numbers are all positive. You can replace the -1 in the formula by the min of all the values -1 if that assumption isn't valid.
Another approach, if the criteria for being a relative cell is too ad-hoc to be summarized by a simple formula but if you have a listing of the cells is to use the Indirect function:
In the above screenshot I have a listing of the cells containing the relative values. In D1 I put the formula =INDIRECT(C1) and copied down. Then, the formula
=SUM(LARGE(D1:D5,{1,2,3,4}))
returns the desired sum.
There might be a way to dispense with the helper column, though the function INDIRECT seems to not play very nicely with array formulas.
On Edit: Here is a VBA solution:
'The following function returns the sum of the largest k
'elements in range R that are at the list of indices
'if indices is left blank, then the sum of
'the largest k in R is returned
Function SumLargest(R As Range, k As Long, ParamArray indices() As Variant) As Double
Dim A As Variant
Dim i As Long, n As Long
Dim sum As Double
n = UBound(indices)
If n = -1 Then
For i = 1 To k
sum = sum + Application.WorksheetFunction.Large(R, i)
Next i
SumLargest = sum
Exit Function
Else
ReDim A(0 To n)
For i = 0 To n
A(i) = R.Cells(indices(i)).Value
Next i
For i = 1 To k
sum = sum + Application.WorksheetFunction.Large(A, i)
Next i
SumLargest = sum
End If
End Function
If you put this function in a standard code module then it can be used from the worksheet like:
=SumLargest(A1:A10,4,2,4,6,8,10)
this last returns the sum of the largest 4, drawn from the entries at 2,4,6,8,10
I am not sure how to accomplish the following task:
On columnA I have numbers, on columnB I have names associated to those numbers.
I would like to be able and generate a random number from column A, but only from the numbers that do not have anything associated on column B. For eg.: If I have a number in A1, but I do not have a name in B1, the number from A1 should be one of those that could be randomly generated.
If A2 has a number, and B2 is not empty (it has a name), I do not want to have A2 in the random range.
Also, it would be great to be able and re-generate the random number (aka, If I assign a number, let's say, 321, I want to be able somehow to generate the random number again, but it should not take into consideration 321 as it was already assigned).
I appreciate any ideas.
Sample
You can try the following:
based on your sample data/spreadsheet, do the following:
1) in Cell C1: put the value "0"
2) in Cell C2: put the formula:
=IF(ISBLANK(B2), 1, 0)+OFFSET(C2,-1,0,1,1)
3) copy the formula in C2 down alongside all your data.
4) Put this formula anywhere you want to display the randomized # from Column A:
=OFFSET(A1,MATCH(RANDBETWEEN(1,COUNTA(A:A)-COUNTA(B:B)),C:C,0)-1,0,1,1)
I think you'll have a hard time doing it with worksheet functions. Here's one quick-and-dirty approach that works at least some of the time---
In two (or more) cells, add the formula =RANDBETWEEN(COLUMN(A1),COLUMN(D1)), replacing A1 and D1 with the first and last cells in your row of numbers. This will pick random column numbers.
Give each of those cells a unique name, e.g., therand, therand2, ... .
In another cell, which will be your answer, add =IF(ISBLANK(INDIRECT(ADDRESS(2,therand))),INDIRECT(ADDRESS(1,therand)),INDIRECT(ADDRESS(1,therand2))). This will check whether the first random column number corresponds to a blank label (ISBLANK(...)) and, if so, use the corresponding row-1 value. If not, it will use the row-1 value from the second random column number.
Hit F9 to recalcualate.
This will not always give you a blank-labeled cell. Add more random column numbers and corresponding IF(...) clauses to reduce the probability of a mis-selection.
Edit: you said "row A" in the original question, but I see you meant "column A" (rather than "row 1"). You'll need to transpose the rows and columns in the formulas above to make them work.
Here is a VBA answer. It can be used directly as a worksheet function:
Function RandUnassigned(R As Range) As Variant
Application.Volatile
Dim A As Variant, i As Long, n As Long
Dim num As Long
n = R.Rows.Count
ReDim A(1 To n)
For i = 1 To n
If Len(R.Cells(i, 2)) = 0 Then
num = num + 1
A(num) = R.Cells(i, 1)
End If
Next i
RandUnassigned = A(Application.WorksheetFunction.RandBetween(1, num))
End Function
Used like this:
This recalculates every time the spreadsheet does. If you want it to draw a random number just once, remove the line Application.Volatile from the function definition.