I have 10,000 rows of data in Excel and the first column is the time of a data recording (hh:mm:ss). I want to filter/extract (in the front end) only the rows where the readings are at least six consecutive seconds - these are signal strength recordings and I only consider readings of at least six consecutive seconds to be valid for my purposes. I would only want to extract the rows in green in the attached image. Separate ask - what if I wanted segments of only six to eight seconds? Thanks!
With O365 (assuming no excel version constraints per tags used in the question) you can try the following in cell D1:
=LET(rng, A1:B32, ref, SCAN("", INDEX(rng,,1), LAMBDA(ac,a, IF(ac="", 1,
IF((a - TIME(0,0,1)) = OFFSET(a,-1,0), 0, 1)))), size, ROWS(ref),
seq, SEQUENCE(size), start, FILTER(seq, ref=1),
end, VSTACK(DROP(start-1, 1), size), DROP(REDUCE("", start, LAMBDA(ac,s,
LET(e, XLOOKUP(s, start, end), f, FILTER(rng, (seq >= s) * (seq <= e)),
IF(ROWS(f) > 5, VSTACK(ac, f), ac)))),1))
Note: The same result can be achieved to avoid using the volatile function OFFSET, not using SCAN, and instead calculating the ref value using comparison as follow, where A represents the first column of rng (we keep the same logic as previous formula, but it can be simplified changing the logic to check 0 instead 1 to remove 1-N()):
=LET(rng,A1:B32,A,INDEX(rng,,1), ref,VSTACK(1,1-N(DROP(A,1)
=(DROP(A,-1)+TIME(0,0,1)))), size, ROWS(ref), seq, SEQUENCE(size),
start, FILTER(seq, ref=1), end, VSTACK(DROP(start-1, 1), size),
DROP(REDUCE("", start, LAMBDA(ac,s, LET(e, XLOOKUP(s, start, end),
f, FILTER(rng, (seq >= s) * (seq <= e)),
IF(ROWS(f) > 5, VSTACK(ac, f), ac)))),1))
Here is the output:
Then adjust the input range (rng) to your real case.
Explanation
Using LET for easy reading and composition. The name ref identifies with 1, the start of each group of values with consecutive seconds of the first column from rng. It has the same number of rows as the input data.
We use DROP/REDUCE/VSTACK pattern to generate iteratively the output with the data that satisfies the conditions the group has more than 5 consecutive elements. Check the following answer to this question: how to transform a table in Excel from vertical to horizontal but with different length
Via REDUCE we iterate over all start group positions (start). For each start group position (s), finds the corresponding end group position (e) via XLOOKUP. Filter the range (rng) for rows (seq) between start (s) and end (e) rows via FILTER. Append the filter result (f) only if the number of rows is bigger than 5 via the VSTACK function.
Conditional Format
The formula provided is too large to be used for a conditional format formula (255 maximum number of characters). A possible workaround could be to use a helper column (you can hide it). It returns TRUE if the row belongs to a valid group, otherwise returns FALSE. If no valid group were found it returns FALSE too (we use this trick: NOT(SEQUENCE(size,,1,0)=1) to generate a constant column with FALSE values). Then you can highlight the column A values that correspond with TRUE values in the helper column.
=LET(A, TOCOL(A:A,1), ref, VSTACK(1,N(DROP(A,1) = (DROP(A,-1)+TIME(0,0,1)))),
size, ROWS(ref), seq, SEQUENCE(size), start, FILTER(seq, ref=0),
end, VSTACK(DROP(start-1, 1), size), gr, FILTER(HSTACK(start, end),
(end-start +1) > 5, -1), sgr, INDEX(gr,,1), egr, INDEX(gr,,2),
IF(#gr=-1, NOT(SEQUENCE(size,,1,0)=1), MAP(seq, LAMBDA(x,
LET(overlaps, SUM((sgr <= x) * (egr >= x)), IF(overlaps = 1, TRUE, FALSE))))))
as input, we use the entire column and filter by nonblank values via the TOCOL function (named A) using the second input argument of this function. In case of more than one overlapping interval, it returns FALSE too, just for testing purposes, it indicates some error calculating start and end names because per design it should never happen.
Tip: The previous formula can be used for the original purpose, using its output as a condition for a FILTER function to select only the input range where the value is TRUE. It is a matter of taste which route you want to go. For example, as follow:
=LET(rng,A1:B32,A,INDEX(rng,,1), ref, VSTACK(1,N(DROP(A,1)
= (DROP(A,-1)+TIME(0,0,1)))), size, ROWS(ref), seq, SEQUENCE(size),
start, FILTER(seq, ref=0), end, VSTACK(DROP(start-1, 1), size),
gr, FILTER(HSTACK(start, end), (end-start +1) > 5, -1),
sgr, INDEX(gr,,1), egr, INDEX(gr,,2),
incl, IF(#gr=-1, NOT(SEQUENCE(size,,1,0)=1), MAP(seq, LAMBDA(x,
LET(overlaps, SUM((sgr <= x) * (egr >= x)), IF(overlaps = 1, TRUE, FALSE))))),
FILTER(rng, incl=TRUE, "No Group found"))
I am trying to compare decimal values in Excel VBA and delete rows that
match the criteria. This was my original code and it skipped over many rows.
For Each i In WSData.Range("A7", WSData.Range("A7").End(xlDown)).Cells
If i.Offset(0, 3).Value >= 98 Then
i.EntireRow.Delete
End If
Next
And the values on the spreadsheet are decimal values just with the % sign.
I tried "> 97.99" because Excel has some issues with floating point comparison but it still doesn't accurately compare.
Here is what it shows after using Selection.Value.
Percentages are decimal depicted with integers. For example 100.00% is stored as 1 and 98.01% is stored as .9801.
Therefor you need to adjust the threshold:
For Each i In WSData.Range("A7", WSData.Range("A7").End(xlDown)).Cells
If i.Offset(0, 3).Value >= .98 Then
i.EntireRow.Delete
End If
Next
The second problem is that when deleting rows it is best to iterate backwards. Otherwise it might miss some rows, because as each row is deleted it is moved up and then the next iteration skips the next row.
Change i from a range to a long and use this:
For i = WSData.Range("A7").End(xlDown).row to 7 Step -1
If WSData.Cells(i,3).Value >= .98 Then
Row(i).Delete
End If
Next
Edit: Because it appears there is a custom mask on the number format that is forcing numbers to look like percentages try this:
For i = WSData.Range("A7").End(xlDown).row to 7 Step -1
If WSData.Cells(i,3).Value >= 98 Then
Row(i).Delete
End If
Next
If this works then your main problem was that you were looking at column D. The offset is additive. So when you used .offset(0,3) it was moving three columns from column A. 1 + 3 is 4.
I have a vector with 8760 values (the values could be like 1.2, 3.0, 7.6, 2.5, 8.4, 6.3 etc) and I want to find these 24 consecutive values that give the maximum sum. I would also like to know the specific position of the first value are this sum.
The vector is an Excel file that is read in Matlab as Pload=xlsread('demand.xlsx');
if you insist on Matlab, you can use the conv (convolution) function to do the sum for you, then find the position of the max:
Assuming your data are in a vector named A, and I call nc the number of consecutive data for the summation:
nc = 24 ; %// number of consecutive values to sum
kn = ones(nc,1) ; %// define a kernel for the convolution
C = conv(A,kn) ; %// calculate a "moving sum"
[~,idx] = max(C) ; %// find the max of the convolution result
idx = idx - nc + 1 ; %// The starting index of the FIRST maximum sum is here
If you have several identical maximum, this will only return the index of the first one.
Put the values in column A. In B24 enter:
=SUM(A1:A24)
Copy this downwards.
Finally use:
=MAX(B24:B7860)
To get the position, use =MATCH()
In numerology you add, for example, the numbers for your year of birth; let's assume 1945. When these digits are added together they total 19 (1+9+4+5). They must then be added again to come up with a single number, in this instance 10 then 1. Could you please provide a formula for this?
Adding this to your VBA file (hit Alt-F11) will give you a function called "totalString".
The sum can then be calculated using =totalString(A1), where A1 is the cell containing your number, eg 1945, or directly using (you guessed it...) =totalString(1945)
Function totalString(ByVal numberString As Integer) As Integer
Dim length As Integer
length = LEN(numberString)
Dim sum As Integer
sum = 0
For i=1 To length
sum = sum + mid(numberString, i, 1)
Next i
EDIT: Instead of this next section, you could use the 3rd code block (at the bottom of the post) to specify only certain values to add.
Dim l2 As Integer
l2 = LEN(sum)
If l2 <> 1 Then
totalString = totalString(sum)
Else
totalString = sum
Exit Function
End If
End Function
3rd block (alternative ending...)
If sum > 11 Then
totalString = totalString(sum)
Else
totalString = sum
Exit Function
End If
End Function
The function calculates the length of the number you give it, and then gets adds the value of each number. It checks to see if the length of the sum is 1, and if not, recursively calls the function.