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.
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.
My workbook is a bit complicated, but the basic problem can be illustrated with a short example.
Let's assume that I have 5 cells in Excel: A, B, C, D, and E.
A = 0
B = 5 * A
C = 0
D = 10 * C
E = B + D
In the Excel Solver Function I select cell E as the objective that is to be maximised, and cells A and C as the variable cells. Furthermore, I add the constraint that cell E must not exceed 10, and the second constraint that cells A and C must be integers.
The ideal solution would be that the value in cell A should be 0, and the value in cell C should be 10. In the more complicated version of this problem, however, Excel cannot find the optional solution.
The way the current formulas look like I expect that Excel could find the right solution, if excel only used natural numbers to find the optimal solution. For example, Excel currently would look at the outcome for A = 0.01 and C = 9.99. Instead, Excel should strictly compare outcomes for variable choices such as A = 1 and C = 9.
How can I make the Excel solver function operate with natural numbers only?
I suggest you keep your current cells, and create a mirror set beside them. Each mirror cell will equal the rounded version of the original cell. ie: RoundedA will have the formula:
=ROUND(A,0)
Then when you do your data analysis, solve for the rounded version of those cells, with the changing variable being one of the "original" cells.
EDIT
As discussed below, you may need to 'trick' data validation into creating the values you want.
Assume A1 is going to be your "testing" data validation cell. B1 is a permanently blank cell. Set another cell, say, C1, equal to:
=randbetween(1,10)
This will create a random integer between 1 and 10. Set your "variable" cell to be equal to C10. So, your variable cell will always be a random number between 1 & 10 (or you could set "1" equal to the smallest number of your other variables, and "10" equal to the largest number of your other variables. This will create the scope needed to answer your question).
Then when you do data validation, make it try to get A1 = TRUE. Do this by figuring out what the 'test' condition is of your cells. Something like "when X = 0, I know that all my variables are correct". ie set A1 to:
=if(X=0,1,0)
Then do data validation by changing B1 (your blank, unrefered-to cell), waiting until A1 = 1. Does that make sense? It will turn C1 into the random cycling variable between LOWVALUE and HIGHVALUE, always using integers. Data validation will stop when A1 = 1 (which happens when some value X = 0). B1 will just spin uselessly until Data validation finds something (or it will stop after a few hundred tries if it finds nothing).
To get Solver to try larger increment divide the target cells by a large number and in such a way, each increment that solver tries is larger. (for example it changes the target cells by +-0.00001 - in that case divide the target cells by 100000 or more).
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.
I have a 2 variable 100x100 data table in excel.
I need to have a function that returns all the possible sets of variables that yield a given target value.
What I am looking at is some kind of a reursive 2 dimensional lookup function. Can someone point me in the right direction?
It can be done without VBA, fairly compactly, like so.
Suppose your 100x100 table is in B2:CW101, and we put a list of numbers 1 to 100 down the left from A2 to A101, and again 1 to 100 across the top from B1 to CW1
Create a column of cells underneath, starting (say) in B104
B104=MAX(($A$2:$A$101*100+$B$1:$CW$1<B103)*($B$2:$CW$101=TargetValue)*($A$2:$A$101*100+$B$1:$CW$1))
This is an "array" formula,so press Ctrl-Shift-Enter instead of Enter, and curly brackets {} should appear around the formula.
Then copy down for as many rows as you might need. You also need to put a large number above your first formula, i.e. in B103, e.g. 999999.
What the formula does is to calculate Rowx100+Column, but only for each successful cell, and the MAX function finds the largest result, excluding all previous results found, i.e. it finds the target results one at a time, starting from bottom right and working up to top left. (With a little effort you could get it to search the other way).
This will give you results like 9922, which is row 99, column 22, and you can easily extract these values from the number.
There is no built-in function that will do what you want, I'm 99% sure of that.
A VBA function that returns an array could be built, along the lines of the quick-and-dirty Sub already shown. Create an Variant to hold the output, perhaps Redimmed to the maximum possible number of results and Redim Preserve-d down to the actual number at the end. Then return that as the result of the function which then needs to be called as an array function (Control-Shift-Enter).
One down-side is that you'd have to ensure that the target range was large enough to hold the entire result: Excel won't do that automatically.
Would the Solver suit?
http://office.microsoft.com/en-us/excel/HA011118641033.aspx
I tried this a lot without using VBA but doesn't seem to be possible without it.
To solve this issue , I needed to loop through the entire array and found closest values. These values were then derefernced using calls and range properties and the output was generated in a range being incremented at each valid match.
The quick and dirty implementation is as under:
Dim arr As Range
Dim tempval As Range
Dim op As Integer
Set arr = Worksheets("sheet1").Range("b2:ao41")
op = 1
Range("B53:D153").ClearContents
For Each tempval In arr
If Round(tempval.Value, 0) = Round(Range("b50").Value, 0) Then
Range("b52").Offset(op, 0).Value = Range("a" & tempval.Row).Value
Range("b52").Offset(op, 1).Value = Cells(tempval.Column, 1).Value
Range("b52").Offset(op, 2).Value = tempval.Value
op = op + 1
End If
Next
Range("b50").Select
I am still looking for an approach without VBA.
I've got a solution that doesn't use VBA, but it's fairly messy. It involves creating a further one-dimensional table in Excel and doing lookups on that. For a 100x100 data table, the new table would need 10,000 rows.
Apologies if this doesn't fit your needs.
A summary is below - let me know if you need more detail. N = the dimension of the data, e.g. 100 in your example.
First, create a new table with five columns and NxN rows. In each case, replace my column names with the appropriate Excel reference
The first column (call it INDEX) simply lists 1, 2... NxN.
The second column (DATAROW) contains a formula to loop through 1, 2... N, 1, 2...N... This can be done using something like =MOD(INDEX-1, N)+1
The third column (DATACOL) contains 1, 1, 1... 2, 2, 2... (N times each).
This can be done with =INT((INDEX-1)/N)+1
The fourth column (VALUE) contains the value from your data table, using something like:
=OFFSET($A$1, DATAROW, DATACOL), assuming your data table starts at $A$1
We have now got a one-dimensional table holding all your data.
The fifth column (LOOKUP) contains the formula:
=MATCH(target, OFFSET(VALUERANGE, [LOOKUP-1], 0),0)+ [LOOKUP-1]
where [LOOKUP-1] refers to the cell immediately above (e.g. in cell F4 this refers to F3). You'll need a 0 above the first cell in the LOOKUP column.
VALUERANGE should be a fixed (named or using $ signs) reference to the entire VALUE column.
The LOOKUP column then holds INDEX numbers which can be used to look up DATAROW and DATACOL to find the position of the match in the data.
This works by searching for matches in VALUERANGE, then searching for matches in an adjusted range starting after the previous match.
It's much easier in a spreadsheet then via the explanation above, but that's the best I can do for the moment...
I have an Excel spreadsheet with 1 column, 700 rows. I care about every seventh line. I don't want to have to go in and delete the 6 rows between each row I care about. So my solution was to create another sheet and specify a reference to each cell I want.
=sheet1!a1
=sheet1!a8
=sheet1!a15
But I don't want to type in each of these formulas ... `100 times.I thought if I selected the three and dragged the box around, it would understand what I was trying to do, but no luck.
Any ideas on how to do this elegantly/efficiently?
In A1 of your new sheet, put this:
=OFFSET(Sheet1!$A$1,(ROW()-1)*7,0)
... and copy down. If you start somewhere other than row 1, change ROW() to ROW(A1) or some other cell on row 1, then copy down again.
If you want to copy the nth line but multiple columns, use the formula:
=OFFSET(Sheet1!A$1,(ROW()-1)*7,0)
This can be copied right too.
In my opinion the answers given to this question are too specific. Here's an attempt at a more general answer with two different approaches and a complete example.
The OFFSET approach
OFFSET takes 3 mandatory arguments. The first is a given cell that we want to offset from. The next two are the number of rows and columns we want to offset (downwards and rightwards). OFFNET returns the content of the cell this results in. For instance, OFFSET(A1, 1, 2) returns the contents of cell C2 because A1 is cell (1,1) and if we add (1,2) to that we get (2,3) which corresponds to cell C2.
To get this to return every nth row from another column, we can make use of the ROW function. When this function is given no argument, it returns the row number of the current cell. We can thus combine OFFSET and ROW to make a function that returns every nth cell by adding a multiplier to the value returned by ROW. For instance OFFSET(A$1,ROW()*3,0). Note the use of $1 in the target cell. If this is not used, the offsetting will offset from different cells, thus in effect adding 1 to the multiplier.
The ADDRESS + INDIRECT approach
ADDRESS takes two integer inputs and returns the address/name of the cell as a string. For instance, ADDRESS(1,1) return "$A$1". INDIRECT takes the address of a cell and returns the contents. For instance, INDIRECT("A1") returns the contents of cell A1 (it also accepts input with $'s in it). If we use ROW inside ADDRESS with a multiplier, we can get the address of every nth cell. For instance, ADDRESS(ROW(), 1) in row 1 will return "$A$1", in row 2 will return "$A$2" and so on. So, if we put this inside INDIRECT, we can get the content of every nth cells. For instance, INDIRECT(ADDRESS(1*ROW()*3,1)) returns the contents of every 3rd cell in the first column when dragged downwards.
Example
Consider the following screenshot of a spreadsheet. The headers (first row) contains the call used in the rows below.
Column A contains our example data. In this case, it's just the positive integers (the counting continues outside the shown area). These are the values that we want to get every 3rd of, that is, we want to get 1, 4, 7, 10, and so on.
Column B contains an incorrect attempt at using the OFFSET approach but where we forgot to use $. As can be seen, while we multiply by 3, we actually get every 4th row.
Column C contains an incorrect attempt at using the OFFSET approach where we remembered to use $, but forgot to subtract. So while we do get every 3rd value, we skipped some values (1 and 4).
Column D contains a correct function using the OFFSET approach.
Column E contains an incorrect attempt at using the ADDRESS + INDRECT approach, but where we forgot to subtract. Thus we skipped some rows initially. The same problem as with column C.
Column F contains a correct function using the ADDRESS + INDRECT approach.
If I were confronted with extracting every 7th row I would “insert” a column before Column “A” . I would then (assuming that there is a header row in row 1) type in the numbers 1,2,3,4,5,6,7 in rows 2,3,4,5,6,7,8, I would highlight the 1,2,3,4,5,6,7 and paste that block to the end of the sheet (700 rows worth). The result will be 1,23,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7……. Now do a data sort ascending on column “A”. After the sort all of the 1’s will be the first in the series, all of the 7’s will be the seventh item.
insert a new column and put a series in 1,2,3,4, etc. Then create another new column and use the command =if(int(a1/7)=(a1/7),1,0) you should get a 1 in every 7th row, filter the column on the 1
Highlight the 7th line. Paintbrush the format for the first 7 lines a few times. Then do a bigger chunk of paintbrush copying the format until you are done. Every 7th line should be highlighted. Filter by color and then copy and paste (paste the values) from the highlighted cells into a new sheet.
Create a macro and use the following code to grab the data and put it in a new sheet (Sheet2):
Dim strValue As String
Dim strCellNum As String
Dim x As String
x = 1
For i = 1 To 700 Step 7
strCellNum = "A" & i
strValue = Worksheets("Sheet1").Range(strCellNum).Value
Debug.Print strValue
Worksheets("Sheet2").Range("A" & x).Value = strValue
x = x + 1
Next
Let me know if this helps!
JFV
If your original data is in column form with multiple columns and the first entry of your original data in C42, and you want your new (down-sampled) data to be in column form as well, but only every seventh row, then you will also need to subtract out the row number of the first entry, like so:
=OFFSET(C$42,(ROW(C42)-ROW(C$42))*7,0)
Add new column and fill it with ascending numbers. Then filter by ([column] mod 7 = 0) or something like that (don't have Excel in front of me to actually try this);
If you can't filter by formula, add one more column and use the formula =MOD([column; 7]) in it then filter zeros and you'll get all seventh rows.