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.
Related
Here's a sample of my matrix:
A B C D E
1 0 0 1 1
0 0 0 0 0
0 0 1 1 0
0 2 1
You can think of each row as a respondent and each column as an item on a questionnaire.
My goal is to take an average of the sum of each row (i.e. total score for each respondent) without creating a new column AND accounting for the fact that some or all of the entries in a given row are empty (e.g., some respondents
missed some items [see row 5] or didn't complete the questionnaire entirely [see row 3]).
The desired solution for this matrix = 1.67, whereby
[1+0+0+1+1 = 3] + [0+0+0+0+0 = 0] + [0+0+1+1+0 = 2]/3 == 5/3 = 1.67
As you can see, we have averaged over three values despite there being five rows because one has missing data.
I am already able to take an average of the sum of rows which are only summed for non-missing entries, e.g.,:
=AVERAGE(IF(AND(A1<>"",B1<>"",C1<>"",D1<>"",E1<>""),SUM(A1:E1)),IF(AND(A2<>"",B2<>"",C2<>"",D2<>"",E2<>""),SUM(A2:E2)),IF(AND(A3<>"",B3<>"",C3<>"",D3<>"",E3<>""),SUM(A3:E3)),IF(AND(A4<>"",B4<>"",C4<>"",D4<>"",E4<>""),SUM(A4:E4)),IF(AND(A5<>"",B5<>"",C5<>"",D5<>"",E5<>""),SUM(A5:E5)))
However, this results in a value of 1 because it treats any row with some or all values values as = 0.
It does the following:
[1+0+0+1+1 = 3] + [0+0+0+0+0 = 0] + [0+0+0+0+0 = 0] + [0+0+1+1+0 = 2] + [0+0+0+0+0 = 0]/4 == 5/5 = 1
Does anyone have any ideas about how to adapt the current code to average over non-missing values or an alternative way of achieving the desired result?
You can do this more concisely with an array formula, but the short answer to fix up your existing formula is, if you have a blank cell in your sheet somewhere (say it's F1) AVERAGE will ignore blank cells so change your formula to
=AVERAGE(IF(AND(A1<>"",B1<>"",C1<>"",D1<>"",E1<>""),SUM(A1:E1),F1),IF(AND(A2<>"",B2<>"",C2<>"",D2<>"",E2<>""),SUM(A2:E2),F1),IF(AND(A3<>"",B3<>"",C3<>"",D3<>"",E3<>""),SUM(A3:E3),F1),IF(AND(A4<>"",B4<>"",C4<>"",D4<>"",E4<>""),SUM(A4:E4),F1),IF(AND(A5<>"",B5<>"",C5<>"",D5<>"",E5<>""),SUM(A5:E5),F1))
This would be one array formula version of your formula - it uses OFFSET to pull out each row of the matrix then SUBTOTAL to see if every cell in that row has a number in it. Then it uses SUBTOTAL again to work out the sum of each row and AVERAGE to get the average of rows.
=AVERAGE(IF(SUBTOTAL(2,OFFSET(A1,ROW(A1:A5)-ROW(A1),0,1,COLUMNS(A1:E1)))=COLUMNS(A1:E1),SUBTOTAL(9,OFFSET(A1,ROW(A1:A5)-ROW(A1),0,1,COLUMNS(A1:E1))),""))
Has to be entered as an array formula using CtrlShiftEnter
Note 1 - some people don't like using OFFSET because it is volatile - you can use matrix multiplication instead but it's arguably less easy to understand.
Note 2 - I used "" instead of referring to an empty cell. Interesting that the non-array formula needed an actual blank cell but the array formula needed an empty string.
You can omit the empty string
=AVERAGE(IF(SUBTOTAL(2,OFFSET(A1,ROW(A1:A5)-ROW(A1),0,1,COLUMNS(A1:E1)))=COLUMNS(A1:E1),SUBTOTAL(9,OFFSET(A1,ROW(A1:A5)-ROW(A1),0,1,COLUMNS(A1:E1)))))
Basically, what you're describing here for your desired result is the =AVERAGEA() function
The Microsoft Excel AVERAGEA function returns the average (arithmetic
mean) of the numbers provided. The AVERAGEA function is different from
the AVERAGE function in that it treats TRUE as a value of 1 and FALSE
as a value of 0.
With that in mind, the formula should look like this.
=SUM(AVERAGEA(A1:A4),AVERAGEA(B1:B4),AVERAGE(C1:C4),AVERAGEA(D1:D4),AVERAGEA(E1:E4))
Produces the expected result:
Note, if you want to ROUND() the result to two digits, add the following formula to it:
=ROUND(SUM(AVERAGEA(A1:A4),AVERAGEA(B1:B4),AVERAGE(C1:C4),AVERAGEA(D1:D4),AVERAGEA(E1:E4)), 2)
I have a table like this, where I want to insert formulas in column B to arrive at the indicated values.
The logic is this - I want to count every alternate cell in that particular row, starting from column C till column AA, and get the number of cells that contain a date value greater than or equal to Target date.
Cols/Rows A B C D E F G
1 Target Date X X Date Y Y Date Z Z Date
2 13-12-2015 2 13-12-2015 13-01-2016
3 24-11-2015 1 25-11-2015 20-10-2015
4 23-01-2016 0
5 30-01-2016 0 06-06-2016 14-04-2015
To begin with, before I put the condition on the date, I first tried to get the number of alternate columns in this range by using the array formula =IF(MOD(COLUMN($C4:$AA4),2)=0,COLUMNS($C4:$AA4))
But this returns FALSE for some reason. Only if this returns a numeric value, I can proceed with adding a condition for dates.
How do I modify the formula? Any help is appreciated!
You want to use SUMPRODUCT():
=SUMPRODUCT((MOD(COLUMN($C4:$AA4),2)=0)*($C4:$AA4>=DATEVALUE"13/1/2015")*($C4:$AA4<=DATEVALUE"13/1/2016"))
This will return a count of every other column that has a date between 13/1/2015 and 13/1/2016
I don't have access to Excel at the moment to check, but I suspect the issue is that you're calling COLUMN with arguments. I think if you remove the range, and do =IF(MOD(COLUMN(), 2)=0, COLUMNS($C4:$AA4) the if will at least get true on even columns. COLUMN returns the numeric index of the current column, so you might need to fiddle with things to get this working.
The brute-force approach, write as many of these as you need,
=1*(C5>A5) + 1*(E5>A5) + 1*(G5>A5) + 1*(I5>A5)
and so on.
Have a seemingly simple question that I just don’t know how to fix. These are the excel columns:
PointID number result
**1 1
2 1 #end resultA (3)**
3 0
4 0
**5 1 #start
6 1
7 1 #end resultB (2)**
8 0
9 1 #start
10 1 #end
11 0
# is the comment sign.
bewtween ** ** is highlight
The key is to find a value in ‘number’ column with current value being 1 and next one being 0 (call it end point), and then look for next value in same column with current value being 1 and previous one being 0 (call start point). Between each start point and end point is a short line.
Then in ‘result’ column (can go to any row), calculate a result of function based on existing values (just name it functionA ) and when the calculated result resultA reaches a threshold value 3, I will highlight all the values involved in these two lines and remove them later on. As seen in the data. resultA is calculated using the value of points (ID2 and ID5) in ‘number ’ column, and it reaches the threshold , so all points in these two lines are highlighted in bold font. resultB doesn’t reach the threshold, so points in line3 are not highlighted. I cannot think of how to write function that fill in the column ‘result’ . Any idea?
Now the question has simplified: the H column is my result ! And what i need to do is just find the H cell that is small than 100 (the cell highlighted is 48 so it meets criteria), and highlight all data that have consecutive 1 values in K column as 1,1,1,... and closest to the current row. as being highlighted in the image. This selection is the final step.
to clarify:
H column in worksheet corresponds to result column in the first excel dataset. Now just look
at the worksheet.
this row 13 (with H value of 48.9) is where a end point is in--from K column you can see a series of 1.. and that tells you where is end and starting point of each list made of 1.
so 48.09 on H13 is calculated using the G13 and G15, which are the G column value of end point of current list and starting point of next list. so K13=function(G13,G15)
what needs to do is, if this value in H < 100, then find all the points in these two lists and select (first list goes up from current end point until 1 disappears in the K column, and second list goes down from next starting point until 1 disappears in K column).
as in sheet, since 48<100, I simply select all the points in previous list of 1 (that is from row 6-13), and points in next list (that is from row 14 to 22) and highlight them in red.
Hope the problem is understanble now, it is kinda hard to describe but really should be simple problem. Dont know if excel can handle this.
I have to admit that it took me a while to even comprehend how you were arriving at the numbers within your results column but I think I have a formula based solution.
I had to split off the #start and #end labels in order to treat the numbers in column B as actual numbers. The formula in D2 is,
=IF(AND($B2=1,$B3=0),TEXT(IFERROR(MATCH(1, $B3:$B$99,0),COUNT($B2:$B$98)),"\r\e\s\u\l\t\"&CHAR(64+SUMPRODUCT(SIGN(LEN($D$1:$D1))))&"\(0\)\*\*"),"")
Fill down as necessary.
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 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.